redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException: Too many Cluster redirections?
使用flink往redis集群写数据时暴露的问题,通过正常的JedisCluster类进行连接操作
private static JedisCluster jedisCluster;
jedisCluster = new JedisCluster(hostAndPortSet,connectionTimeout,soTimeout,maxAttempts,password,config);
异常堆栈信息如下:
flink集群和redis集群网络是不通的,我们是通过一台机器用nginx进行中转
在一台网络和redis集群通的机器上测试过,也是通过中转网络进行操作的,发现是没有问题,也通过redis shell客户端使用代理端口进行连接,也把代码copy出来放在服务器上测试,发现没啥问题的。当时不知道网络是通的,总以为是通过中转网络进行操作redis集群的,看报错,又不是那种**No reachable node in cluster**连接不上的问题,思路就往nginx方向想了,集群参数redis.maxAttempts用的默认值5,这个值也尝试调大过,在设置为0的时候会抛出No reachable node in cluster异常,于是想着是不是nginx多次转发的问题,于是在测试环境也构建一套nginx服务,并且进行二次转发。nginx配置如下。发现问题还是没有解决。
nginx-stream
#redis代理测试
stream {
upstream redis1 {
#redis真实访问地址
server machine1:6380 max_fails=3 fail_timeout=30s;
}
upstream redis2 {
#redis真实访问地址
server machine1:6381 max_fails=3 fail_timeout=30s;
}
upstream redis3 {
#redis真实访问地址
server machine2:6380 max_fails=3 fail_timeout=30s;
}
upstream redis4 {
#redis真实访问地址
server machine2:6381 max_fails=3 fail_timeout=30s;
}
upstream redis5 {
#redis真实访问地址
server machine3:6380 max_fails=3 fail_timeout=30s;
}
upstream redis6 {
#redis真实访问地址
server machine3:6381 max_fails=3 fail_timeout=30s;
}
server {
#外网监听地址
listen 7001;
#代理连接超时时间
proxy_connect_timeout 5s;
#代理超时时间
proxy_timeout 300s;
#代理名称
proxy_pass redis1;
}
server {
#外网监听地址
listen 7002;
#代理连接超时时间
proxy_connect_timeout 5s;
#代理超时时间
proxy_timeout 300s;
#代理名称
proxy_pass redis2;
}
server {
#外网监听地址
listen 7003;
#代理连接超时时间
proxy_connect_timeout 5s;
#代理超时时间
proxy_timeout 300s;
#代理名称
proxy_pass redis3;
}
server {
#外网监听地址
listen 7004;
#代理连接超时时间
proxy_connect_timeout 5s;
#代理超时时间
proxy_timeout 300s;
#代理名称
proxy_pass redis4;
}
server {
#外网监听地址
listen 7005;
#代理连接超时时间
proxy_connect_timeout 5s;
#代理超时时间
proxy_timeout 300s;
#代理名称
proxy_pass redis5;
}
server {
#外网监听地址
listen 7006;
#代理连接超时时间
proxy_connect_timeout 5s;
#代理超时时间
proxy_timeout 300s;
#代理名称
proxy_pass redis6;
}
upstream redis7 {
#redis真实访问地址
server machine1:7001 max_fails=3 fail_timeout=30s;
}
upstream redis8 {
#redis真实访问地址
server machine1:7002 max_fails=3 fail_timeout=30s;
}
upstream redis9 {
#redis真实访问地址
server machine2:7003 max_fails=3 fail_timeout=30s;
}
upstream redis10 {
#redis真实访问地址
server machine2:7004 max_fails=3 fail_timeout=30s;
}
upstream redis11 {
#redis真实访问地址
server machine3:7005 max_fails=3 fail_timeout=30s;
}
upstream redis12 {
#redis真实访问地址
server machine3:7006 max_fails=3 fail_timeout=30s;
}
server {
#外网监听地址
listen 7007;
#代理连接超时时间
proxy_connect_timeout 5s;
#代理超时时间
proxy_timeout 300s;
#代理名称
proxy_pass redis7;
}
server {
#外网监听地址
listen 7008;
#代理连接超时时间
proxy_connect_timeout 5s;
#代理超时时间
proxy_timeout 300s;
#代理名称
proxy_pass redis8;
}
server {
#外网监听地址
listen 7009;
#代理连接超时时间
proxy_connect_timeout 5s;
#代理超时时间
proxy_timeout 300s;
#代理名称
proxy_pass redis9;
}
server {
#外网监听地址
listen 7010;
#代理连接超时时间
proxy_connect_timeout 5s;
#代理超时时间
proxy_timeout 300s;
#代理名称
proxy_pass redis10;
}
server {
#外网监听地址
listen 7011;
#代理连接超时时间
proxy_connect_timeout 5s;
#代理超时时间
proxy_timeout 300s;
#代理名称
proxy_pass redis11;
}
server {
#外网监听地址
listen 7012;
#代理连接超时时间
proxy_connect_timeout 5s;
#代理超时时间
proxy_timeout 300s;
#代理名称
proxy_pass redis12;
}
}
整的头皮发麻
后面思路往JedisCluster Client方向思考,看了下JedisCluster的源码,分析如下
1.JedisCluster在初始化的时候用代理的ip和端口和ip进行初始化连接
2.初始化后会使用redis的**cluter nodes**命令将,集群的主从信息,包括solt信息进行缓存
3.在使用redis进行set命令的时候,会根据CRC16得到slot从pool中找到对应的某台机器ip和端进行连接,再写入数据,读的话会先匹配从机进行连接
4.核心就在这,实际上JedisCluster保存的是真实的redis集群地址,并不是代理的,在操作redis的时候发现网络不通,所以就抛出本文的这种的异常
具体缓存的集群信息可以打印出来看看
Map<String, JedisPool> clusterNodes = jedisCluster.getClusterNodes();
System.out.println(clusterNodes);
在测试的时候,我们有三台机器,默认防火墙是关掉的,所以不会抛这种问题,网络也是通的,没有复现生产上的问题。
在开启某一台机器的防火墙后,发现会问题和本文一样,初始化JedisCluster Client是没有问题的,在对redis进行操作的时候,就会抛出这种异常。如果在初始化JedisCluster Client后不去对集群进行操作,发现程序是不会报错的,也不会停止运行的。就算三台机器开启两台防火墙,只留一台不开,网络通,在初始化JedisCluster Client的时候也是不会抛出异常的。