应用用的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的其余超时相关设定。
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命令响应的时间,若是无返回,则认为此节点不可用。若是客户端和集群间网络延迟较大或者链接不稳定,可能须要调大这个值。
SearchResponse scrollResp = client.prepareSearch(test) .addSort(FieldSortBuilder.DOC_FIELD_NAME, SortOrder.ASC) .setScroll(new TimeValue(60000)) .setQuery(qb) .setSize(100).get();
scroll里面的时间,这个将启用超时的scroll滚动,通过测试,这个参数应该又是一个薛定谔的参数,没什么做用,仍是少依赖它作一些事情吧