【原創】大叔問題定位分享(14)Kylin頻繁OOM問題

公司一個kylin集羣,每到週二下午就會逐個節點OOM退出,很是有規律,kylin集羣5個節點,每一個節點分配的內存已經不斷增長到70多G,可是問題依舊;apache


經排查發現,每週二下午kylin集羣的請求量確實會多一些,有多是kylin的bug,也多是其餘緣由,當節點kylin進程內存佔用上升時,打印線程堆棧發現,有不少線程都被卡住,synchronized,各類Manager,好比CubeManager、DictionaryManager、MetadataManager,以MetadataManager爲例查看kylin代碼發現,這些Manager的套路差很少,都有clearCache、getInstance(synchronized),而後getInstance中會調用Constructor,Constructor中會加載一堆東西,這個加載過程比較慢,因此getInstance會長時間synchronized:tomcat


org.apache.kylin.metadata.MetadataManager服務器

    public static void clearCache() {
        CACHE.clear();
    }
    
    public static MetadataManager getInstance(KylinConfig config) {
        MetadataManager r = CACHE.get(config);
        if (r != null) {
            return r;
        }

        synchronized (MetadataManager.class) {
            r = CACHE.get(config);
            if (r != null) {
                return r;
            }
            try {
                r = new MetadataManager(config);
                CACHE.put(config, r);
                if (CACHE.size() > 1) {
                    logger.warn("More than one singleton exist");
                }

                return r;
            } catch (IOException e) {
                throw new IllegalStateException("Failed to init MetadataManager from " + config, e);
            }
        }
    }

    private MetadataManager(KylinConfig config) throws IOException {
        init(config);
    }

    private void init(KylinConfig config) throws IOException {
        this.config = config;
        this.srcTableMap = new CaseInsensitiveStringCache<>(config, "table");
        this.srcTableExdMap = new CaseInsensitiveStringCache<>(config, "table_ext");
        this.dataModelDescMap = new CaseInsensitiveStringCache<>(config, "data_model");
        this.extFilterMap = new CaseInsensitiveStringCache<>(config, "external_filter");

        reloadAllSourceTable();
        reloadAllTableExt();
        reloadAllDataModel();
        reloadAllExternalFilter();

        // touch lower level metadata before registering my listener
        Broadcaster.getInstance(config).registerListener(new SrcTableSyncListener(), "table");
        Broadcaster.getInstance(config).registerListener(new SrcTableExtSyncListener(), "table_ext");
        Broadcaster.getInstance(config).registerListener(new DataModelSyncListener(), "data_model");
        Broadcaster.getInstance(config).registerListener(new ExtFilterSyncListener(), "external_filter");
    }

 

查看了kylin各個版本的代碼,發現都是這個套路,看來kylin不認爲這是一個問題,這確實會致使一些潛在的問題,好比高負載時,突然要刷新,這時就會有大量的請求被synchronized,這個會致使OOM嗎?多線程


進一步檢查線程堆棧發現,當時tomcat的線程池幾乎被佔滿,這個也很容易理解,以前的請求被synchronized,還不斷有新的請求進來,而後線程池就滿了,突然想到,一旦synchronized結束,全部的請求都開始同時處理,並且其中一些請求可能會佔用比較多的內存,這樣內存可能瞬間就扛不住了,這是一個雪崩效應,上面的場景其實和壓測的場景差很少,即服務器滿負荷運轉,線程池全部的線程都在處理請求,若是tomcat配置的線程池數量太大了,服務器就撐不住了,OOM就是由於這個,若是不改這個配置,內存配置的再大也沒用,仍是會OOM,把tomcat線程池配置小一些便可;this

 

另外還有一種方法,就是在Load Balancer上加控制,一旦響應很慢,就標記unhealthy,把請求分給其餘節點,這樣就不會在synchronized的節點上堆積大量請求,也能夠避免問題;spa

相關文章
相關標籤/搜索