环境准备:
RocketMQ 服务
腾讯TDMQ-RocketMQ版 实在是懒得搭RocketMQ服务, 最近发现腾讯云TDMQ在内测,不收费,所以想拿来玩玩试试,于是就有了今天的这篇文章来给大家分享, 请各位多多指教, 那么腾讯TDMQ-RocketMQ的一些配置就不多做介绍了,请自行参考官网配置方法
引入Pom依赖
接下来引入RocketMQ的依赖包:本次使用的版本为2.2.2
其实依赖中有监听注解,但用了一下,感觉不是很顺手,所以才自己写了一套
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
yml配置
编写yml配置
rocketmq:
name-server: rocketMqRemoteAddress
consumer:
access-key: 访问密钥
secret-key: 加密密钥
namespace: rocketmq命名空间
如果你同我一样使用了腾讯云的RocketMQ,name以上几个配置参数我会一一解释 rocketmq.name-server 在使用时要注意, 如果你有RocketMQ示例对应地区的服务器,请使用VPC网络接入, 如果没有请联系客服为你开通公网访问.
| 配置参数 | 取自 | |
|---|---|---|
| rocketmq.name-server | 集群接入地址,在控制台集群管理页面的集群列表操作栏的接入地址处获取。 | |
| rocketmq.consumer.access-key | 角色密钥,在 角色管理 页面复制密钥列复 | |
| rocketmq.consumer.secret-key | 头像-> 访问管理 -> 访问密钥中复制 | |
| rocketmq.consumer.namespace | 命名空间的名称,在控制台命名空间页面复制,格式是**集群 ID + | + 命名空间** |
定义枚举
该枚举会在后面使用
package com.***.***.enums;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
/**
* Mq枚举
*
* @author evans
*/
@Getter
@AllArgsConstructor
public enum MQEnum {
/**
* Mq
*/
DEMO(1000, "描述", "topic", "group", "tag");
private final Integer code;
private final String describe;
private final String topic;
private final String groupId;
private final String tag;
}
定义注解:
自定义注解(RocketMQConsumerListener)
以下是我个人的自定义注解, 你们可以随便哈, 此处的MqEnum即为上方的MQEnum
package com.***.***.annotation;
import com.catnet.common.enums.MQEnum;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
/**
* @author evans
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RocketMQConsumerListener {
/**
* Mq枚举
*{@link MQEnum}
* @return mqEnum
*/
MQEnum mqEnum();
}
注解扫描:
编写处理类代码
对@RocketMQConsumerListener修饰的类进行注册处理,这里我们选择实现BeanPostProcessor接口进行项目启动开始注入bean时初始化MQ消费者, 感兴趣的可以自行百度BeanPostProcessor的用法
package com.***.message.config;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.catnet.common.annotation.RocketMQConsumerListener;
import com.catnet.common.enums.MQEnum;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.consumer.DefaultLitePullConsumer;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.spring.autoconfigure.RocketMQProperties;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import java.util.Arrays;
import java.util.List;
/**
* {@link com.catnet.common.annotation.RocketMQConsumerListener} 处理类
*
* @author yangbin
*/
@Slf4j
@Configuration
@AutoConfigureAfter(RocketMQProperties.class)
public class ProcessMqConsumerHandler implements BeanPostProcessor {
@Autowired
private RocketMQProperties mqProperties;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@SneakyThrows
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
RocketMQConsumerListener annotation = AnnotationUtils.findAnnotation(bean.getClass(), RocketMQConsumerListener.class);
if (ObjectUtil.isNull(annotation)) {
return bean;
}
// 开始创建消费者
List<Class<?>> interfaces = Arrays.asList(bean.getClass().getInterfaces());
if (!CollUtil.contains(interfaces, MessageListenerConcurrently.class)) {
log.error("[{}] >>> 未实现 {} 的<onMessage>方法", bean.getClass().getName(), MessageListenerConcurrently.class.getName());
return bean;
}
MQEnum mqEnum = annotation.mqEnum();
if (ObjectUtil.isNull(mqEnum)) {
log.error("[{}] >>> 未填写对应监听MQ枚举", bean.getClass().getName());
return bean;
}
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(mqProperties.getConsumer().getNamespace(),
mqEnum.getGroupId(), new AclClientRPCHook(new SessionCredentials(mqProperties.getConsumer().getAccessKey(), mqProperties.getConsumer().getSecretKey())));
consumer.setNamesrvAddr(mqProperties.getNameServer());
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe(mqEnum.getTopic(), mqEnum.getTag());
consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
// 消息处理逻辑
MessageListenerConcurrently consumerBean = (MessageListenerConcurrently) bean;
return consumerBean.consumeMessage(msgs, context);
});
try {
consumer.start();
log.info("[MQ-Consumer:{}] >>> 已启动 -- 监听Topic:{} - GID:{} - Tag:{}", bean.getClass().getName(), mqEnum.getTopic(), mqEnum.getGroupId(), mqEnum.getTag());
} catch (Exception ex) {
log.error("[MQ-Consumer:{}] >>> 启动失败", bean.getClass().getName());
}
return bean;
}
}
注意事项
使用@RocketMQConsumerListener 修饰的消费者需要实现 MessageListenerConcurrently 接口的 consumeMessage 方法, 然后在此方法中进行消费消息的业务逻辑处理
启动项目:
观察日志
可以看到, 我在初始化消费者时打印的日志, 标识消费者启动成功并成功监听对应Topic
查看TDMQ后台
然后登录TDMQ控制台查看Group消费者详情,可以看到已经连接了
接下来就可以使用消息生产者向对应的tag发送消息啦