异常产生的运行环境是这样的,redis集群部署在docker下,docker宿主机是一台linux的虚拟机,而程序在我本机

今天在使用JedisCluster向redis集群set值的时候,后台突然报出了一个这样的异常:

redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException: Too many Cluster redirections?
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:95)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:135)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:135)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:135)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:135)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:135)
at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:30)
at redis.clients.jedis.JedisCluster.setex(JedisCluster.java:313)
at slg.rainbow.user.service.impl.UserServiceImpl.doLogin(UserServiceImpl.java:53)
……

上网搜了半天,但是来来回回就是那几篇文章,大抵上就是redis.conf配置文件中的bind 127.0.0.1没注释,或者创建集群时候IP写错了,又或者在给jedisCluster配置节点的时候配错了之类。

但是我挨个进到docker的redis容器中查看,都没有问题,而且相互之间通信正常,使用redis-cli客户端连上之后也可以正常set值进去。也有解决方案让把DEFAULT_MAX_REDIRECTIONS这个默认参数调大的,但是就算我调到很大,也没卵用。那么问题到底出在哪呢?

我程序里连接redis的配置是这样的:

redis:
	clusterNodes:192.168.116.150:7000,192.168.116.150:7001,192.168.116.150:7002,192.168.116.150:7003,192.168.116.150:7004,192.168.116.150:7005,192.168.116.150:7006,192.168.116.150:7007,192.168.116.150:7008

其中每个节点都是一个docker容器,宿主机的IP是192.168.116.150各个容器对外暴露的端口如下:

redission too many open files原因 redis too many cluster_redis


程序连接redis时并没有报No reachable node in cluster这种找不到集群的异常,说明集群暴露的地址和端口均正常;而直接通过客户端redis-cli连接集群时,也能正常set值,说明集群slot和内部通信也没问题。那么唯一的可能就是:使用JedisCluster连接集群时,集群的内部通信出了问题!redis集群在docker内部的网段是在172上,如图:

redission too many open files原因 redis too many cluster_redis_02


我就想,会不会是在用了JedisCluster之后,集群内部通信时使用的是程序中配置的IP呢?如果真是那样,由于集群内部通信的网段在172上,而宿主机在192网段(程序里配置的是192),那集群间必然是无法正常内部通信的!一个172的节点怎么能找到另一个192的节点呢?

为了验证这个想法,我将yml中的配置改成:

redis:
  clusterNodes: 172.17.0.2:6379,172.17.0.3:6379,172.17.0.4:6379,172.17.0.5:6379,172.17.0.6:6379,172.17.0.7:6379,172.17.0.8:6379,172.17.0.9:6379,172.17.0.10:6379

然后将springboot应用打包成war,也扔到一个docker容器中去运行,这样的话程序就和redis集群在同一个内网环境中了。

果不其然,当JedisCluster和redis集群在同一个网段时,没有再报出Too many Cluster redirections这个异常,值也顺利set进去了!

这下原因虽然找到了,可是情况也变尴尬了。假如我非得在程序里配置172网段连接redis,而程序又运行在我本机,那么必然会报一个:

redis.clients.jedis.exceptions.JedisNoReachableClusterNodeException: No reachable node in cluster
at redis.clients.jedis.JedisSlotBasedConnectionHandler.getConnection(JedisSlotBasedConnectionHandler.java:61)
at redis.clients.jedis.JedisSlotBasedConnectionHandler.getConnectionFromSlot(JedisSlotBasedConnectionHandler.java:78)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:113)
at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:30)
at redis.clients.jedis.JedisCluster.setex(JedisCluster.java:313)

这样的异常。因为我的程序只能连到宿主机上,通过映射出来的的端口才能连到docker容器中的应用,docker内网的IP(172网段)对我本机的程序来说其实是不可见的。

所以,这个问题好像无法调和,我在开发阶段基本上别想直接使用docker里的这个redis集群了,只有打成war包发布到容器时才可以用它。悲催……也想过把docker network改成192网段这种将错就错的法子,但是总觉得心里不太踏实,所以只能再开一个虚拟机搭一套redis集群了。