基于阿里云RocketMQ封装库的集成技术示例
整体架构
新建一个私有库,该库封装阿里云 RocketMQ 的三种协议接入方式,通过 Spring Boot 自动配置,根据 YAML 中的 protocol 字段选择不同的消费者客户端实现。
application.yml
│
▼
RocketMqProperties (@ConfigurationProperties)
│
├── producer: MqProperties → ONSFactory.createProducer()
│
└── consumer: Map<String, MqProperties>
│
▼ (根据 protocol 字段路由)
┌────────────┬────────────┬────────────┐
│ TCP │ HTTP │ SELF │
▼ ▼ ▼
ConsumerBean MQClient DefaultMqPush
(阿里云ONS) (阿里云HTTP) ConsumerWrapper
(开源RocketMQ)
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
配置结构
YAML 配置模板
yaml
rocketmq:
producer:
accessKey: xxx
secretKey: xxx
endpoint: http://{instanceId}.{region}.mq.aliyuncs.com:80
sendMsgTimeoutMillis: 3000 # 可选,默认 3000
consumer:
my-consumer-1: # 自定义消费者名称(key)
accessKey: xxx
secretKey: xxx
endpoint: http://{instanceId}.{region}.mq-internal.aliyuncs.com:8080
topic: TOPIC_NAME # 支持 | 分隔多个 topic
tag: my-tag
groupId: GID-xxx
protocol: tcp # tcp / http / self
instanceId: MQ_INST_xxx # 仅 HTTP 协议使用
msgType: NORMAL # 仅 SELF 协议使用(NORMAL / ORDERLY)
属性类 MqProperties
java
public class MqProperties {
private String endpoint;
private String accessKey;
private String secretKey;
private String topic;
private String tag;
private String groupId;
private String instanceId;
private String protocol;
private String msgType;
private String sendMsgTimeoutMillis;
}
属性绑定关系
java
@Configuration
@EnableConfigurationProperties(RocketMqProperties.class)
public class RocketMqConfig { ... }
@ConfigurationProperties(prefix = "rocketmq")
public class RocketMqProperties {
private MqProperties producer;
private Map<String, MqProperties> consumer;
}
三种协议模式对比
| 特性 | TCP | HTTP | SELF |
|---|---|---|---|
| 协议类型 | 阿里云 ONS TCP | 阿里云 HTTP | 开源 Apache RocketMQ |
| 客户端类 | ConsumerBean |
MQClient |
DefaultMqPushConsumerWrapper |
使用 accessKey/secretKey |
✅ | ✅ | ❌ |
使用 endpoint |
✅ (作为 NAMESRV_ADDR) | ✅ (作为 HTTP endpoint) | ✅ (作为 namesrvAddr) |
使用 groupId |
✅ (GROUP_ID) | ✅ | ✅ |
使用 instanceId |
❌ | ✅ (区分实例) | ❌ |
使用 msgType |
❌ | ❌ | ✅ (NORMAL/ORDERLY) |
| 多 topic 支持 | ✅ (用 ` | ` 分隔) | ✅ (用 ` |
| 消息监听接口 | TcpMqListener |
RocketMqHttpPuller |
SelfBuildMqListener / SelfBuildMqOrderlyListener |
生产者初始化流程
java
// RocketMqConfig.producer() 等效逻辑
@Bean
public Producer producer() {
MqProperties props = rocketMqProperties.getProducer();
if (props == null) return null;
Properties properties = new Properties();
properties.put("AccessKey", props.getAccessKey());
properties.put("SecretKey", props.getSecretKey());
properties.put("NAMESRV_ADDR", props.getEndpoint());
properties.put("SendMsgTimeoutMillis",
StringUtils.defaultIfBlank(props.getSendMsgTimeoutMillis(), "3000"));
Producer producer = ONSFactory.createProducer(properties);
producer.start();
return producer;
}
消费者初始化流程
路由逻辑
在 ApplicationReadyEvent 事件触发时,遍历所有消费者配置,按 protocol 路由:
java
@EventListener
public void handleApplicationReady(ApplicationReadyEvent event) {
Map<String, xxxRocketMqListener> beans = context.getBeansOfType(JshRocketMqListener.class);
// 将 listeners 按 topic 分组为 listenerMap
for (MqProperties mqProperties : rocketMqProperties.getConsumer().values()) {
MqProtocol protocol = MqProtocol.valueOf(mqProperties.getProtocol().toUpperCase());
switch (protocol) {
case HTTP -> clients.add(createHttpClient(mqProperties, listenerMap));
case TCP -> clients.add(createTcpClient(mqProperties, listenerMap));
case SELF -> clients.add(createSelfClient(mqProperties, listenerMap));
}
}
// 启动所有客户端
clients.forEach(xxxRocketMqConsumerClient::start);
}
TCP 模式 --- createTcpClient
java
private xxxRocketMqConsumerClient createTcpClient(MqProperties mqProperties, Map<String, xxxRocketMqListener> listenerMap) {
Properties properties = new Properties();
properties.setProperty("GROUP_ID", mqProperties.getGroupId());
properties.setProperty("AccessKey", mqProperties.getAccessKey());
properties.setProperty("SecretKey", mqProperties.getSecretKey());
properties.setProperty("NAMESRV_ADDR", mqProperties.getEndpoint());
ConsumerBean consumerBean = new ConsumerBean();
consumerBean.setProperties(properties);
xxxRocketMqConsumerClient client = new xxxRocketMqConsumerClient(consumerBean);
// 解析多 topic,为每个 topic 创建订阅
List<String> topics = Lists.newArrayList(StringUtils.split(mqProperties.getTopic(), "|"));
for (String topic : topics) {
String key = String.format("%s_%s",
topic, StringUtils.defaultIfBlank(mqProperties.getTag(), ""));
xxxRocketMqListener listener = listenerMap.get(key);
Subscription subscription = new Subscription();
subscription.setTopic(topic);
subscription.setExpression(mqProperties.getTag());
Map<Subscription, MessageListener> subscriptionTable = new HashMap<>();
subscriptionTable.put(subscription, new TcpMqListener(listener));
consumerBean.setSubscriptionTable(subscriptionTable);
}
return client;
}
HTTP 模式 --- createHttpClient
java
private xxxRocketMqConsumerClient createHttpClient(MqProperties mqProperties, Map<String, xxxRocketMqListener> listenerMap) {
List<String> topics = Lists.newArrayList(StringUtils.split(mqProperties.getTopic(), "|"));
MQClient mqClient = new MQClient(
mqProperties.getEndpoint(),
mqProperties.getAccessKey(),
mqProperties.getSecretKey()
);
Map<MQConsumer, xxxRocketMqListener> map = new HashMap<>();
for (String topic : topics) {
String key = String.format("%s_%s",
topic, StringUtils.defaultIfBlank(mqProperties.getTag(), ""));
xxxRocketMqListener listener = listenerMap.get(key);
MQConsumer consumer;
if (StringUtils.isNoneBlank(mqProperties.getInstanceId())) {
// 带 instanceId 的 4 参数版本
consumer = mqClient.getConsumer(
mqProperties.getInstanceId(), topic,
mqProperties.getGroupId(), mqProperties.getTag());
} else {
// 不带 instanceId 的 3 参数版本
consumer = mqClient.getConsumer(topic,
mqProperties.getGroupId(), mqProperties.getTag());
}
map.put(consumer, listener);
}
return new xxxRocketMqConsumerClient(map);
}
SELF 模式 --- createSelfClient
java
private xxxRocketMqConsumerClient createSelfClient(MqProperties mqProperties, Map<String, xxxRocketMqListener> listenerMap) {
DefaultMqPushConsumerWrapper consumer = new DefaultMqPushConsumerWrapper(mqProperties.getGroupId());
consumer.setNamesrvAddr(mqProperties.getEndpoint());
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
xxxRocketMqConsumerClient client = new xxxRocketMqConsumerClient(consumer);
List<String> topics = Lists.newArrayList(StringUtils.split(mqProperties.getTopic(), "|"));
for (String topic : topics) {
String key = String.format("%s_%s",
topic, StringUtils.defaultIfBlank(mqProperties.getTag(), ""));
xxxRocketMqListener listener = listenerMap.get(key);
consumer.subscribe(topic, mqProperties.getTag());
}
// 根据 msgType 注册不同类型的监听器
MqType mqType = MqType.valueOf(mqProperties.getMsgType().toUpperCase());
switch (mqType) {
case NORMAL -> consumer.registerMessageListener(new SelfBuildMqListener(listener));
case ORDERLY -> consumer.registerMessageListener(new SelfBuildMqOrderlyListener(listener));
}
return client;
}
业务消费者实现模板
接口定义
java
public interface xxxRocketMqListener {
void consume(byte[] bytes);
String topic();
String tag();
}
实现示例
java
@Component
@Slf4j
public class OrderMqConsumer implements xxxRocketMqListener {
@Value("${rocketmq.consumer.order.tag}")
private String tag;
@Value("${rocketmq.consumer.order.topic}") // 或直接引用业务 topic 变量
private String topic;
@Override
public void consume(byte[] bytes) {
OrderDto order = JsonUtil.deserializeObject(bytes, new TypeReference<OrderDto>() {});
log.info("收到订单消息: {}", order.getId());
// 业务逻辑
}
@Override
public String topic() {
return topic;
}
@Override
public String tag() {
return tag;
}
}
Listener 匹配机制
库通过 String.format("%s_%s", topic, tag) 生成 key,将 xxxRocketMqListener 的实现与 YAML 中的消费者配置关联起来。消费者实现类的 topic() + tag() 返回值必须与配置一致。
消息发送模板
java
@Component
public class OrderMqSender {
@Autowired
private Producer producer;
@Value("${rocketmq.producer.tag}")
private String tag;
@Value("${rocketmq.consumer.order.topic}")
private String topic;
public void send(OrderDto order) {
byte[] body = JsonUtil.serializeObject(order).getBytes();
Message message = new Message(topic, tag, body);
message.setKey(String.valueOf(order.getId()));
producer.send(message);
}
}
生命周期管理
java
// 启动:ApplicationReadyEvent 时启动所有 consumer client
clients.forEach(JshRocketMqConsumerClient::start);
// 销毁:ContextClosedEvent 时关闭所有 consumer client
@EventListener
public void handleContextClosed(ContextClosedEvent event) {
clients.forEach(client -> {
try { client.destroy(); } catch (Exception e) { ... }
});
}
// xxxRocketMqConsumerClient 实现了 DisposableBean
public class xxxRocketMqConsumerClient implements DisposableBean {
public void start() { ... }
public void destroy() { ... }
}
各属性使用总结
| 属性 | Producer | TCP Consumer | HTTP Consumer | SELF Consumer |
|---|---|---|---|---|
accessKey |
✅ | ✅ | ✅ | ❌ |
secretKey |
✅ | ✅ | ✅ | ❌ |
endpoint |
✅ | ✅ | ✅ | ✅ |
groupId |
❌ | ✅ | ✅ | ✅ |
topic |
❌ | ✅ | ✅ | ✅ |
tag |
❌ | ✅ | ✅ | ✅ |
instanceId |
❌ | ❌ | ✅ | ❌ |
protocol |
❌ | --- | --- | --- |
msgType |
❌ | ❌ | ❌ | ✅ |
sendMsgTimeoutMillis |
✅ | ❌ | ❌ | ❌ |
关键设计要点
- 多消费者支持 :
consumer是一个 Map,支持配置多个独立消费者,每个消费者可以有不同的协议、topic、groupId - 多 topic 订阅 :单个消费者配置支持
|分隔的多个 topic - Listener 自动发现 :通过 Spring 的
getBeansOfType(xxxRocketMqListener.class)自动收集所有实现类 - 延迟启动 :消费者在
ApplicationReadyEvent之后才启动,确保所有 Bean 初始化完成 - 优雅停机 :监听
ContextClosedEvent关闭所有消费者连接