rocketmq Listener 还可以这样配置(基于SPEL\限定符号\自动连线)

文章介绍了如何在SpringBoot应用中配置ApacheRocketMQ的消费者,包括创建Consumer实例,设置NameServer,订阅Topic,并使用MessageListener和MessageHandler处理接收到的消息,同时涉及自定义过滤和SpEL表达式的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

配置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);
    }

 实现记录

思路来源

Fine-tuning Annotation-based Autowiring with Qualifiers :: Spring Framework

Using @Autowired :: Spring Framework

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值