异常信息:java.io.IOException: listener timeout after waiting for [60000] ms
异常堆栈:
java.io.IOException: listener timeout after waiting for [60000] ms at org.elasticsearch.client.RestClient$SyncResponseListener.get(RestClient.java:660) at org.elasticsearch.client.RestClient.performRequest(RestClient.java:219) at org.elasticsearch.client.RestClient.performRequest(RestClient.java:191) at com.suning.maoning.core.es.EsClientHolder.performRequest(EsClientHolder.java:113) at com.suning.maoning.core.es.EsClientHolder.performRequest(EsClientHolder.java:127) at com.suning.maoning.core.es.EsClient.searchAfter(EsClient.java:343) at com.suning.maoning.mnbi.service.impl.commissionexport.XXXXXXXXXXXService.queryData(BaseCommissionEsExcelExportService.java:76) at com.suning.maoning.excel.common.export.service.impl.EsScrollRunnable.async(EsScrollRunnable.java:39) at com.suning.maoning.excel.common.export.service.impl.EsScrollRunnable$$FastClassBySpringCGLIB$$1736c315.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:721) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at com.suning.maoning.core.cache.interceptor.RedisInterceptor.invoke(RedisInterceptor.java:119) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:115) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745)
根据异常堆栈指示的信息,找到对应发生异常的业务类,定位到相关具体语句,是一个ES查询调用的地方,调用方法为
esClient.searchAfter
继续跟踪对应方法
继续往下跟踪
最后再这里
到这里,分成了两个部分,
第一部分创建了一个SyncResponseListener,用于监听对es请求的返回
另一部分performRequestAsync则对es发起HttpRequest,进行查询。
需要注意的是SyncResponseListener创建的时候会同时创建一个CountDownLatch
private final CountDownLatch latch = new CountDownLatch(1);
而在performRequestAsync请求es的逻辑中,不论请求成功得到结果,还是失败或者发送异常等情况下,都会调用latch.countDown()
同时,最重要的,在上面的代码中,最后调用listener.get()会进行等待
这里也就是最终异常抛出的地方。
下面我们简单整理描述下这部分代码到底在干啥
首先这边分成了两个线程,一个线程为response结果监听线程,用于监听请求结果是否得到了,另一个则是去向es请求的线程,监听线程在这里会监听等待timeout的时长,如果没有得到结果,则直接抛出IOException。
那么到这里,我们已经明白了为什么会产生这样的异常了,简单来说就是去es请求,但是超时了。
对于处理办法,首先想到的是修改参数增加timeout超时时间,这样监听线程会等待更久,以不至于会超时。
不过这显然不够优雅,扒完了源码,我们重新回到业务中,我看到了es查询请求的分页参数设置的size = 10000,即每页查询10000条数据,根据开发这块业务功能的同学以及测试同学一起描述的,“测试的时候没啥问题啊!”,嗯,确实,测试的时候测试环境费劲巴拉最多也就造了1000多条数据,o(╥﹏╥)o,基本不可能暴露出来问题的。
所以,在这边,我们重新配置修改了分页大小参数,先对半砍改为5000试了下,执行,确认通过,没有问题。
这边还有个比较有意思的,关于es查询的深分页,这边使用的是es的search after方法,另外es还提供了一个scroll的深分页查询方法,回头可以展开再讨论下
发表评论