处理elasticsearch中的NODENOTAVAILABLEEXCEPTIONS异常

来源:互联网 发布:淘宝实战培训 编辑:程序博客网 时间:2024/06/08 18:20
Elasticsearch用极少的安装和配置提供了分布式查询。现在,关于它美好的事情就是,大部分时间,你不需要特别关注它怎样做以及做什么。你只需要给它一些参数--“我想三个节点”,“我想三个分片”,“我想每个分片都将会被复制以至于它至少要有两个节点”,以及ES也会计算出如何移动内容以至于你会得到你所请求的内容。如果一个节点变的不可达,ES将会尝试的保持一些事情继续运行,然而当这个节点出现的时候会重新加入,然后管理器将会被更新以至于每件事情都会再一次正常了。

这个问题是出现在集群没有按照你期望的去工作了的时候。。。


计算机说“没有节点可用”


我们只是已经得到一个客户的新的存活的网址,并且每件事情都是在稳定顺利的运行。每个人也都高兴的回家了。然后就来了一封邮件,“当我们去这个页面的时候,它就挂机了”。回复说道:“嗯嗯,我这是没问题的”。另外还有回复的,“是,我也碰到了相同的问题”。由于这个界面的性质,这个问题很快变得明显的,有些事情跟ES有关系。我们的日志会周期性的出现下面的错误:

org.elasticsearch.client.transport.NoNodeAvailableException: No node available
        at org.e.c.t.TransportClientNodesService$RetryListener.onFailure(...)
        at org.e.a.TransportActionNodeProxy$1.handleException(...)
        at org.e.t.TransportService$Adapter$3.run(...)
        ... 3 more

和这些中的一些一样:

