明确需求
我现在要解决的是mq在消费过程中消息乱序处理问题。
简单介绍一下上图。消息通过消息解析器解析出其自带的报文序号。如果该报文序号和解析器中的前置报文序号相差等于1(消息正常)更新前置报文。并将消息的functionID得到其具体的适配处理器,得到具体的handler,进行处理。序号小于0时表示此时发生消息重复消费。其它就是消息乱序。乱序的消息加入延迟队列并按照消息的序号进行排序。并使用定时器定时的取出到消息解析器中校验。
代码实现
消息体
package com.fire.plan.mq;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* @author zwd
* @date 2021/8/28
* @email zwd@hhh.com
*/
public class MessageContent implements Delayed {
Integer sourceId;//不同下流的消息ID
Integer businessId;//业务ID
String msg;//待发送的message
String functionId;//具体需要处理的方法
long createTimeMillis;//消息的创建时间
int count; //加入队列的次数
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public long getCreateTimeMillis() {
return createTimeMillis;
}
public void setCreateTimeMillis(long createTimeMillis) {
this.createTimeMillis = createTimeMillis;
}
public static Builder builder(){
return new Builder();
}
@Override
public long getDelay(TimeUnit unit) {
return 0;
}
@Override
public int compareTo(Delayed o) {
MessageContent messageContent = (MessageContent) o;
if (this.businessId > messageContent.businessId) {
return 1;
}else if (this.businessId < messageContent.businessId){
return -1;
}else{
return 0;
}
}
static class Builder{
private Integer sourceId;//不同下流的消息ID
private Integer businessId;//业务ID
private String msg;//待发送的message
private String functionId;
public Builder() {
}
public Builder sourceId(Integer sourceId) {
this.sourceId = sourceId;
return this;
}
public Builder businessId(Integer businessId) {
this.businessId = businessId;
return this;
}
public Builder msg(String msg) {
this.msg = msg;
return this;
}
public Builder functionId(String functionId) {
this.functionId = functionId;
return this;
}
public MessageContent build(){
MessageContent messageContent = new MessageContent();
messageContent.sourceId = this.sourceId;
messageContent.businessId = this.businessId;
messageContent.msg = this.msg;
messageContent.functionId = this.functionId;
return messageContent;
}
}
}
消息处理器
package com.fire.plan.mq;
/**
* @author zwd
* @date 2021/8/28
* @email zwd@hhh.com
*/
public interface MessageHandler {
void processMsg(MessageContent messageContent);
}
/**
* @author zwd
* @date 2021/8/28
* @email zwd@hhh.com
*/
public class MessageHandlerOne implements MessageHandler{
@Override
public void processMsg(MessageContent messageContent) {
System.out.println(messageContent.businessId + " is being process");
}
}
/**
* @author zwd
* @date 2021/8/28
* @email zwd@hhh.com
*/
public class MessageHandlerTwo implements MessageHandler{
@Override
public void processMsg(MessageContent messageContent) {
System.out.println(messageContent.businessId + " is being process");
}
}
/**
* @author zwd
* @date 2021/8/28
* @email zwd@hhh.com
*/
public class MessageHandlerThree implements MessageHandler{
@Override
public void processMsg(MessageContent messageContent) {
System.out.println(messageContent.businessId + " is being process");
}
}
消息适配器
/**
* @author zwd
* @date 2021/8/28
* @email zwd@hhh.com
*/
public interface MessageAdaptive {
MessageHandler adapter(MessageContent messageContent);
}
package com.fire.plan.mq;
import java.util.HashMap;
/**
* @author zwd
* @date 2021/8/28
* @email zwd@hhh.com
*/
public class MessageAdaptiveMq implements MessageAdaptive{
private HashMap<String, MessageHandler> handlerMapping = new HashMap<>();
public void registry(String functionId, MessageHandler messageHandler){
handlerMapping.put(functionId, messageHandler);
}
@Override
public MessageHandler adapter(MessageContent messageContent) {
return handlerMapping.get(messageContent.functionId);
}
}
消息解析器
package com.fire.plan.mq;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.DelayQueue;
/**
* @author zwd
* @date 2021/8/28
* @email zwd@hhh.com
*/
public class MessageSolver {
private static volatile MessageSolver messageSolver = null;
Integer preBusinessId;
DelayQueue<MessageContent> delayed;
MessageAdaptive messageAdaptive;
long timeout = 6;
private MessageSolver(){
this.delayed = new DelayQueue<>();
preBusinessId = 0;
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
if (!delayed.isEmpty()) {
MessageContent messageContent = delayed.poll();
long currentTimeMillis = System.currentTimeMillis() / 1000;
long createTimeMillis = messageContent.createTimeMillis;
if (currentTimeMillis - createTimeMillis > timeout) {
//将消息存储到db
System.out.println(messageContent.businessId + " will be store to db");
} else {
//重新检查前置报文
messageSolver.messageSolver(messageContent);
}
}
}
};
Timer timer = new Timer();
//500ms检查一次前置报文
timer.schedule(timerTask, 0 ,500);
}
public static MessageSolver getMessageSolver(){
if (messageSolver == null) {
synchronized (MessageSolver.class) {
if (messageSolver == null) {
messageSolver = new MessageSolver();
}
}
}
return messageSolver;
}
public void messageSolver(MessageContent messageContent){
int flag = messageContent.businessId - messageSolver.preBusinessId;
if (flag == 1) {
MessageHandler adapter = messageAdaptive.adapter(messageContent);
adapter.processMsg(messageContent);
//更新前置报文编号
this.preBusinessId = messageContent.businessId;
}else if (flag <= 0){
System.out.println("this message has been consumed");
}else {
if (messageContent.getCount() == 0) messageContent.setCreateTimeMillis(System.currentTimeMillis() / 1000);
messageContent.setCount(messageContent.getCount() + 1);
delayed.add(messageContent);
}
}
public long getTimeout() {
return timeout;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
public MessageAdaptive getMessageAdaptive() {
return messageAdaptive;
}
public void setMessageAdaptive(MessageAdaptive messageAdaptive) {
this.messageAdaptive = messageAdaptive;
}
}
测试
package com.fire.plan.mq;
/**
* @author zwd
* @date 2021/8/28
* @email zwd@hhh.com
*/
public class Main {
public static void main(String[] args) throws InterruptedException {
MessageContent message1 = MessageContent.builder()
.sourceId(1).businessId(1).msg("first message").functionId("function01").build();
MessageContent message2 = MessageContent.builder()
.sourceId(1).businessId(2).msg("second message").functionId("function02").build();
MessageContent message3 = MessageContent.builder()
.sourceId(1).businessId(3).msg("third message").functionId("function03").build();
MessageAdaptiveMq messageAdaptiveMq = new MessageAdaptiveMq();
messageAdaptiveMq.registry("function01",new MessageHandlerOne());
messageAdaptiveMq.registry("function02",new MessageHandlerTwo());
messageAdaptiveMq.registry("function03",new MessageHandlerThree());
MessageSolver messageSolver = MessageSolver.getMessageSolver();
messageSolver.setMessageAdaptive(messageAdaptiveMq);
messageSolver.messageSolver(message3);
messageSolver.messageSolver(message1);
messageSolver.messageSolver(message2);
}
}
输出:
1 is being process
2 is being process
3 is being process