使用ElasticSearch的HLRC(High Level Rest Client)时,报出I/O Reactor STOPPED
问题现象
使用ElasticSearch的HLRC(High Level Rest Client)时,偶现报出I/O Reactor STOPPED。排查ElasticSearch日志,未有报错。
I/O Reactor STOPPED是什么问题?
首先根据调用栈可以定位到报错来自CloseableHttpAsyncClientBase中的90行,如下图所示:
ensureRunning()方法是在每次请求执行开始的时候调用,用来确认client状态是否为ACTIVE,如果不是ACTIVE,则会报错。然后观察CloseableHttpAsyncClientBase中的status何时会变成STOPPED,发现有且仅有两处会set为STOPPED,如下图所示:
![点击放大](https://support.huaweicloud.com/trouble-css/zh-cn_image_0000001387624621.png)
![点击放大](https://support.huaweicloud.com/trouble-css/zh-cn_image_0000001387624685.png)
- 由于客户不会手动关闭客户端之后再调用接口,所以有且仅有第一处会导致status切换为STOPPED状态。
- 对于第一处STOPPED,reactorThread线程是用来在io events发生时调度io events,当内部抛出异常时,最终会将status改为STOPPED状态。然后在bulkAsync请求的回调中抛出异常验证status的状态切换,如下图所示:
- 当请求失败,status会切换为STOPPED且I/O Reactor将关闭,并使HLRC实例卡住。后续使用该HLRC实例调用任何请求都会失败。此处手动抛出异常是为了复现问题,生产环境中很难分辨是什么原因导致I/O Reactor关闭。
原因分析
导致出现I/O Reactor STOPPED的原因,大致可以分为以下3类:
- 回调中抛出异常导致。
- 客户端并发太高导致。
在日志中发现异常后,查看ElasticSearch集群监控指标,如CPU使用率、网络连接数等。
当用户集群配置为5台16U128G的i3.4xlarge.8节点时,且每天上午5点左右会做大量bulk操作,写入大概100G-200G的数据,根据集群监控指标的CPU使用率、网络流入流出速率来看对ElasticSearch节点造成不了压力,网络连接数较高,其它节点情况也相同。但是,有的节点网络连接数高达近9000,5个节点瞬间有将近5万连接数,用户的代码大致是用同一个Rest Client多个线程并发且调用HLRC的bulkAsync接口。客户端一个节点,单是ES的连接就消耗了4-5万个连接数,这种情况很容易造成客户端节点句柄数耗尽,或者连接数耗尽。
- ElasticSearch的Rest client导致。建议ElasticSearch完善Rest client,添加exception handler。
Apache(HLRC和LLRC都使用了Apache HTTPComponents Async Client)手册中提到,在与会话通道交互过程中有些I/O异常是可以预料的,这些异常可能会导致单个session终止,但不会影响I/O Reactor和其他session。但某些情况下,当I/O Reactor本身遇到内部问题是,例如底层NIO的一些类中的I/O异常或者没被handle的一些Runtime Exception。这些异常是致命的,会使I/O Reactor关闭。Apache官方建议重写IOReactorExceptionHandler接口。
图3 重写IOReactorExceptionHandler接口但是尝试了重写ExceptionHandler并放到HLRC的配置中,并通过在回调中抛出异常来模拟异常场景,最后发现这种回调的异常并不会被IOReactorExceptionHandler捕获,运行脚本后也没有异常抛出,所以此处IOReactorExceptionHandler的实现并不好验证。通过ElasticSearch社区issue中其他开发者的验证,添加了IOReactorExceptionHandler后跑很久也不会有问题。所以建议添加IOReactorExceptionHandler,但是注意不要忽略所有异常。
Elasticsearch Rest Client需要有一个exception handler,而不是让用户通过设置IOReactorExceptionHandler来处理异常,且这种方式也不会解决所有的异常。
处理建议
- 客户端连接池大小建议根据业务实际情况调整。
- 客户端并发数建议根据业务实际情况调整,或者配置多个节点、分摊压力。
- 建议配置IOReactorExceptionHandler,可以用来处理一些异常。
- 调用HLRC时catch exception,并判断cause是否为I/O Reactor STOPPED,然后通过重启客户端恢复连接。