策略模式实现rocketmq的消息处理器解耦

策略模式实现rocketmq的消息处理器解耦

一、效果

MyListener作为mq消息的唯一入口,只负责分发消息到对应的处理器,具体的业务逻辑由处理器实现。后续业务扩张,增加处理器即可,无需修改原来的代码。

二、核心类

2.0自定义注解RocketMqNotice

使用RocketMqNotice注解 标记 消息处理器类,传入topic 和 tag

package com.example.demo.rocketmq;

import java.lang.annotation.*;

/**
 * 自定义注解,标识 rocketmq的消息的处理器,处理指定的 topic tag
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface RocketMqNotice {
	String topic() default "";
	String tag() default "";
}

2.1消费者的绑定的 监听器 MyListener

(1)Listener初始化时,获取有自定义注解RocketMqNotice的bean(标识消息处理器)。
并且根据topic和tag归类。
(2)Listener消费消息时,根据topic和tag获取处理器。
只负责把消息转发到对应的处理器,具体的业务逻辑由处理器实现。
达到解耦的目的,支持一个消息转发到多个处理器。

package com.example.demo.rocketmq.consumer;

import com.example.demo.rocketmq.RocketMqNotice;
import com.example.demo.rocketmq.HandlerInterface;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class MyListener implements ApplicationContextAware, MessageListenerOrderly {

    private ApplicationContext applicationContext;

    private final Map<String, List<Object>> handlerMap = new ConcurrentHashMap<>();

    @Override
    public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
        consumeOrderlyContext.setAutoCommit(true);
        for (MessageExt msg : list) {
            handleNotification(msg);
        }
        return ConsumeOrderlyStatus.SUCCESS;
    }

    /**
     * =================核心代码=====================
     * 根据消息的     topic - tag
     * 从map中获取   处理器列表
     * 转发消息到    每个处理器
     */
    private void handleNotification(Message message) {
        String topic = message.getTopic();
        String tag = message.getTags();
        String key = topic + "-" + tag;
        if (CollectionUtils.isEmpty(handlerMap)) {
            initHandleMap();
        }
        List<Object> handlers = handlerMap.get(key);
        if (!CollectionUtils.isEmpty(handlers)) {
            for (Object handler : handlers) {
                ((HandlerInterface) handler).onNotification(message);
            }
        }
    }


    /**
     * 初始化消息处理器map
     * 

* 1.获取所有标记了自定义注解RocketMqNotice的处理器对象 * 2.判断是否是 处理器接口HandlerInterface的实现类 * 3.再次获取实现类的注解RocketMqNotice,校验 * 4.过滤掉注解的topic,tag为空的处理器对象 * 5.根据注解的 Topic-Tag 对处理器归类 */ private void initHandleMap() { Map<String, Object> handlers = applicationContext.getBeansWithAnnotation(RocketMqNotice.class); if (!CollectionUtils.isEmpty(handlers)) { for (Object handler : handlers.values()) { if (handler instanceof HandlerInterface) { RocketMqNotice consumerAnno = handler.getClass().getAnnotation(RocketMqNotice.class); if (consumerAnno == null) { continue; } String annoTopic = consumerAnno.topic(); String annoTag = consumerAnno.tag(); if (StringUtils.isEmpty(annoTopic) || StringUtils.isEmpty(annoTag)) { continue; } String key = annoTopic + "-" + annoTag; List<Object> handlerList = handlerMap.getOrDefault(key, new ArrayList<>()); handlerList.add(handler); handlerMap.put(key, handlerList); } } } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }

2.2 消息处理策略 接口类

package com.example.demo.rocketmq;

import org.apache.rocketmq.common.message.Message;

/**
 * MQ消息处理策略 接口类
 */
public interface HandlerInterface {
	/**
	 * 消息处理方法
	 */
	void onNotification(Message message);
}

2.3消息处理器 抽象类

消息处理器 抽象类实现 消息处理策略接口。

package com.example.demo.rocketmq;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.Message;

/**
 * mq消息处理器 抽象类
 */
@Slf4j
public abstract class AbstractHandler implements HandlerInterface {

    @Override
    public void onNotification(Message message) {
        try {
            handlerMessage(message);
        } catch (Exception e) {
            log.error("推送失败,", e);
        }
    }

    public void handlerMessage(Message message) {

    }

}

2.5处理器实现类1和2

    继承处理器抽象类
package com.example.demo.rocketmq.handler;

import com.example.demo.rocketmq.AbstractHandler;
import com.example.demo.rocketmq.RocketMqNotice;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.Message;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;

