从应用频繁502提及ES的超时问题——es setTimeout无效

缘起

应用用的ES集群版本是1.7,公司内部让升级版本,说是这个集群版本太老了,因为种种缘由没有升级,就这样应用拖到了618,结果在这期间应用频繁的发生502和rpc线程池被打满的状况,给咱们本应该顺畅的618带来了一片乌云。java

获得此报警以后,开始dump机器线程,发现有大量的es线程在阻塞,而且有不少的http线程也在阻塞,这也就不难排查发生502和RPC线程池被打满的缘由,因为es操做迟迟未返回,致使业务线程池被打满,http链接一直等待结果返回,链接不释放,致使了tomcat接收业务请求也被打满,进而致使应用访问频繁出现502。node

上图就是jstack下来线程,能够看到有大量的http线程被阻塞,而后查看详情,git

详情中有大量的线程被阻塞在了ES的调用上,这样问题就很明显了,按理来讲问题定位到,那解决就应该很好解决了。但是哪想到这才是痛苦的开始。github

缘因

联合es中间件组排查es集群,结论没有查到有任何慢日志,说是es集群正常。联系网络组,网络组排查以后网络正常,联系运维组排查机器正常。把全部机器实例都删除掉,从新换了一批机器以后问题依旧。不过经过调用监控,日志,线程和堆栈种种迹象均指向了是es的问题。而且es集群是低版本集群,公司已经声明再也不维护,以前没有报这过这个问题,如今频繁发生,猜测应该是es资源被缩减,无维护有部分缘由。为了临时解决这个问题,不能让线程无限阻塞,应尽快释放线程,即使没有查到数据,也能快速返回。api

FilteredQueryBuilder fqb = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), boolFilterBuilder);
            SearchResponse searchResponse = client.prepareSearch(indexName)
                    .setTimeout(TimeValue.timeValueMillis(500))
                    .setTypes(documentType)
                    .setSearchType(SearchType.QUERY_THEN_FETCH)
                    .setQuery(fqb)
                    .addSort("created", SortOrder.DESC)
                    .setFrom(0).setSize(30)
                    .execute()
                    .actionGet();

关键在于setTimeout(TimeValue.timeValueMillis(500)) 这一行,查询时间设置为500毫秒,而后愉快的上线,本觉得问题会解决,结果比较打脸,问题依旧!tomcat

缘灭

问题迟迟得不到解决,天天应用报警502,线程满,碰到这种状况只能重启应用,烦不胜烦,而且很怀疑为何明明设置了超时时间就是不生效。最后只能寻根!因为以前对es了解的并非那么透彻,认为setTimeout就是查询以后若是多长时间没有返回,则断开查询链接直接返回。实际上并非这样的,他的意思是在查询es的时候,会查询到多个分片上的数据,当到了设定的时间若是尚未查询完,则把已经查询到的数据返回。即使是这样,这个timeout也是常常失效,在es官方的issue中有具体说到:网络

Sadly, it is a best effort timeout, its not being checked on all places. Specifically, if you send a query that ends up being rewritten into many terms (fuzzy, or wildcard), that part (the rewrite part) does not check for a timeout.运维

传送门:Timeout on search not respected异步

那到底怎么配置才能知足若是在设定时间内查询不到数据就超时,扔出超时异常的这种需求呢。通过查看能够在api查询的最后一步也就说 actionGet()或者get()中设置timeout超时时间 actionGet(timeout) T actionGet(long var1, TimeUnit var3) throws ElasticsearchException; ,这样设置以后若是在设定的时间没有查询到数据,就会抛出timeout的异常,实际上这个超时并非链接超时,而是处理超时,它的超时逻辑是java异步future的超时。不过这也已经知足了咱们的需求。在设定时间内没有处理完毕,会抛出超时的异常。elasticsearch

FilteredQueryBuilder fqb = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), boolFilterBuilder);
            SearchResponse searchResponse = client.prepareSearch(indexName)
                    .setTimeout(TimeValue.timeValueMillis(500))
                    .setTypes(documentType)
                    .setSearchType(SearchType.QUERY_THEN_FETCH)
                    .setQuery(fqb)
                    .addSort("created", SortOrder.DESC)
                    .setFrom(0).setSize(30)
                    .execute()
                    .actionGet(1000);

改造以后,系统再也不抛502异常,线程数也趋于稳定,稳定在了400左右,以前经常是一千多。

更近一步

因为对es超时机制理解的不透特,因此又查询了es的其余超时相关设定。

  • client链接集群节点超时(client.transport.ping_timeout
Settings settings = Settings.builder().put("client.transport.sniff", true).build();
TransportClient client = new PreBuiltTransportClient(settings);

client.transport.ping_timeout ,The time to wait for a ping response from a node. Defaults to 5s. 默认5s,client ping命令响应的时间,若是无返回,则认为此节点不可用。若是客户端和集群间网络延迟较大或者链接不稳定,可能须要调大这个值。

  • scroll中的超时
SearchResponse scrollResp = client.prepareSearch(test)
        .addSort(FieldSortBuilder.DOC_FIELD_NAME, SortOrder.ASC)
        .setScroll(new TimeValue(60000))
        .setQuery(qb)
        .setSize(100).get();

scroll里面的时间,这个将启用超时的scroll滚动,通过测试,这个参数应该又是一个薛定谔的参数,没什么做用,仍是少依赖它作一些事情吧

相关文章
相关标签/搜索