背景

    先澄清一下,整个过程问题都不是我解决的,我在里面就是起了个打酱油的角色。因为实际上我负责这个项目,整个过程也比较清楚。之前也跟具体负责的同事说过,等过段时间带他做做项目复盘。结果一直忙,之前做的事情都快忘了也没带他做复盘。所以趁着还记得,总结一下这个问题,也算一起做个复盘总结了。

    本周一的时候,我们测试环境遇到一个问题:启动一个服务就会导致后端调用耗时增加。当时咨询了对这个问题之前有了解的同事得到的答复是因为一笔请求发到两套测试环境(一个请求需要在两套环境下运行结果做对比),因为这两套环境共用同一套redis集群。收到第二个相同请求的时候,会将这笔请求标记为重复请求。下游接收到这笔请求是重复的,需要重新查询数据库验证请求是否重复,不是的话做一个纠正。所以这时候会造成请求延迟升高。

    负责解决这个问题的同事小A就问我:那是不是再搭建一套将两套Redis集群分开就解决了。我说不一定,还有解释不通的地方:一个环境服务不启动不写redis,另一个环境服务启动写redis的时候也会遇到这个问题。

    于是小A找了这个服务相关负责的同事了解业务。因为测试环境的总责任人是我,所以了解业务的时候,小A也把我拉了一起了解。通过同事的讲述了解到一个环境中服务要写两个机房。如果两个机房的Redis是同一套也会被标记成重复请求。

    至此,解决环境问题的方法有了答案:每套环境要搭建两套Redis集群,两个环境4套Redis集群来解决问题。

    往往,一个答案只是一系列问题的开始。

 

时间线

    我们的搭建方案是直接在使用Redis的服务上搭建连接它的两套Redis集群,只改下端口,多跑两个进程。

问题1:服务器退出登录Redis服务会停止

     小A告诉我遇到问题的现象:按照网上经典的安装启动教程,启动成功了。但是当干会儿别的,ssh自动退出登录之后再看Redis服务就停止了。

     我听到这里首先想到的是这个现象基本可以断定是以非daemon进程在运行,于是我上网上找了以deamon方式运行的命令发给小A:

redis-server ./redis.conf --daemonize yes

     小A看了解决方法补充到那一定也可以在配置文件里直接配置daemon方式运行。我表示赞同,他也是这么做的。我当时没有点破,相信刚毕业的他不久也自己会发现配置文件和显示命令实际上是一回事。只是一个是永久生效,一个是每次运行时生效。而直接用这条命令只是为了说明本质问题。

问题2:服务连接Redis报错Not Auth

     小A又向我反馈报了一个错,说他在网上查的是Redis版本问题,估计需要重新搭建Redis。我过去看了一下:Redis集群是3.X的版本,jedis客户端用的是2.9的版本。没有听说过Redis3.X的版本有不向下兼容的问题,同时因为这个Redis是从负责Redis的团队要过来的安装包,应该和现在跑着的是一个版本。如果怀疑Redis的团队发的安装包与之前不一样的话,我也确信之前肯定版本不会低于3.0,因为Redis是从3.0之后才支持集群的。所以我判定不是Redis版本问题。让他再查查。实际上我的意思是让他换关键词来查。比如可以按照报错的提示原因来查,也可以按照异常来查,不同的关键词搜索可以获得不同的信息。

    然后我看了报的错:其他的没细看,只见赫然写着:Not Auth。我就问:Redis服务有没有设置密码。他说没有,还演示了一下,我在旁边确认了没有。就查看客户端配置有没有配置密码。果然客户端里有密码配置。这就与服务端不匹配了。

问题3:报错cluster support disabled

    小A将客户端密码去掉重新打包部署之后,Not Auth的错不报了,但是其实报了两个错,还有一个错没解决:就是提示cluster support disabled。

    我说集群方式启动应该就是一个配置,应该有个cluster-enabled什么的从no改成yes。我还出了个馊主意(注意这里用到的馊主意,想想《红楼梦》里每句话都是剧透,这里也不例外):我说理论上一台集群也可以算一个集群。应该可以直接改个配置就以集群方式启动了。

    小A按照我说的思路用直接改配置为集群的方法,客户端再启动果然没有报错了。

