如果項目使用Spring boot集成Rocket MQ可以使用RocketMQTemplate,代碼看起來很簡潔,但是如果項目只使用了spring,就要自己手寫一堆代碼去實現消息發送與消息監聽
下面就參考RocketMQTemplate自己實現一個基於註解的消息監聽框架
先看一下RocketMQTemplate是怎麼實現註解方式註冊消息監聽的
@RocketMQMessageListener(
topic = "test_topic", //topic:和消費者發送的topic相同
consumerGroup = "test_my-consumer", //group:不用和生產者group相同
selectorExpression = "*") //tag
@Component //必須注入spring容器
//泛型必須和接收的消息類型相同
public class TestListner implements RocketMQListener<User> {
@Override
public void onMessage(User user) {
System.out.println(user);
}
}
接下來我們就自己實現@RocketMQMessageListener,我們爲了區別就將註解命名爲@RocketMqMsgListener
思路:
1.定義@RocketMqMsgListener
2.項目啓動後掃描所有@RocketMqMsgListener註解的類
3.根據註解中的參數自動創建消費者
4.服務停止時自動停止消費者
1.定義@RocketMqMsgListener
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RocketMqMsgListener {
/**
* 訂閱主題
* @return
*/
String topic();
/**
* 消費者組
* @return
*/
String consumerGroup();
/**
* tag,訂閱子查詢條件,默認獲取所有
* @return
*/
String selectorExpression() default "*";
/**
* 查詢類型 SelectorType.TAG根據tag查詢 SelectorType.SQL92根據sql查詢
* @return
*/
SelectorType selectorType() default SelectorType.TAG;
/**
* 控制消息模式,如果希望所有訂閱者都接收消息全部消息,廣播是一個不錯的選擇。如果希望一個消費者接收則使用負載均衡模式
* @return
*/
MessageModel messageModel() default MessageModel.CLUSTERING;
/**
* 實例名稱
* @return
*/
String instanceName() default "";
}
2.項目啓動後掃描所有@RocketMqMsgListener註解的類
3.根據註解中的參數自動創建消費者
使用spring自帶掃描,類實現ApplicationListener<ContextRefreshedEvent>會在容器啓動後自動執行其中的onApplicationEvent(ContextRefreshedEvent event)方法
public class MqMsgLoadListener implements ApplicationListener<ContextRefreshedEvent>, DisposableBean {
//掃描的類
private Map<String, Object> beanMap = new HashMap<>();
//消費者列表
private List<SimpleConsumer> consumerList = new ArrayList<>();
@Autowired
private RocketConfig config;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
//如果是第二次加載則不處理了
if(event.getApplicationContext().getParent() != null) {
return;
}
//獲取註解類
beanMap = event.getApplicationContext().getBeansWithAnnotation(RocketMqMsgListener.class);
System.out.println("================"+beanMap.size());
//未掃描到不處理
if(beanMap == null || beanMap.size() == 0) {
return;
}
for(Object bean : beanMap.values()) {
createConsumer(bean);
}
}
/**
* 創建消費者
* @param bean
*/
private void createConsumer(Object bean) {
if(!(bean instanceof MessageListenerConcurrently)) {
return;
}
//獲取註解
RocketMqMsgListener mqMsgListener = bean.getClass().getAnnotation(RocketMqMsgListener.class);
//配置
ConsumerConfig consumerConfig = new ConsumerConfig();
consumerConfig.setConsumeFromWhere(mqMsgListener.consumeFromWhere());
consumerConfig.setTopic(mqMsgListener.topic());
consumerConfig.setConsumerGroup(mqMsgListener.consumerGroup());
consumerConfig.setSelectorExpression(mqMsgListener.selectorExpression());
consumerConfig.setSelectorType(mqMsgListener.selectorType());
consumerConfig.setMessageModel(mqMsgListener.messageModel());
consumerConfig.setConsumeMode(mqMsgListener.consumeMode());
if(!StringUtils.isEmpty(mqMsgListener.instanceName())) {
consumerConfig.setInstanceName(mqMsgListener.instanceName());
}
//創建消費者
SimpleConsumer consumer = new SimpleConsumer(config, consumerConfig, (MessageListenerConcurrently)bean);
consumerList.add(consumer);
//初始化並啓動
try {
consumer.init();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 服務停止
*/
@Override
public void destroy() throws Exception {
for(SimpleConsumer consumer : consumerList) {
if(consumer == null) {
continue;
}
consumer.destroy();
}
}
}
xml中配置
<!-- 啓動監聽 -->
<bean id="mqMsgLoadListener" class="com.tes.rocket.annotation.listener.MqMsgLoadListener" />
4.服務停止時自動停止消費者
實現DisposableBean接口,服務停止時會自動調用destroy()方法,在destroy()釋放消費者資源
/**
* 服務停止
*/
@Override
public void destroy() throws Exception {
for(SimpleConsumer consumer : consumerList) {
if(consumer == null) {
continue;
}
consumer.destroy();
}
}
使用註解
@Component
@RocketMqMsgListener(topic = "test_topic", consumerGroup = "rocketmq-test")
public class StringMessageListener implements MessageListenerConcurrently {
private int total;
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
setTotal();
for (MessageExt msg : msgs) {
System.out.println("========收到消息:"+new String(msg.getBody())+"---------總數:"+total);
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
public synchronized void setTotal() {
total++;
}
public int getTotal(){
return total;
}
}
完整代碼
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">
<!-- rocket mq配置 -->
<bean id="rocketMQConfig" class="com.test.config.RocketConfig">
<constructor-arg name="address" value="192.168.1.113:9876"/>
</bean>
<!-- 默認生產者 -->
<bean id="producer" class="com.test.rocket.producer.SimpleProducer" init-method="init" destroy-method="destroy">
<constructor-arg name="producerGroup" value="rocketmq-test" />
<constructor-arg name="config" ref="rocketMQConfig"/>
</bean>
<!-- 掃描包 -->
<context:component-scan base-package="com.test.**.mq.**" />
<!-- 啓動監聽 -->
<bean id="mqMsgLoadListener" class="com.test.rocket.annotation.listener.MqMsgLoadListener" />
</beans>
RocketMqMsgListener
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RocketMqMsgListener {
/**
* 訂閱主題
* @return
*/
String topic();
/**
* 消費者組
* @return
*/
String consumerGroup();
/**
* tag,訂閱子查詢條件,默認獲取所有
* @return
*/
String selectorExpression() default "*";
/**
* 查詢類型 SelectorType.TAG根據tag查詢 SelectorType.SQL92根據sql查詢
* @return
*/
SelectorType selectorType() default SelectorType.TAG;
/**
* 控制消息模式,如果希望所有訂閱者都接收消息全部消息,廣播是一個不錯的選擇。如果希望一個消費者接收則使用負載均衡模式
* @return
*/
MessageModel messageModel() default MessageModel.CLUSTERING;
/**
* 實例名稱
* @return
*/
String instanceName() default "";
}
RocketConfig
public class RocketConfig implements Serializable {
private static final long serialVersionUID = 6807425160034094787L;
public RocketConfig() {
}
/**
* 創建配置對象
* @param address 服務地址,多個用;分割,例如"192.168.25.135:9876;192.168.25.138:9876"
*/
public RocketConfig(String address) {
this.address = address;
}
//地址
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
ConsumerConfig
public class ConsumerConfig implements Serializable {
private static final long serialVersionUID = 10022583900545392332L;
/**
* 訂閱主題
* @return
*/
private String topic;
/**
* 消費者組
* @return
*/
private String consumerGroup;
/**
* 默認是tag,訂閱子查詢條件,默認獲取所有
* 如果selectorType爲sql,則值爲sql
* @return
*/
private String selectorExpression = "*";
/**
* 查詢類型 SelectorType.TAG根據tag查詢 SelectorType.SQL92根據sql查詢
* @return
*/
private SelectorType selectorType;
/**
* 控制消息模式,如果希望所有訂閱者都接收消息全部消息,廣播是一個不錯的選擇。如果希望一個消費者接收則使用負載均衡模式
* Control message mode, if you want all subscribers receive message all message, broadcasting is a good choice.
* @return
*/
private MessageModel messageModel;
/**
* 實例名稱
* @return
*/
private String instanceName;
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public String getConsumerGroup() {
return consumerGroup;
}
public void setConsumerGroup(String consumerGroup) {
this.consumerGroup = consumerGroup;
}
public String getSelectorExpression() {
return selectorExpression;
}
public void setSelectorExpression(String selectorExpression) {
this.selectorExpression = selectorExpression;
}
public SelectorType getSelectorType() {
return selectorType;
}
public void setSelectorType(SelectorType selectorType) {
this.selectorType = selectorType;
}
public MessageModel getMessageModel() {
return messageModel;
}
public void setConsumeMode(ConsumeMode consumeMode) {
this.consumeMode = consumeMode;
}
public String getInstanceName() {
return instanceName;
}
public void setInstanceName(String instanceName) {
this.instanceName = instanceName;
}
}
SelectorType
public enum SelectorType {
/**
* @see 根據tag查詢
*/
TAG,
/**
* @see 根據sql查詢
*/
SQL92
}
SimpleProducer
public class SimpleProducer {
private String producerGroup;
private RocketConfig config;
private String instanceName;
private DefaultMQProducer producer;
public DefaultMQProducer getProducer() {
return producer;
}
public SimpleProducer(RocketConfig config, String producerGroup){
this.producerGroup = producerGroup;
this.config = config;
}
public void init() throws MQClientException{
producer = new DefaultMQProducer(producerGroup);
producer.setNamesrvAddr(config.getAddress());
if(instanceName != null) {
producer.setInstanceName(instanceName);
}
producer.start();
}
public void destroy(){
producer.shutdown();
}
public String getInstanceName() {
return instanceName;
}
public void setInstanceName(String instanceName) {
this.instanceName = instanceName;
}
}
SimpleConsumer
public class SimpleConsumer {
private DefaultMQPushConsumer consumer;
//rocketMq配置
private RocketConfig config;
//消費者配置
private ConsumerConfig consumerConfig;
//消息監聽器
private MessageListenerConcurrently messageListener;
public SimpleConsumer(RocketConfig config, ConsumerConfig consumerConfig, MessageListenerConcurrently messageListener){
this.messageListener = messageListener;
this.config = config;
this.consumerConfig = consumerConfig;
}
public void init() throws Exception{
consumer = new DefaultMQPushConsumer(consumerConfig.getConsumerGroup());
//服務地址
consumer.setNamesrvAddr(config.getAddress());
//實例名稱
if(consumerConfig.getInstanceName() != null) {
consumer.setInstanceName(consumerConfig.getInstanceName());
}
//訂閱主題
if(consumerConfig.getSelectorType() == SelectorType.TAG) {//如果是根據tag過濾消息
consumer.subscribe(consumerConfig.getTopic(), consumerConfig.getSelectorExpression());
}else if(consumerConfig.getSelectorType() == SelectorType.SQL92) {//如果是根據sql過濾消息
consumer.subscribe(consumerConfig.getTopic(), MessageSelector.bySql(consumerConfig.getSelectorExpression()));
}
//消息模式
consumer.setMessageModel(consumerConfig.getMessageModel());
//監聽器
consumer.registerMessageListener(messageListener);
consumer.start();
}
public void destroy(){
consumer.shutdown();
}
public DefaultMQPushConsumer getConsumer() {
return consumer;
}
}
MqMsgLoadListener
public class MqMsgLoadListener implements ApplicationListener<ContextRefreshedEvent>, DisposableBean {
//掃描的類
private Map<String, Object> beanMap = new HashMap<>();
//消費者列表
private List<SimpleConsumer> consumerList = new ArrayList<>();
@Autowired
private RocketConfig config;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
//如果是第二次加載則不處理了
if(event.getApplicationContext().getParent() != null) {
return;
}
//獲取註解類
beanMap = event.getApplicationContext().getBeansWithAnnotation(RocketMqMsgListener.class);
System.out.println("================"+beanMap.size());
//未掃描到不處理
if(beanMap == null || beanMap.size() == 0) {
return;
}
for(Object bean : beanMap.values()) {
createConsumer(bean);
}
}
/**
* 創建消費者
* @param bean
*/
private void createConsumer(Object bean) {
if(!(bean instanceof MessageListenerConcurrently)) {
return;
}
//獲取註解
RocketMqMsgListener mqMsgListener = bean.getClass().getAnnotation(RocketMqMsgListener.class);
//配置
ConsumerConfig consumerConfig = new ConsumerConfig();
consumerConfig.setTopic(mqMsgListener.topic());
consumerConfig.setConsumerGroup(mqMsgListener.consumerGroup());
consumerConfig.setSelectorExpression(mqMsgListener.selectorExpression());
consumerConfig.setSelectorType(mqMsgListener.selectorType());
consumerConfig.setMessageModel(mqMsgListener.messageModel());
if(!StringUtils.isEmpty(mqMsgListener.instanceName())) {
consumerConfig.setInstanceName(mqMsgListener.instanceName());
}
//創建消費者
SimpleConsumer consumer = new SimpleConsumer(config, consumerConfig, (MessageListenerConcurrently)bean);
consumerList.add(consumer);
//初始化並啓動
try {
consumer.init();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 服務停止
*/
@Override
public void destroy() throws Exception {
for(SimpleConsumer consumer : consumerList) {
if(consumer == null) {
continue;
}
consumer.destroy();
}
}
}