各版本Java SDK
阿里云Rocket MQ 提供多种 Java SDK 版本接入
商业版TCP协议SDK
商业版SDK即用于接入阿里云RocketMQ商业版
阿里云RocketMQ商业版,是对开源社区版的RocketMQ作增值,其兼容Apache RocketMQ的核心API和功能
通常生产环境使用此版本SDK,RocketMQ 5.0 版本及以上使用2.x.x.x,否则使用1.8.x.x
<dependency>
<groupId>com.aliyun.openservices</groupId>
<artifactId>ons-client</artifactId>
<version>1.8.8.5.Final</version>
</dependency>
社区版TCP协议SDK
社区版SDK即用于开源社区版的Apache RocketMQ
社区版SDK可用于测试环境,并兼容生产环境接入阿里云RocketMQ商业版时使用
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-acl</artifactId>
<version>4.8.0</version>
</dependency>
商业版HTTP协议SDK
对TCP协议社区版本作增强开发的HTTP协议版本
开源自建 RocketMQ 不支持HTTP协议
<dependency>
<groupId>com.aliyun.mq</groupId>
<artifactId>mq-http-sdk</artifactId>
<!--以下版本号请替换为Java SDK的最新版本号-->
<version>1.0.3.2</version>
<classifier>jar-with-dependencies</classifier>
</dependency>
SDK与RocketMQ版本组合方案
根据各版本Java SDK对不同 RocketMQ 版本的支持,可列出它们之间的组合方案
- 商业版TCP协议SDK 与 阿里云RocketMQ商业版
- 商业版TCP协议SDK 与 开源社区版Apache RocketMQ
- 社区版TCP协议SDK 与 阿里云RocketMQ商业版
- 社区版TCP协议SDK 与 开源社区版Apache RocketMQ
- 商业版HTTP协议SDK 与 阿里云RocketMQ商业版
商业版TCP协议接入示例
本次示例基于 Spring Boot 框架引入商业版TCP协议SDK,并接入阿里云RocketMQ商业版
1.引入依赖
<dependency>
<groupId>com.aliyun.openservices</groupId>
<artifactId>ons-client</artifactId>
<version>1.8.8.5.Final</version>
</dependency>
2.配置类
accessKey,阿里云RocketMQ AccessKey
secretKey,阿里云RocketMQ SecretKey
nameSrvAddr,RocketMQ namesrv 地址
groupId,某一类 Producer 或 Consumer,将这一组指定一个标识
normalTopic,消息主题,这里是作为普通消息类型
delayTopic,消息主体,这里是作为延迟消息类型
/**
* Rocket MQ 配置类
*/
@Configuration
public class RocketMqConfig {
@Value("${rocketmq.access-key}")
private String accessKey;
@Value("${rocketmq.secret-key}")
private String secretKey;
@Value("${rocketmq.namesrv-addr}")
private String nameSrvAddr;
@Value("${rocketmq.group-id}")
private String groupId;
@Value("${rocketmq.topic.normal}")
private String normalTopic;
@Value("${rocketmq.topic.delay}")
private String delayTopic;
public Properties getMqProperties() {
Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.AccessKey, this.accessKey);
properties.setProperty(PropertyKeyConst.SecretKey, this.secretKey);
properties.setProperty(PropertyKeyConst.NAMESRV_ADDR, this.nameSrvAddr);
return properties;
}
}
3.生产者客户端
创建完 TransactionProducerBean 后,即 Bean 实例化后进入其中的 initMethod,销毁时进入 shutdown;
发送消息时调用 send 方法
@Component
public class ProducerClient {
@Autowired
private RocketMqConfig mqConfig;
@Autowired
private MqLocalTransactionChecker localTransactionChecker;
@Bean(initMethod = "start", destroyMethod = "shutdown")
public TransactionProducerBean buildTransactionProducer() {
TransactionProducerBean producer = new TransactionProducerBean();
//配置属性
Properties properties = mqConfig.getMqProperties();
properties.put(PropertyKeyConst.GROUP_ID, mqConfig.getGroupId());
producer.setProperties(properties);
producer.setLocalTransactionChecker(localTransactionChecker);
return producer;
}
}
4.本地事务检查器
生产者采用事务消息的形式,RocketMQ Broker会定期发起事务回查,确保事务一致性
@Component
public class MqLocalTransactionChecker implements LocalTransactionChecker {
@Override
public TransactionStatus check(Message message) {
System.out.println("开始回查本地事务状态");
return TransactionStatus.CommitTransaction; //根据本地事务状态检查结果返回不同的TransactionStatus
}
}
5.消费者客户端
消费客户端用于接收消息,在配置阶段需要订阅消息,指定 topic、tag 过滤表达式、type(SQL92 | TAG)
@Component
public class ConsumerClient {
@Autowired
private RocketMqConfig mqConfig;
@Autowired
private NormalMessageListener normalMessageListener;
@Autowired
private DelayMessageListener delayMessageListener;
@Bean(initMethod = "start", destroyMethod = "shutdown")
public ConsumerBean buildConsumer() {
ConsumerBean consumerBean = new ConsumerBean();
//配置文件
Properties properties = mqConfig.getMqProperties();
properties.put(PropertyKeyConst.GROUP_ID, mqConfig.getGroupId());
properties.put(PropertyKeyConst.MaxReconsumeTimes, "5"); //消息消费失败的最大重试次数
consumerBean.setProperties(properties);
//订阅关系
Map<Subscription, MessageListener> subscriptionTable = new HashMap<Subscription, MessageListener>();
//订阅普通消息 Topic
Subscription subscribeNormal = new Subscription();
subscribeNormal.setTopic(mqConfig.getNormalTopic());
subscribeNormal.setExpression("*");
subscriptionTable.put(subscribeNormal, normalMessageListener);
//订阅延迟消息 Topic
Subscription subscribeDelay = new Subscription();
subscribeDelay.setTopic(mqConfig.getDelayTopic());
subscribeNormal.setExpression("*");
subscriptionTable.put(subscribeDelay, delayMessageListener);
consumerBean.setSubscriptionTable(subscriptionTable);
return consumerBean;
}
}
6.消费者监听器
消费客户端只用于订阅,真正的消费逻辑在监听器回调中实现
6.1.延时消息监听器
@Component
public class DelayMessageListener implements MessageListener {
@Override
public Action consume(Message message, ConsumeContext consumeContext) {
System.out.println("Receive: " + message);
try {
//do something..
return Action.CommitMessage;
} catch (Exception e) {
//消费失败
return Action.ReconsumeLater;
}
}
}
6.2.普通消息监听器
@Component
public class NormalMessageListener implements MessageListener {
@Override
public Action consume(Message message, ConsumeContext consumeContext) {
System.out.println("Receive: " + message);
try {
//do something..
return Action.CommitMessage;
} catch (Exception e) {
//消费失败
return Action.ReconsumeLater;
}
}
}
消费幂等
当消息重复投递,被重复消费时,消费一次与消费多次的结果应是同等的
消息重复的可能:
- 发送时消息重复
生产者客户端已向MQ服务端发送消息,但由于服务端未应答(网络原因或宕机),生产者客户端认为消息发送失败,因此重复发送,消费者后续会收到两条内容相同但Message ID不同的消息。 - 投递时消息重复
消费者客户端已完成消费,但在向MQ服务端发送应答时失败(网络原因),为保证消息至少被消费一次,服务端重新发送消息,消费者后续会收到两条内容相同并且Message ID也相同的消息。 - 负载均衡时消息重复(包括但不限于网络抖动、Broker重启以及消费者应用重启)
当消息队列RocketMQ版的Broker或客户端重启、扩容或缩容时,会触发Rebalance,此时消费者可能会收到少量重复消息。
幂等处理
因为不同的Message ID对应的消息内容可能相同,所以最好的方式是以业务唯一标识作为幂等处理的关键依据,而业务的唯一标识可以通过消息Key设置。
生产者发送消息时设置消息Key:
Message message = new Message();
message.setKey("ORDERID_100");
SendResult sendResult = producer.send(message);
消费者收到消息时可以根据消息的Key,即订单号来实现消息幂等:
consumer.subscribe("ons_test", "*", new MessageListener() {
public Action consume(Message message, ConsumeContext context) {
String key = message.getKey()
// 根据业务唯一标识的Key做幂等处理。
}
});