訂閱 + 定時任務重構後臺主機操做任務

問題描述

主機狀態一直顯示有問題,去向動態連接庫請求數據時,除了第一臺主機訪問成功外,之後的每一臺主機返回的結果都是9(HOST_NOT_FOUND)java

clipboard.png

研究了好久也沒研究明白,最後求助潘老師。安全

clipboard.png

潘老師指出是指針有問題,主機的指針是咱們構造出來的,雖然該指針指向對象的namecontext與動態連接庫服務那邊的都同樣,可是他那裏多是按地址處理的,不是同一個地址,就報主機找不到了。服務器

通過測試,確實是這樣。併發

不能直接構造指針,須要使用同步計算機數據時返回的指針進行操做。測試

clipboard.png

因此,獲取計算機狀態,操做計算機都須要從新同步一下計算機,將指針同步過來。this

假設用戶操做很頻繁的話,對動態連接庫的壓力就很大。指不定啥時候就炸了。spa

雖然我想到了這點,但也沒想到好的解決方案。線程

潘老師最終的設計方案很是好,採用定時任務,當有計算機的操做時,不實時進行操做,而是先存起來,每隔一段時間統一進行操做,從而減輕服務器壓力。debug

實現

設計

對主機進行關機或重啓時,不當即執行,而是將其添加到一個要執行的任務列表中,當定時任務觸發後,再統一執行。設計

clipboard.png

定時任務

定時任務很簡單,以前在計量中寫過一個天天晚上定時統計數據的定時任務,此次寫這個沒什麼難度,都不須要看文檔了。

註釋很詳盡,相信聰明的你徹底能夠理解。

這裏的存儲計算機狀態設置了一個主機名到計算機狀態映射的HashMap,這裏沒有用ConcurrentHashMap,由於只有定時任務一個線程進行put,其餘接口調用該Map只負責查詢,不會出現衝突。

@Async
@Scheduled(fixedRate = 10000)        // 每隔10s執行
public void hostHandle() {
    logger.info("--- 開始執行定時任務 ---");

    logger.debug("獲取關機重啓的訂閱者");
    Set<String> shutdownSubscribers = this.cloneStringSetAndClear(hostService.getShutdownSubscribers());
    Set<String> rebootSubscribers = this.cloneStringSetAndClear(hostService.getRebootSubscribers());

    logger.debug("獲取主機結構體指針Map");
    List<HostStruct.ByReference> byReferenceList = baseService.getHostStructReferenceList();
    Map<String, HostStruct.ByReference> byReferenceMap = baseService.getHostStructReferenceMap(byReferenceList);

    if (!shutdownSubscribers.isEmpty()) {
        logger.info("存在關機訂閱,執行關機操做");

        for (String name : shutdownSubscribers) {
            logger.debug("獲取結構體指針並關機");
            HostStruct.ByReference byReference = byReferenceMap.get(name);
            baseService.shutdown(byReference);
        }
    }

    if (!rebootSubscribers.isEmpty()) {
        logger.info("存在重啓訂閱,執行重啓操做");

        for (String name : rebootSubscribers) {
            logger.debug("獲取結構體指針並重啓");
            HostStruct.ByReference byReference = byReferenceMap.get(name);
            baseService.reboot(byReference);
        }
    }

    logger.debug("查詢主機列表");
    for (HostStruct.ByReference byReference : byReferenceList) {
        logger.debug("查詢主機指針,同時獲取主機信息");
        Integer status = baseService.getHostStatus(byReference);

        logger.debug("添加到Map中");
        hostService.getHostStatusMap().put(Native.toString(byReference.name), status);
    }

    logger.info("--- 定時任務執行完畢 ---");
}

/**
 * 克隆字符串集合並清空原集合
 * @param primarySet 原集合
 * @return 克隆的字符串集合
 */
private Set<String> cloneStringSetAndClear(Set<String> primarySet) {
    logger.debug("新建集合");
    Set<String> set = new HashSet<>(primarySet);

    logger.debug("清空原集合");
    primarySet.clear();

    logger.debug("返回");
    return set;
}

todo

目前是用Set存儲要操做的主機,可是通過查詢,HashSetLinkedHashSetTreeSet都是線程不安全的。

這裏就怕執行定時任務的時候,忽然來了一個關機的指令,兩個線程同時訪問Set,定時任務要克隆一個Set而後把這個清空,關機須要在Set中添加元素,兩個線程若是交替執行,結果就很難說了。

clipboard.png

concurrent包下找,也沒找到合適的。看了看,有第三方庫實現的線程安全的Set,之後引入進來。

clipboard.png

這裏不知道是否是我用的有問題,是併發場景下不推薦用Set嗎?爲何concurrent包下沒有相關的實現類?

總結

感嘆一句:C++仍是難啊!

C++老師對個人評價是字寫得還不錯,哈哈哈。佩服C++工程師!

相關文章
相關標籤/搜索