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