问题4:请求延迟没有好转,Redis服务端没有写入成功数据

    客户端没有报错之后,小A重试原问题现场,请求延迟没有好转。另外,还发现Redis服务端没有写入成功的数据。

    这次我和小A首先一起排查配置有没有配置对。发现配置没有问题,我就跟小A说:让他多打日志。客户端连接的地方打一些,读写数据的地方打一些。

    通过这个方法,小A定位到客户端连接的连接池为空。最终自己排查到是一台机器的集群的哈希槽在一台机器情况下哈希槽分配有问题,数据写入失败。最后每个集群多起了2个Redis进程做成3个节点的集群解决了问题。

 

可优化的排查思路分析

    在问题4排查的时候,我和小A一起检查了配置是否正确来确认Redis请求是请求到了正确的服务端。其实,有个更为直接和说明问题的方法:抓包。可以tcpdump端口查请求流量是不是正确从客户端发出来了,被转发到了哪里。

    在《技术方案设计的方法》里我也提到,很多时候搜索不到自己想要的信息很可能是关键词的问题。排查问题的时候也可以试着换换关键词来搜索。

 

根本原因分析

    这里面有个问题没有彻底搞清楚:为什么一台机器的Redis集群会有问题。

    问了小A,当时异常时getSlots方法时返回了空。就是说问题实际上可能是slot没有被分配。

    我就问他单台机器的时候有没有在redis-cli客户端上运行cluster info命令。他给我发了下面的运行情况截图。

Redis集群搭建采坑总结_Redis集群

 

 

    这张截图验证了我的猜想,slot没有被分配,集群状态为失败,所以连接不上。

    那需要连接上的条件并非是集群里有几个节点,而是slots分配,集群状态成功。

Redis集群搭建采坑总结_Redis集群_02

 

 

    为了验证这个猜想,我搭建了一个一个节点的集群,手动cluster addslots了0到16383个slot。集群判断16384个slot都分配完毕,自动状态改成OK。

    

    这整个过程说明了:网上都是说Redis集群必须是3个节点以上的最好是单数个节点来启动。单数个节点是为了投票的时候可以三局两胜得出结论:一半以上的节点挂掉整个集群不可用。而对于Redis根本上判断集群是否可用是根据slot有没有完整的16384个slot在提供服务决定的。

 

总结

    我觉得在整个过程中小A的表现我觉得很OK的。有4点:

1>主观能动性

    在过程中,他自己通过网上搜素找资源,自己解决了很多问题。整个问题处理过程中其实没花费我多少时间,花时间的事情他都自己解决了。

2>合理的利用了各种资源

    对于业务不理解,他找了理解业务的同事。技术问题搞不定他找了我。因为我对项目负责,所以找我是很合情合理的。同时,我是很希望他遇到这种事情来找我的。因为他找我证明他是信任我的,相信我能一定程度帮到他。第二,他找我是把我当成一种资源。作为资源我被需要,是有价值的。被需要让人觉得很踏实。

    我在有搞不定的事情的时候也向上寻求帮助。比如之前需要其他组协作的时候人家有排期遇到困难,领导出面帮忙搞定了。还有申请资源由于暂时性资源紧张,申请不到,也是更上级出面帮忙搞定了。一个称职的上级一定可以成为一种资源,也愿意让自己成为资源。但是成为资源的形式不同,有的可能提供的是战略,有的提供的是精神支持等。

3>事后总结

    问题解决后,小A有自己写wiki总结事情经过,避免后人采坑,同时自身也有总结收获。

4>及时沟通

    中间过程中,他每个关键步骤都有及时跟我沟通。因为解决完后他反馈给我:他自己觉得对于redis原理还没有理解,所以不清楚为什么3个节点就OK的原因。我因为了解他的想法,所以才自己又实验给出一个根本原因分析。

     关于Redis,就一句话:那些很多人说只有面试的时候才能用到的东西,我总是发现实际工作中很有用。