A scalable implementation of message routing

I. Introduction

Recently, when implementing WeChat applets, it is necessary to process different messages pushed from the WeChat server. There are many types of messages, including subscription messages, graphic messages, and so on. After learning from some design patterns of great gods, a simple message routing model is implemented, which can meet multiple types of messages, multiple routing matching, and multiple message processing.

Two, the model

image.png

  • The message itself has some characteristic quantities, which can be used as the basis for matching message routing rules.

  • A message router contains multiple message routing rules.

  • A message routing rule includes interceptors, matchers, and multiple processors.

Three, the code

3.1 Message definition

Source code: https://segmentfault.com/a/1190000039689926

Message to be consumed

@Builder
@Getter
@ToString
public class Message {
    private String event;
    private String msgType;
    private String content;
    private String fromUser;
    private String toUser;
    private Instant createTime;
    //... as you wish
}

Consumer information to be object contains a number of Traitfields, including event, msgType, content. These fields can be used not only as the basis for matching routing rules, but also as conditions for processing specific messages. It can be expanded according to different business types.

Message processing result

@Builder
@ToString
public class OutMessage {
    private String msgType;
    private String fromUser;
    private String toUser;
    private Instant createTime;
}

Define the message processing result according to your specific business.

3.2 Message processor

Abstract type

public interface MessageHandler {
    OutMessage handle(Message message, Map<String,Object> context);
}

This is an abstract type of all message processors, and custom processors must implement it. Simply implement a message logging processor.

@Component
public class LogMessageHandler implements MessageHandler {
    @Override
    public OutMessage handle(Message message, Map<String, Object> context) {
        System.out.println(message.toString());
        // define your return value
        return null;
    }
}

3.2 Routing related

Message interceptor

public interface MessageInterceptor {
    OutMessage handle(Message message, Map<String,Object> context);
}

Interceptors can enhance the processing of messages. Implement this interface yourself.

Message matcher

public interface MessageRouterMatcher {
    boolean match(Message message);
}

The matcher can filter the message to realize the rule matching of the message.

router

public class MessageRouter {

    @Getter
    private final List<MessageRouterRule> rules = new ArrayList<>();

    public MessageRouterRule rule(){
        return new MessageRouterRule(this);
    }

    private OutMessage route(Message message,Map<String,Object> context){
        final List<MessageRouterRule> matchRules = new ArrayList<>();
        final Iterator<MessageRouterRule> iterator = this.rules.iterator();
        while (iterator.hasNext()){
            final MessageRouterRule rule = iterator.next();
            if (rule.test(message)){
                matchRules.add(rule);
            }
        }
        if(matchRules.size() == 0){
            return null;
        }else{
            final Iterator<MessageRouterRule> matchIterator = matchRules.iterator();
            while (matchIterator.hasNext()){
                final MessageRouterRule rule = matchIterator.next();
                //think think multi  OutMessage
                return rule.service(message, context);
            }
        }
        return null;
    }

    public OutMessage route(Message message){
        return this.route(message,new HashMap<>(2));
    }
}

Message routing rules

public class MessageRouterRule {

    //是否异步处理消息
    private boolean async;
    private String event;
    private String msgType;
    private String content;
    private String fromUser;
    private String toUser;

    /**
     * 路由器
     */
    private MessageRouter router;
    /**
     * 匹配器
     */
    private MessageRouterMatcher matcher;
    /**
     * 处理器
     */
    private List<MessageHandler> handlers = new ArrayList<>();
    /**
     * 拦截器 
     */
    private List<MessageInterceptor> interceptors = new ArrayList<>();


    public MessageRouterRule async(boolean async){
        this.async = async;
        return this;
    }

    public MessageRouterRule msgType(String msgType){
        this.msgType = msgType;
        return this;
    }

    public MessageRouterRule event(String event){
        this.event = event;
        return this;
    }

    public MessageRouterRule content(String content){
        this.content= content;
        return this;
    }

    public MessageRouterRule fromUser(String fromUser){
        this.fromUser= fromUser;
        return this;
    }

    public MessageRouterRule toUser(String toUser){
        this.toUser= toUser;
        return this;
    }

    public MessageRouterRule handler(MessageHandler handler,MessageHandler... otherHandlers){
        this.handlers.add(handler);
        if(otherHandlers != null && otherHandlers.length>0){
            Collections.addAll(this.handlers,otherHandlers);
        }
        return this;
    }

    public MessageRouterRule handle(MessageHandler handler){
        return this.handler(handler,(MessageHandler[]) null);
    }

    public MessageRouter end(){
        this.router.getRules().add(this);
        return this.router;
    }

    protected boolean test(Message message){
        //here can use matcher
        return (this.fromUser == null || this.fromUser.equals(message.getFromUser())) && (this.msgType == null || this.msgType.toLowerCase().equals(message.getMsgType() == null ? null : message.getMsgType().toLowerCase())) && (this.event == null || this.event.toLowerCase().equals(message.getEvent() == null ? null : message.getEvent().toLowerCase())) && (this.content == null || this.content.equals(message.getContent() == null ? null : message.getContent().trim())) && (this.matcher == null || this.matcher.match(message));
    }

    public MessageRouterRule(MessageRouter router){
        this.router = router;
    }

    protected OutMessage service(Message message, Map<String,Object> context){
        OutMessage outMessage = null;
        final Iterator<MessageHandler> iterator = handlers.iterator();
        while (iterator.hasNext()){
            final MessageHandler handler = iterator.next();
            if(null != handler){
                outMessage = handler.handle(message,context);
            }
        }
        return outMessage;
    }
}

The message router contains a variety of message routing rules.

Message routing rules enhance the processing of all messages through interceptors, and filter and match messages through matchers. Integrate multiple processors at the same time to complete the final processing of the message.

Four, test

This test uses a springframework.

First implement a configuration classbean

@Configuration
public class MessageRouterConfiguration {

    final LogMessageHandler logMessageHandler;

    public MessageRouterConfiguration(LogMessageHandler logMessageHandler) {
        this.logMessageHandler = logMessageHandler;
    }

    @Bean
    @ConditionalOnMissingBean(value = MessageRouter.class)
    public MessageRouter newRouter(){
        final MessageRouter messageRouter = new MessageRouter();
        //log print router rule
        messageRouter.rule().async(false).event("event").handle(logMessageHandler).end();
        //... add more
        return messageRouter;
    }
}

Message routing call

Route messages at the entrance of the application and get the result

final OutMessage outMessage = router.route(message);

Eventually, the log information will be printed on the terminal.

Five, summary

This model only implements a simple message routing model, and there are still many areas to be improved, such as

  • Handle the execution results of multiple message processors
  • Process messages asynchronously
  • Messages may be repeatedly consumed
  • ...

Wait for the next iteration to optimize! Welcome to leave a message to communicate.

Guess you like

Origin blog.csdn.net/qq_38082146/article/details/115091385