@Component
@Slf4j
@RocketMqNotice(topic = "TOPIC1", tag = "TAG1")
public class MyHandler1 extends AbstractHandler {
    @Override
    public void handlerMessage(Message message) {
        log.info("处理器1:接收的消息为topic:{},tag:{},content:{}",
                message.getTopic(), message.getTags(), new String(message.getBody(), StandardCharsets.UTF_8));
    }

}

package com.example.demo.rocketmq.handler;

import com.example.demo.rocketmq.AbstractHandler;
import com.example.demo.rocketmq.RocketMqNotice;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.Message;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;

@Component
@Slf4j
@RocketMqNotice(topic = "TOPIC2", tag = "TAG2")
public class MyHandler2 extends AbstractHandler {
    @Override
    public void handlerMessage(Message message) {
        log.info("处理器2:接收的消息为topic:{},tag:{},content:{}",
                message.getTopic(), message.getTags(), new String(message.getBody(), StandardCharsets.UTF_8));
    }

}

2.6其他代码

消费者
package com.example.demo.rocketmq.test;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
@ConfigurationProperties(prefix = "rocketmq.producer")
@Data
public class MQProducerProperties {
    private String namesrvAddr = "127.0.0.1:9876";
    private String producerGroup = "producerGroup";
}

package com.example.demo.rocketmq.consumer;


import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Map;

@Slf4j
@Component
public class MyConsumer {

    @Resource
    private MQConsumerProperties properties;

    @Resource
    private MyListener myListener;

    /**
     * 初始化消费者信息
     */
    @PostConstruct
    public void init() throws MQClientException {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(properties.getConsumerGroup());
        consumer.setNamesrvAddr(properties.getNamesrvAddr());
        consumer.setInstanceName(String.valueOf(System.currentTimeMillis()));
        Map<String, String> topicAndTag = properties.getTopicAndTag();
        if (CollectionUtils.isEmpty(topicAndTag)) {
            consumer.subscribe(properties.getDefaultTopic(), "*");
        } else {
            for (Map.Entry<String, String> entry : topicAndTag.entrySet()) {
                String topic = entry.getKey();
                String tag = entry.getValue();
                consumer.subscribe(topic, tag);
            }
        }
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
        String msgMode = properties.getMsgModel();
        MessageModel messageModel = "broadcast".equalsIgnoreCase(msgMode) ? MessageModel.BROADCASTING : MessageModel.CLUSTERING;
        consumer.setMessageModel(messageModel);
        consumer.registerMessageListener(myListener);
        consumer.start();
        log.info("Consumer OK to start with topicAndTag[" + topicAndTag.toString() + "]");
    }
}
生产者
package com.example.demo.rocketmq.test;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
@ConfigurationProperties(prefix = "rocketmq.producer")
@Data
public class MQProducerProperties {
    private String namesrvAddr = "127.0.0.1:9876";
    private String producerGroup = "producerGroup";
}

package com.example.demo.rocketmq.test;


import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.common.message.Message;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

@Slf4j
@Component
public class MyProducer {

    @Resource
    MQProducerProperties properties;

    @Getter
    private DefaultMQProducer producer;

    /**
     * Spring bean init-method
     */
    @PostConstruct
    public void init() throws MQClientException {
        producer = new DefaultMQProducer(properties.getProducerGroup());
        producer.setNamesrvAddr(properties.getNamesrvAddr());
        producer.setInstanceName(String.valueOf(System.currentTimeMillis()));
        producer.setDefaultTopicQueueNums(1);
        producer.start();
        log.info("Producer OK to start");
    }

    /**
     * Spring bean destroy-method
     */
    public void destroy() {
        producer.shutdown();
    }

    /**
     * 发送消息
     */
    public boolean sendMessage(Message msg) {
        SendResult sendResult = null;
        try {
            sendResult = producer.send(msg);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        if (sendResult == null || sendResult.getSendStatus() != SendStatus.SEND_OK) {
            log.error("Failed to send  message,SendStatus is not OK");
            return false;
        }
        log.info("MsgId:" + sendResult.getMsgId());
        return true;
    }
}

nacos的配置
rocketmq:
  producer:
    namesrvAddr: 127.0.0.1:9876
    producerGroup: producerGroup-1712
  consumer:
    namesrvAddr: 127.0.0.1:9876
    consumerGroup: consumerGroup-1712
    msgModel: CLUSTERING
    topicAndTag:
      TOPIC1: TAG1
      TOPIC2: TAG2
      TOPIC3: TAG3

你可能感兴趣的:(rocketmq,策略模式,rocketmq)