package com.product.datasource.utils; import com.product.core.spring.context.SpringMVCContextHolder; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.*; import java.util.*; import java.util.stream.Collectors; /** * @Author cheng * @Date 2022/12/6 20:42 * @Desc */ public class RedisSentinelUtil { private List jedisPools = new ArrayList<>(); private String masterName; private GenericObjectPoolConfig poolConfig; private Set sentinelSet; private CreateJedisPool createJedisPool = new CreateJedisPool() { }; private JedisSentinelPool jedisSentinelPool; private final String PONG = "PONG"; public RedisSentinelUtil(Set sentinels, JedisSentinelPool jedisSentinelPool, GenericObjectPoolConfig poolConfig, String masterName) { this(sentinels, jedisSentinelPool, poolConfig, masterName, null); } public RedisSentinelUtil(Set sentinels, JedisSentinelPool jedisSentinelPool, GenericObjectPoolConfig poolConfig, String masterName, CreateJedisPool createJedisPool) { Jedis jedis = jedisSentinelPool.getResource(); if (!PONG.equals(jedis.ping())) { throw new RuntimeException("redis 连接错误"); } this.poolConfig = poolConfig; this.masterName = masterName; this.createJedisPool = createJedisPool != null ? createJedisPool : this.createJedisPool; this.jedisSentinelPool = jedisSentinelPool; this.sentinelSet = sentinels.stream().map(item -> HostAndPort.parseString(item)).collect(Collectors.toSet()); changeSlave(); } void changeSlave(Pool pool) { closePool(pool); this.changeSlave(); } private void closePool(Pool pool) { if (pool == null) { return; } try { synchronized (jedisPools) { this.jedisPools.remove(pool); } pool.getJedisPool().close(); } catch (Exception e) { } } private void changeSlave() { try { List redisClient = new ArrayList<>(); try { for (HostAndPort hostAndPort : this.sentinelSet) { Jedis jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort()); List> maps = jedis.sentinelSlaves(this.masterName); for (Map map : maps) { String ip = map.get("ip"); String port = map.get("port"); HostAndPort slaveHostAndPort = new HostAndPort(ip, Integer.valueOf(port)); if (redisClient.contains(slaveHostAndPort)) { continue; } redisClient.add(slaveHostAndPort); } jedis.close(); } } catch (Exception e) { } if (jedisPools != null && !jedisPools.isEmpty()) { for (int i = 0; i < jedisPools.size(); i++) { Pool pool = jedisPools.get(i); if (!redisClient.contains(pool)) { JedisPool jedisPool = pool.getJedisPool(); if (!jedisPool.isClosed()) { try { jedisPool.close(); } catch (Exception e) { } } synchronized (jedisPools) { jedisPools.remove(i); } i--; } } } synchronized (jedisPools) { for (HostAndPort hostAndPort : redisClient) { jedisPools.add(new Pool(hostAndPort, createJedisPool.create(hostAndPort, this.poolConfig))); } } } catch (Exception e) { if (jedisPools != null && !jedisPools.isEmpty()) { for (int i = 0; i < jedisPools.size(); i++) { closePool(jedisPools.get(i)); } } e.printStackTrace(); SpringMVCContextHolder.getSystemLogger().error("创建Slave Redis连接池出错"); SpringMVCContextHolder.getSystemLogger().error(e); } } /** * 获取只读的Redis连接 * 若获取角色为 slave的失败则会获取master * * @return */ public Jedis getResource() { Pool pool = null; try { synchronized (jedisPools) { if (jedisPools != null && !jedisPools.isEmpty()) { pool = jedisPools.get(0); //将已取出的对象移到集合尾部 实现简单的负载均衡 Collections.swap(jedisPools, 0, jedisPools.size() - 1); } else { changeSlave(); if (jedisPools != null && !jedisPools.isEmpty()) { return getResource(); } } } if (pool != null) { Jedis resource = pool.getJedisPool().getResource(); System.out.println(String.format("获取到只读连接host:%s,port:%s", resource.getClient().getHost(),resource.getClient().getPort())); return resource; } } catch (Exception e) { e.printStackTrace(); SpringMVCContextHolder.getSystemLogger().error("获取Slave Redis连接出错"); SpringMVCContextHolder.getSystemLogger().error(e); changeSlave(pool); } return this.jedisSentinelPool.getResource(); } interface CreateJedisPool { default JedisPool create(HostAndPort hostAndPort, GenericObjectPoolConfig poolConfig) { return new JedisPool(poolConfig, hostAndPort.getHost(), hostAndPort.getPort()); } } class Pool { private HostAndPort hostAndPort; private JedisPool jedisPool; public Pool(HostAndPort hostAndPort, JedisPool jedisPool) { this.hostAndPort = hostAndPort; this.jedisPool = jedisPool; } public HostAndPort getHostAndPort() { return hostAndPort; } public JedisPool getJedisPool() { return jedisPool; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj instanceof HostAndPort) { return this.hostAndPort.equals(obj); } return super.equals(obj); } } }