在分布式系统中,延时队列是一种常见的需求。它允许我们在指定的时间后执行某个操作,或者在指定的时间后发送一条消息。Redisson是一个在Redis上实现的Java客户端,它提供了丰富的分布式对象和服务,包括延时队列。
和之前讲过的事件实现一样,我们首先定义事件
java
public class RedissonEvent<T extends BaseEvent> implements Serializable {
/**
* DATA
*/
private T data;
/**
* TAG
*/
private String tag;
private String className;
public RedissonEvent() {
}
public RedissonEvent(T data, String tag, String className) {
this.data = data;
this.tag = tag;
this.className = className;
}
public RedissonEvent(T data, String tag) {
this.data = data;
this.tag = tag;
this.className = data.getClass().getName();
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public void setClassName(String className) {
this.className = className;
}
public String getClassName() {
return className;
}
}
发送者:
java
@Slf4j
public class RedissonEventPublisher implements EventPublisher {
private final RedissonClient redissonClient;
public RedissonEventPublisher(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
/*
**
* @Date: 2024/1/15 18:09
* @MethodName: sendMessage
* @Description: 发送消息
* @param: payload 消息体
* @param: msgTag 消息的tag
* @Return: boolean
**/
@Override
public <T extends BaseEvent> boolean sendMessage(T message, String msgTag) {
return sendMessage(message, msgTag, 0, TimeUnit.SECONDS);
}
@Override
public <T extends BaseEvent> boolean sendMessage(T payload, String msgTag, long delayedTimes, TimeUnit timeUnit) {
sendMessageByRedisson(payload, msgTag, delayedTimes, timeUnit);
return true;
}
private <T extends BaseEvent> void sendMessageByRedisson(T message, String msgTag, long delayedTimes, TimeUnit timeUnit) {
RedissonEvent<T> redissonEvent = new RedissonEvent<>(message, msgTag);
log.info("往消息队列中添加Redission延迟消息:{}", message);
RBlockingQueue<RedissonEvent<T>> blockingQueue = redissonClient.getBlockingQueue(RedissonMessageListener.queue + ":" + msgTag);
RDelayedQueue<RedissonEvent<T>> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);
delayedQueue.offer(redissonEvent, delayedTimes, timeUnit);
}
}
消费者:
java
@Slf4j
public class RedissonMessageListener {
public static String queue = "dragon:jjb-saas-common:redis:queue";
private final RedissonClient redissonClient;
public RedissonMessageListener(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
public void init(String tag) {
RBlockingQueue<RedissonEvent<?>> blockingQueue = redissonClient.getBlockingQueue(RedissonMessageListener.queue + ":" + tag);
RDelayedQueue<RedissonEvent<?>> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);
ThreadUtil.execAsync(() -> {
while (true) {
try {
RedissonEvent<?> redissonEvent = blockingQueue.poll();
if (null != redissonEvent) {
onMessage(redissonEvent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public void onMessage(RedissonEvent redissonEvent) {
assert redissonEvent != null;
ConsumerType msgProcessorType = ConsumerCache.getConsumer(redissonEvent.getTag());
if (msgProcessorType == null) {
log.warn("No message processor present for tag: " + redissonEvent.getTag());
return;
}
BaseEvent baseMessage = redissonEvent.getData();
TraceUtils.putSid(baseMessage.getSid());
TraceUtils.putTid(baseMessage.getTid());
AuthContext.set(baseMessage.getLoginUser());
MessageConsumer<BaseEvent> messageConsumer = ApplicationContextHelper.getBean(msgProcessorType.getConsumerClass());
try {
//判断消息是否需要幂等消费
if (baseMessage.isIdempotentEnabled()) {
//计算key
String key = DigestUtils.md5Hex(redissonEvent.getTag() + "#" + baseMessage.getMsgSerialNum()
+ "#" + baseMessage + "#" + messageConsumer.getClass().getName());
Boolean consumed = ApplicationContextHelper.getBean(RedisService.class).setNx(key, 1, 24, TimeUnit.HOURS);
//判断消息是否已经被当前服务消费过,如果消费过,则忽略当前消息
if (Boolean.FALSE.equals(consumed)) {
log.warn("repeat consume message:{}", baseMessage);
return;
}
try {
messageConsumer.consume(baseMessage);
} catch (Throwable e) {
//执行错误,删除key,下次可以重复消费
ApplicationContextHelper.getBean(RedisService.class).delete(key);
throw e;
}
} else {
messageConsumer.consume(baseMessage);
}
} catch (Throwable e) {
throw e;
} finally {
TraceUtils.removeTid();
TraceUtils.removeSid();
AuthContext.remove();
}
}
}
代码有问题,请留言