配置Consumers
package .config;
import .message.listener.MyMessageListener0;
import .message.listener.MyMessageListener1;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.exception.MQClientException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Jay
*/
@Configuration
public class RocketMQConsumerConfig {
@Bean(name = "consumer0", initMethod = "start", destroyMethod = "shutdown")
public DefaultMQPushConsumer consumer0(MessageListenerConcurrently myMessageListener0) throws MQClientException {
// 初始化consumer,并设置 consumer group name
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("DEFAULT_CONSUMER_GROUP0");
// 设置NameServer地址
consumer.setNamesrvAddr("localhost:9876");
//订阅一个或多个topic,并指定tag过滤条件,这里指定*表示接收所有tag的消息
consumer.subscribe("TEST_TOPIC0", "*");
//注册回调接口来处理从Broker中收到的消息
consumer.registerMessageListener(myMessageListener0);
return consumer;
}
@Bean(name = "consumer1", initMethod = "start", destroyMethod = "shutdown")
public DefaultMQPushConsumer consumer1(MessageListenerConcurrently myMessageListener1) throws MQClientException {
// 初始化consumer,并设置 consumer group name
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("DEFAULT_CONSUMER_GROUP1");
// 设置NameServer地址
consumer.setNamesrvAddr("localhost:9876");
//订阅一个或多个topic,并指定tag过滤条件,这里指定*表示接收所有tag的消息
consumer.subscribe("TEST_TOPIC1", "*");
//注册回调接口来处理从Broker中收到的消息
consumer.registerMessageListener(myMessageListener1);
return consumer;
}
}
声明注解 MessageHandlerAnn
package message.ann;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
/**
* @author Jay
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface MessageHandlerAnn {
/**
* 通过SpEL 表达式计算 #messageExt 的相关信息是否满足所有条件
*
* @return
*/
String []eLFilter() default "";
}
消息消费类MessageHandler
package message.handler;
import org.apache.rocketmq.common.message.MessageExt;
/**
* @author Jay
* @param <T> 消息 body 的数据类型,如果没有重写 convertMessage 方法, 则建议<T> 为 String
* 消费 RocketMQ 消息的帮助类
*/
public interface MessageHandler<T> extends InitializingBean {
default void afterPropertiesSet() {
if (this.getClass().getAnnotation(Qualifier.class) == null ||
this.getClass().getAnnotation(Component.class) == null) {
throw new RuntimeException("MessageHandler 实现类必须被 @Qualifier 和 @Component 注解修饰");
}
}
/**
* 是否要保留此消息
* @param messageExt messageExt
* @return TRUE 保留 FALSE 跳过
*/
default Boolean keepMessage(MessageExt messageExt){ return Boolean.TRUE;}
/**
* @param messageExt 消息
* @return 消息体已经转换过的信息
*/
default T getMessageBodyThenConvertType(MessageExt messageExt) {
return (T) new String(messageExt.getBody());
}
/**
* 具体处理消息的逻辑
*
* @param message messageExt.body
*/
void consumerMessage(T message);
}
消费实现类三个
package message.handler;
import message.ann.MessageHandlerAnn;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
/**
*
* @author Jay
*/
@Component
@Qualifier("TEST_TOPIC0")
@MessageHandlerAnn(eLFilter = {"#messageExt.topic == 'TEST_TOPIC0'", "#messageExt.tags == 'tag0'"})
public class Topic0Tag0MessageHandler implements MessageHandler<String> {
@Override
public void consumerMessage(String message) {
System.out.println("This is Topic0Tag0MessageHandler"+message);
}
}
package message.handler;
import message.ann.MessageHandlerAnn;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
/**
* @author Jay
*/
@Component
@Qualifier("TEST_TOPIC0")
@MessageHandlerAnn(eLFilter = {"#messageExt.topic == 'TEST_TOPIC0'", "#messageExt.tags == 'tag1'"})
public class Topic0Tag1MessageHandler implements MessageHandler<String> {
@Override
public void consumerMessage(String message) {
System.out.println("This is Topic0Tag1MessageHandler"+message);
}
}
package message.handler;
import message.ann.MessageHandlerAnn;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
/**
* @author Jay
*/
@Component
@Qualifier("TEST_TOPIC1")
@MessageHandlerAnn(eLFilter = {"#messageExt.topic == 'TEST_TOPIC1'", "#messageExt.tags == 'tag0'"})
public class Topic1Tag0MessageHandler implements MessageHandler<String> {
@Override
public void consumerMessage(String message) {
System.out.println("This is Topic1Tag0MessageHandler" + message);
}
}
消息监听者MessageListener
package message.listener;
import message.ann.MessageHandlerAnn;
import message.handler.MessageHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Optional;
/**
* @author Jay
*/
@Slf4j
public class MessageListener implements MessageListenerConcurrently,InitializingBean {
final private List<MessageHandler<?>> messageHandlerList;
public MyMessageListener(List<MessageHandler<?>> messageHandlerList) {
this.messageHandlerList = messageHandlerList;
}
@Override
public void afterPropertiesSet() {
// messageHandlerList 不能为空
if (ObjectUtils.isEmpty(this.messageHandlerList)) {
log.error("messageHandlerList is empty");
throw new RuntimeException("messageHandlerList is empty");
}
}
@SuppressWarnings("all")
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt messageExt : msgs) {
log.debug("received msg: {}", messageExt);
try {
long now = System.currentTimeMillis();
// 预设 EL 上下文
EvaluationContext cont = SimpleEvaluationContext.forReadWriteDataBinding()
.build();
cont.setVariable("messageExt", messageExt);
// 获取合适的 MessageHandler
Optional<MessageHandler<?>> messageHandlerOpt = filterListThenGetFirstHandler(this.messageHandlerList, cont, messageExt);
if (!messageHandlerOpt.isPresent()) {
// 需要检查 filterListThenGetFirstHandler 中两处 filter 处理后为什么会返回空.
log.error("没有合适的MessageHandler");
}
MessageHandler messageHandler = messageHandlerOpt.get();
// 消息体转换并消费消息
consumer(messageExt, messageHandler);
long costTime = System.currentTimeMillis() - now;
log.debug("consume {} cost: {} ms", messageExt.getMsgId(), costTime);
} catch (Exception e) {
log.warn("consume message failed. messageExt:{}", messageExt, e);
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
protected Optional<MessageHandler<?>> filterListThenGetFirstHandler(List<MessageHandler<?>> messageHandlerList,
final EvaluationContext cont, MessageExt messageExt) {
if (CollectionUtils.isEmpty(messageHandlerList)) {
log.error("messageHandlerList is empty");
return Optional.empty();
}
return messageHandlerList.stream()
/* 处理 MessageHandler关于自定义的过滤函数 */
.filter(messageHandler -> messageHandler.keepMessage(messageExt))
/* 处理 MessageHandlerAnn 的表达式 */
.filter(messageHandler -> {
MessageHandlerAnn messageHandlerAnn = messageHandler.getClass()
.getAnnotation(MessageHandlerAnn.class);
if (messageHandlerAnn == null) {
return Boolean.TRUE;
}
String[] elFilterArray = messageHandlerAnn
.eLFilter();
if (elFilterArray == null) {
return Boolean.TRUE;
}
for (String elFilter : elFilterArray) {
Boolean aBoolean = new SpelExpressionParser()
.parseExpression(elFilter)
.getValue(cont, Boolean.class);
if (Boolean.FALSE.equals(aBoolean)) {
return Boolean.FALSE;
}
}
// 通过SpEL 表达式计算 #messageExt 的相关信息是否满足所有条件
return Boolean.TRUE;
})
.findFirst();
}
protected void consumer(MessageExt messageExt, MessageHandler messageHandler) {
messageHandler.consumerMessage(messageHandler.getMessageBodyThenConvertType(messageExt));
}
}
工厂方法创建MyMessageListener 实例
// 放置在 RocketMQConsumerConfig 类中
@Bean("myMessageListener0")
public MessageListenerConcurrently myMessageListener0(@Qualifier("TEST_TOPIC0") List<MessageHandler<?>> messageHandlerList) {
return new MyMessageListener(messageHandlerList);
}
@Bean("myMessageListener1")
public MessageListenerConcurrently myMessageListener1(@Qualifier("TEST_TOPIC1") List<MessageHandler<?>> messageHandlerList) {
return new MyMessageListener(messageHandlerList);
}
实现记录![](https://img-blog.csdnimg.cn/direct/b8bb9afb78464b1da0d5a34fc6b59c7a.png)
![](https://img-blog.csdnimg.cn/direct/594a6f71a0b646cfbf72e4dc103e5951.png)
![](https://img-blog.csdnimg.cn/direct/db05595001e145c3bf9e6f8b5408de43.png)
思路来源
Fine-tuning Annotation-based Autowiring with Qualifiers :: Spring Framework