建议你看看 dubbo 的源码,其中有用到 Redis 的发布订阅 com.alibaba.dubbo.registry.redis.RedisRegistry.Notifier 这个是订阅的线程类
private class Notifier extends Thread {
private final String service;
private volatile Jedis jedis;
private volatile boolean first = true;
private volatile boolean running = true;
private final AtomicInteger connectSkip = new AtomicInteger();
private final AtomicInteger connectSkiped = new AtomicInteger();
private final Random random = new Random();
private volatile int connectRandom;
private void resetSkip() {
connectSkip.set(0);
connectSkiped.set(0);
connectRandom = 0;
}
private boolean isSkip() {
int skip = connectSkip.get(); // 跳过次数增长
if (skip >= 10) { // 如果跳过次数增长超过10,取随机数
if (connectRandom == 0) {
connectRandom = random.nextInt(10);
}
skip = 10 + connectRandom;
}
if (connectSkiped.getAndIncrement() < skip) { // 检查跳过次数
return true;
}
connectSkip.incrementAndGet();
connectSkiped.set(0);
connectRandom = 0;
return false;
}
public Notifier(String service) {
super.setDaemon(true);
super.setName("DubboRedisSubscribe");
this.service = service;
}
@Override
public void run() {
while (running) {
try {
if (! isSkip()) {
try {
for (Map.Entry entry : jedisPools.entrySet()) {
JedisPool jedisPool = entry.getValue();
try {
jedis = jedisPool.getResource();
try {
if (service.endsWith(Constants.ANY_VALUE)) {
if (! first) {
first = false;
Set keys = jedis.keys(service);
if (keys != null && keys.size() > 0) {
for (String s : keys) {
doNotify(jedis, s);
}
}
resetSkip();
}
jedis.psubscribe(new NotifySub(jedisPool), service); // 阻塞
} else {
if (! first) {
first = false;
doNotify(jedis, service);
resetSkip();
}
jedis.psubscribe(new NotifySub(jedisPool), service + Constants.PATH_SEPARATOR + Constants.ANY_VALUE); // 阻塞
}
break;
} finally {
jedisPool.returnBrokenResource(jedis);
}
} catch (Throwable t) { // 重试另一台
logger.warn("Failed to subscribe service from redis registry. registry: " + entry.getKey() + ", cause: " + t.getMessage(), t);
// 如果在单台redis的情况下,需要休息一会,避免空转占用过多cpu资源
sleep(reconnectPeriod);
}
}
} catch (Throwable t) {
logger.error(t.getMessage(), t);
sleep(reconnectPeriod);
}
}
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
}
}
public void shutdown() {
try {
running = false;
jedis.disconnect();
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
}
private class NotifySub extends JedisPubSub {
private final JedisPool jedisPool;
public NotifySub(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
@Override
public void onMessage(String key, String msg) {
if (logger.isInfoEnabled()) {
logger.info("redis event: " + key + " = " + msg);
}
if (msg.equals(Constants.REGISTER)
|| msg.equals(Constants.UNREGISTER)) {
try {
Jedis jedis = jedisPool.getResource();
try {
doNotify(jedis, key);
} finally {
jedisPool.returnResource(jedis);
}
} catch (Throwable t) { // TODO 通知失败没有恢复机制保障
logger.error(t.getMessage(), t);
}
}
}
@Override
public void onPMessage(String pattern, String key, String msg) {
onMessage(key, msg);
}
@Override
public void onSubscribe(String key, int num) {
}
@Override
public void onPSubscribe(String pattern, int num) {
}
@Override
public void onUnsubscribe(String key, int num) {
}
@Override
public void onPUnsubscribe(String pattern, int num) {
}
}