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<Pool> jedisPools = new ArrayList<>();
|
|
private String masterName;
|
|
private GenericObjectPoolConfig poolConfig;
|
|
private Set<HostAndPort> sentinelSet;
|
|
private CreateJedisPool createJedisPool = new CreateJedisPool() {
|
};
|
|
private JedisSentinelPool jedisSentinelPool;
|
|
private final String PONG = "PONG";
|
|
|
public RedisSentinelUtil(Set<String> sentinels, JedisSentinelPool jedisSentinelPool, GenericObjectPoolConfig poolConfig, String masterName) {
|
this(sentinels, jedisSentinelPool, poolConfig, masterName, null);
|
}
|
|
public RedisSentinelUtil(Set<String> 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<HostAndPort> redisClient = new ArrayList<>();
|
try {
|
for (HostAndPort hostAndPort : this.sentinelSet) {
|
Jedis jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort());
|
List<Map<String, String>> maps = jedis.sentinelSlaves(this.masterName);
|
for (Map<String, String> 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);
|
}
|
}
|
}
|