公司一個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