最近项目中使用redis遇到了一个异常(redis版本3.2.3),分享出来供大家参考:
异常如下:
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:53)
at redis.clients.jedis.JedisPool.getResource(JedisPool.java:226)
at redis.clients.jedis.JedisSlotBasedConnectionHandler.getConnectionFromSlot(JedisSlotBasedConnectionHandler.java:66)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:116)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:141)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:141)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:141)
at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:141)
at redis.clients.jedis.JedisClusterCommand.runBinary(JedisClusterCommand.java:60)
at redis.clients.jedis.BinaryJedisCluster.hkeys(BinaryJedisCluster.java:448)
at org.springframework.data.redis.connection.jedis.JedisClusterConnection.hKeys(JedisClusterConnection.java:2388)
... 14 more
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.NoRouteToHostException: 没有到主机的路由
at redis.clients.jedis.Connection.connect(Connection.java:207)
at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:93)
at redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:1767)
at redis.clients.jedis.JedisFactory.makeObject(JedisFactory.java:106)
at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:836)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:457)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:361)
at redis.clients.util.Pool.getResource(Pool.java:49)
... 24 more
Caused by: java.net.NoRouteToHostException: 没有到主机的路由
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at redis.clients.jedis.Connection.connect(Connection.java:184)
... 31 more
看到异常第一反应是网络不通或者redis没有启动
查看后,发现redis启动的,但是配置了防火墙,然后加上了白名单
但是还是不能访问。
继续研究发现redis HA 是通过haproxy+redis实现的。访问接口提供了haproxy的接口。haproxy中映射了多个redis。
redis实例所在的主机也配置了防火墙,是不是应用直接访问了redis实例,而没有通过haproxy间接访问redis。
通过查看代码确实如此。
应用通过spring配置了集群模式访问redis。
应用启动的时候通过haproxy访问redis,并执行CLUSTER SLOTS命令,这个命令是redis3.0出现的,用来获取redis集群的所有节点信息。代码如下:
然后将节点信息放到连接池中,这样以后使用redis的时候,直接连接节点,而不再通过haproxy,当然也可以在配置中直接连接一个redis节点,不用连接haproxy,但是haproxy在第一次连接的时候可以在多个redis节点中找到一个有效的节点并返回,所以haproxy还是有用的。
使用redis时,从pool中取出一个,而且pool中多个redis还要shuffle一下,确保取出的节点是随机的一个,然后测试节点是否有效,如果无效,继续取下一个,直到取完。代码如下:
spring redis集群配置
这个配置中只配置了一个节点,实际这应该至少配置两个节点,这样应用的可用性会更高一些,haproxy也可以不用了。