2015-03-22 05:15:09.329 INFO org.elasticsearch.client.transport 
[elasticsearch[Dark Phoenix][generic][T#19]]: [Dark Phoenix] failed to get node 
info for[#transport#-1][tomcat01][inet[elastic01.xxxxxxxxxxx/10.9.2.221:9300]], 
disconnecting...
org.elasticsearch.transport.ReceiveTimeoutTransportException: 
[][inet[elastic01.xxxxxxxxxxx/10.9.2.221:9300]][cluster/nodes/info] 
request_id [26382] timed out after [5001ms]
        at org.e.t.TransportService$TimeoutHandler.run(...)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(...)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(...)
        at java.lang.Thread.run(Thread.java:744)

在我开始之前,让我画一个结构图:




没有什么事情太陌生的,除了我们的ES节点处于不同的数据中心之外,而不是我们的服务器。这个是问题的一部分。
两个数据中心意味着这个数据正在被路由,并且也可能被一个防火墙过滤了。防火墙喜欢跟踪连接以至于他们可以通过关于一个已经决定它可以接受的一个连接的数据包。因为所有的这些连接跟踪占有内存,所以不会再被使用的连接就会被丢弃。并且防火墙不会通知另一端它这样做了。因为这个防火墙已经超时了,通常设置为1-6小时。超时之后对于这个连接的任何数据包都应该可达吗,这个防火墙将会说“对不起,你说你属于一个连接X,但是我不知道连接X的任何事情”,然后默默的丢弃掉数据包,再一次没有让发送者知道。至少那个就是我们防火墙做的事情。并且我们的应用程序始终认为这个链接仍然打开,一直等待
一个永远不会来的回复。

ES的ping怎么样?


正如你读到的,这里有两个设置:client.transport.nodes_sampler_interval和client.transport.ping_timeout可以控制多长时间一次ES的客户检查是否一个远程节点可达到的。这两个设置默认设置为5秒钟,那就意味着客户端每隔5秒钟发送一个ping到每一个节点并且期望在5秒内得到一个回复或者认为这个节点不再可达了。所以当一个数据包每隔5秒钟发送的时候可能被防火墙杀掉,它是如何连接到节点上的呢?我误解了这个是怎么工作的,直到我读了这篇文章:https://www.elastic.co/blog/found-elasticsearch-networking/,这个解释的Java API的TransportClient比ES的文档更好。简单的总结就是当你可能认为在你的客户端和一个远程节点以及节点他们自己之间有一个单独的连接的时候。




实际上,这个是默认的他们当中的13个!




ES称这些为“通道”,非连接,但是从网络的角度看,他们都是正常的TCP连接。对于更详细的信息,就像配置实际的数量,可以去看看这个类的源码:NettyTransport。
这些13个连接中的一个是ping连接。它的目的是为了不要保持真实的连接去执行查询,但是只是决定一个远程的节点是否仍然可达的。以至于ping操作不会阻止我们的防火墙去关闭查询连接。

但是ES确定会做这个连接是否仍然可用的跟踪吗?

那么ES不会定期的检查它的查询连接,去看看他们是否是可用的吗?嗯,不会的,是的。它把这个责任委托给操作系统。
当操作一个对远程节点连接的时候,ES会默认设置TCP套接字选项“keepalive”。ES文档对于它的TCP设置简单描述连接如下:https://blog.trifork.com/2015/04/08/dealing-with-nodenotavailableexceptions-in-elasticsearch/comment-page-1/;它也指出对于“keepalive”默认的是什么样子的。
“Keepalive”告诉这个操作系统:
1.“我想让你保持这个连接打开,甚至当我没有发送数据给它的时候”。
2.“如果你发现其他的终端始终不再响应了,让我知道”。
确切的语义依赖于你的内核设置。默认的,LINUX将等待没有数据去发送连接两小时。然后这个“keepalive”将会死亡并且每隔75秒发送一个探测包,期望其他的终端去答复。如果连续9个探测都没有得到来自其他终端的响应,LINUX会总结这个连接确实死掉了,然后最后关闭它并且通知应用程序打开它(指的是我们的JAVA ES的TransportClient)。
“Keepalive”解决了我们的两个问题:
1.它确保了在连接期间一个常规的数据包流以至于防火墙不会因为不活动了而关闭它。
2.如果一个防火墙关闭一个连接了,这个操作系统会发现并且会通知应用程序以至于它不会留下来去思考这个连接是否是打开的。
为了得到关于TCP Keepalive更详细的信息,请看:http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/#whatis

改变TCP keepalive设置

现在用这个默认的设置,在LINUX最终确定这个连接最终死掉之前,它仍然将会花费两个小时加上每隔75秒的8个更多的探测等于两小时10分钟。那个对我们来说太长了。首先,因为很明显我们的防火墙认为对于一个连接空闲两个小时是太长时间了。第二,因为如果一个连接死掉了,我想让ES在几分钟之内发现,而不是小时级别的,以至于可以用一个新的连接代替一个死的连接。所以我们要改变这个默认的。在LINUX上,改变内核设置是用sysctl这个命令的。这个设置是:
1.net.ipv4.tcp_keepalive_time 应用程序已经发送最后的数据包后需要等待多长时间开始发送探测,TCP发送keepalive消息的频度
2.net.ipv4.tcp_keepalive_intvl 当探测没有响应时,重新发送探测的频度
3.net.ipv4.tcp_keepalive_probes在确认连接还没有死之前对于还没有响应的探测数
这些值都是以秒为级别的。去改变一个值,例如
sysctl -w net.ipv4.tcp_keepalive_time=600

去立刻一起修改他们,使用下面的:
sysctl -w \
net.ipv4.tcp_keepalive_time=600 \
net.ipv4.tcp_keepalive_intvl=60 \
net.ipv4.tcp_keepalive_probes=3


我分别选择600,60,3这三个值。如果你担心这个会因为探测包充满你的网络,你可以尝试更适度的方法。请意识到这个设置将会应用到有keepalive套接字选项设置的系统中所有打开的连接,而不仅仅是ES打开的那些。

如果你想看设置的影响,你可以使用下面的命令:
watch -n 1 netstat --tcp -t -o -n





这个将会使用watch命令去运行netstat命令在套接字上显示计时器并且每隔一秒就会刷新一次。你应该在后面看到“Timer”列。对于保持存活的连接,它将会写着keepalive紧跟着的是括号中有三个数字。第一个是保持存活的时间。请记住在你改变
这个设置之前已经被打开的套接字仍然会有一个老的存活时间。


保存sysctl设置

你通过sysctl命令做的设置当重启机器的时候就会丢失。为了使得这些设置持久化,你需要将他们存储在你的LINUX发行版中各自的配置文件中(例如,/etc/sysctl.conf),或者仅仅添加sysctl命令到启动脚本在文件/etc/init.d或者/etc/rd.d中。

总结

正确的配置TCP的keepalive设置可以保证从一个ES客户端到另外一个网络的节点的连接一直保持打开状态,甚至他们不再被使用一会儿。他们也会确保如果一个连接死掉了(因为防火墙丢弃它们,或者网络断开),客户端将会被很快通知。
这个就应该避免了在使用javaAPI的ES的时候节点挂起或者NoNodeAvailableExceptions。

摘要:
当节点在不同的网络中,如果你遇到一个来自ES的java客户端的NoNodeAvailableExceptions异常,那就降低你的LINUX得TCP的keepalive时间,如下:
sysctl -w \
net.ipv4.tcp_keepalive_time=600 \
net.ipv4.tcp_keepalive_intvl=60 \
net.ipv4.tcp_keepalive_probes=3


文章原地址:

https://blog.trifork.com/2015/04/08/dealing-with-nodenotavailableexceptions-in-elasticsearch/comment-page-1/


0 0