设为首页 收藏本站
查看: 853|回复: 0

[经验分享] 【ZooKeeper Notes 29】 修复“ZooKeeper客户端打印当前连接的服务器地址为null”的Bug

[复制链接]

尚未签到

发表于 2019-1-8 09:05:53 | 显示全部楼层 |阅读模式
  转载请注明:@ni掌柜 nileader@gmail.com
  问题描述
  公司之前进行了几次机房容灾演习中,经常是模拟一个机房挂掉的场景,把一个机房的网络切掉,使得这个机房内部网络通信正常,与外部的网络不通。在容灾演习过程中,我们发现ZK的客户端应用中出现大量类似这样的日志:


  •   An exception was thrown while closing send thread for ession 0x for server null, unexpected error, closing socket connection and attempting  

  从这个日志中,红色部分出现的是null。当时看到这个情况,觉得,正常情况正在,这个地方应用出现的是那个被隔离的机房中部署的ZK的机器IP的,但是这里出现的是null,非常困惑。
  具体描述也可以在这里查看:https://issues.apache.org/jira/browse/ZOOKEEPER-1480
   问题定位
  看了下3.4.3及其以前版本的ZooKeeper代码,发现问题出在这里,日志打印的逻辑在这里:


  • } catch (Throwable e) {  
  •     if (closing) {  
  •         if (LOG.isDebugEnabled()) {  
  •             // closing so this is expected  
  •             LOG.debug("An exception was thrown while closing send thread for session 0x"  
  •                     + Long.toHexString(getSessionId())  
  •                     + " : " + e.getMessage());  
  •         }  
  •         break;  
  •     } else {  
  •         // this is ugly, you have a better way speak up  
  •         if (e instanceof SessionExpiredException) {  
  •             LOG.info(e.getMessage() + ", closing socket connection");  
  •         } else if (e instanceof SessionTimeoutException) {  
  •             LOG.info(e.getMessage() + RETRY_CONN_MSG);  
  •         } else if (e instanceof EndOfStreamException) {  
  •             LOG.info(e.getMessage() + RETRY_CONN_MSG);  
  •         } else if (e instanceof RWServerFoundException) {  
  •             LOG.info(e.getMessage());  
  •         } else {  
  •             LOG.warn(  
  •                     "Session 0x"  
  •                             + Long.toHexString(getSessionId())  
  •                             + " for server "  
  •                             + clientCnxnSocket.getRemoteSocketAddress()  
  •                             + ", unexpected error"  
  •                             + RETRY_CONN_MSG, e);  
  •         }  

  可以看到,在打印日志过程,是通过clientCnxnSocket.getRemoteSocketAddress() 来获取当前连接的服务器地址的,那再来看下这个方法:


  • /**
  •      * Returns the address to which the socket is connected.
  •      * @return ip address of the remote side of the connection or null if not connected
  •      */
  •     @Override
  •     SocketAddress getRemoteSocketAddress() {
  •         // a lot could go wrong here, so rather than put in a bunch of code
  •         // to check for nulls all down the chain let's do it the simple
  •         // yet bulletproof way
  •         try {
  •             return ((SocketChannel) sockKey.channel()).socket()
  •                     .getRemoteSocketAddress();
  •         } catch (NullPointerException e) {
  •             return null;
  •         }
  • }
  •     /**
  •      * Returns the address of the endpoint this socket is connected to, or
  •      * null if it is unconnected.
  •      * @return a SocketAddress reprensenting the remote endpoint of this
  •      *         socket, or null if it is not connected yet.
  •      * @see #getInetAddress()
  •      * @see #getPort()
  •      * @see #connect(SocketAddress, int)
  •      * @see #connect(SocketAddress)
  •      * @since 1.4
  •      */
  •     public SocketAddress getRemoteSocketAddress() {
  •       if (!isConnected())
  •         return null;
  •       return new InetSocketAddress(getInetAddress(), getPort());
  • }

  所以,现在基本就可以定位问题了,如果服务器端非正常关闭socket连接(例如容灾演习的时候把机房网络切断),那么getRemoteSocketAddress这个方法就会返回null了,也就是日志中为什么出现null的原因了。
  问题解决
  这个日志输出对于开发人员来说非常重要,在排查问题过程中可以清楚的定位当时是哪台服务器出现问题,但是这里一旦输出null,那么将无从下手。这里我做了一些改进,确保出现问题的时候,客户端能够输出当前出现问题的服务器IP。在这里下载补丁:https://github.com/downloads/nileader/taokeeper/getCurrentZooKeeperAddr_for_3.4.3.patch
  首先是给org.apache.zookeeper.client.HostProvider类添加两个接口,分别用于获取“当前地址列中正在使用的地址序号”和获取“所有地址列表”。关于ZooKeeper客户端地址列表获取和随机原理,具体可以查看这个文章《ZooKeeper客户端地址列表的随机原理》。


  • public interface HostProvider {
  •     …… ……
  •     /**  
  •      * Get current index that is connecting or connected.  
  •      * @see ZOOKEEPER-1480:https://issues.apache.org/jira/browse/ZOOKEEPER-1480
  •      * */
  •     public int getCurrentIndex();
  •     /**
  •      * Get all server address that config when use zookeeper client.
  •      * @return List  
  •      * @see ZOOKEEPER-1480:https://issues.apache.org/jira/browse/ZOOKEEPER-1480
  •      */
  •     public List getAllServerAddress();
  •      
  • }

  其次是修改org.apache.zookeeper.ClientCnxn类中日志输出逻辑:


  • /**
  •          * Get current zookeeper addr that client is connected or connecting.
  •          * Note:The method will return null if can't not get host ip.
  •          * */
  •         private InetSocketAddress getCurrentZooKeeperAddr(){
  •             try {
  •                 InetSocketAddress addr = null;
  •                 if( null == hostProvider || null == hostProvider.getAllServerAddress() )
  •                     return addr;
  •                 int index = hostProvider.getCurrentIndex();
  •                 if ( index >= 0  ) {
  •                     addr = hostProvider.getAllServerAddress().get( index );
  •                 }
  •                 return addr;
  •             } catch ( Exception e ) {
  •                 return null;
  •             }
  •         }
  • …… ……
  •         //get current ZK host to log
  •         InetSocketAddress addr = getCurrentZooKeeperAddr();
  •          
  •         LOG.warn(
  •             "Session 0x"
  •                     + Long.toHexString(getSessionId())
  •                     + " for server ip: " + addr + ", detail conn: "
  •                     + clientCnxnSocket.getRemoteSocketAddress()
  •                     + ", unexpected error"
  •                     + RETRY_CONN_MSG, e);



附件:http://down.运维网.com/data/2361716


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-660580-1-1.html 上篇帖子: Zookeeper简介(三) 下篇帖子: ZooKeeper安装实例
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表