本文主要介绍rocketmq-spring-boot支持的高级用例,包括发送顺序消息,异步发送,消费过滤以及事务消息发送。该项目git地址:https://github.com/apache/rocketmq-spring
文章主要内容包括以下几个方面:
1 前言
2 API和注解列表
3 消息发送端
4 消息消费端
5 发送事务消息
5.1 定义回查实现类
5.2 RocketMQTemplate发送事务消息
首先在这里向大家报告一个好消息,rocketmq-spring-boot项目经过6个多月的孵化(孵化项目repo: https://github.com/apache/rocketmq-externals),在今年12月初正式毕业。今后的维护和增强将在新的release仓库中进行,具体的地址是:https://github.com/apache/rocketmq-spring
我们把原来单一的project模块按照Spring Boot的规范划分成了四个子模块:
相对于孵化器版本,本次代码进行了较大的调整。目前已经支持spring-boot 2.0,推荐使用孵化器版本的用户尽快切换到新release的版本。请参考samples来体验spring-boot方式发送和消费消息的编码和使用方式:
https://github.com/apache/rocketmq-spring/tree/master/rocketmq-spring-boot-samples。
下面具体的介绍一下rocketmq-spring-boot 的一些使用细节。
编写代码时需要按消息发送者(Producer)和消息消费者(Consumer)分别进行代码编写,会使用到如下的API或注解:
注:关于上述API或注解的使用方式,我们提供了如何使用Spring-Boot发送和消费RocketMQ消息的例子,可以直接参考sample的源码
https://github.com/apache/rocketmq-externals/tree/master/samples/rocketmq-spring-boot-starter-sample
下面的文档是对示例的简单说明。
在使用RocketMQTemplate编写客户端时,需要执行如下的步骤:
1、定义Spring-Boot的 application.properties (注:如果全部使用默认的配置,可以不定义这个文件)
## application.properties
spring.rocketmq.nameServer=127.0.0.1:9876
#你可以根据自己的name-server信息进行修改
spring.rocketmq.producer.group=my-group
## 其他的配置信息spring.rocketmq.producer.retryTimesWhenSendAsyncFailed=0
spring.rocketmq.producer.sendMessageTimeout=300000
spring.rocketmq.producer.compressMessageBodyOverHowmuch=4096
spring.rocketmq.producer.maxMessageSize=4194304
spring.rocketmq.producer.retryAnotherBrokerWhenNotStoreOk=false
spring.rocketmq.producer.retryTimesWhenSendFailed=2
2、声明RocketMQTemplate,并根据发送方式的不同选择合适的方法进行消息发送。
3、如果是异步发送还需要在异步调用方法中,设置回调的Callback对象。
import org.apache.rocketmq.spring.starter.core.RocketMQTemplate;
...
@SpringBootApplication
public class ProducerApplication implements CommandLineRunner{
@Resource
private RocketMQTemplate rocketMQTemplate;
public static void main(String[] args){
SpringApplication.run(ProducerApplication.class, args);
}
public void run(String... args) throws Exception {
// 以同步的方式发送消息,构造器构造对象消息给指定的topic
sendResult = rocketMQTemplate.syncSend(springTopic, MessageBuilder.withPayload("Hello, World! I'm from spring message").build());
System.out.printf("string-topic syncSend2 sendResult=%s %n", sendResult); // 异步方式发送用户定义对象类型的消息,并实现回调接口SendCallback
rocketMQTemplate.asyncSend(orderPaidTopic, new OrderPaidEvent("T_001", new BigDecimal("88.00")), new SendCallback() {
// 实现消息发送成功的后续处理
public void onSuccess(SendResult var1) {
System.out.printf("async onSucess SendResult=%s %n", var1);
}
// 实现消息发送失败的后续处理
public void onException(Throwable var1) {
System.out.printf("async onException Throwable=%s %n", var1);
}
});
// 指定topic的同时,设置tag值,以便消费端可以根据tag值进行选择性消费
rocketMQTemplate.convertAndSend(msgExtTopic + ":tag0", "I'm from tag0");
// tag0 will not be consumer-selected
rocketMQTemplate.convertAndSend(msgExtTopic + ":tag1", "I'm from tag1");
}
@Data
@AllArgsConstructor
public class OrderPaidEvent implements Serializable{
private String orderId;
private BigDecimal paidMoney;
}
}
在消息消费端,只需要根据发送消息的类型实现RocketMQListener并将它声明成Spring @Service和@RocketMQMessageListener,同时在相应的onMessage()方法里对拉取到的消息做处理。在@RocketMQMessageListener注解中可以定义如下具体的消费属性:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documentedpublic @interface RocketMQMessageListener {
String consumerGroup();// 指定consumerGroup
String topic();// 指定消费的topic
SelectorType selectorType() default SelectorType.TAG; // 指定消费过滤方式: TAG, SQL92
String selectorExpress() default "*"; // 根据过滤方式,定义选择表达式
ConsumeMode consumeMode() default ConsumeMode.CONCURRENTLY; // 消费方式:并发,顺序
MessageModel messageModel() default MessageModel.CLUSTERING; // 消费模式: 集群, 广播
int consumeThreadMax() default 64; //消费的并发线程数
}
以下消费端代码,根据指定tag过滤消费信息并声明消费起点的Push方式消费
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.common.UtilAll;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.spring.starter.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.starter.core.RocketMQListener;
import org.apache.rocketmq.spring.starter.core.RocketMQPushConsumerLifecycleListener;
import org.springframework.stereotype.Service;
// 消费监听标签里定义了消费相关的属性,包括:主题,选择表达式,消费组
@Service
@RocketMQMessageListener(topic = "message-ext-topic", selectorExpress = "tag1", consumerGroup = "${spring.application.name}-message-ext-consumer")
public class MessageExtConsumer implements RocketMQListener, RocketMQPushConsumerLifecycleListener {
@Override
// 实现消息的消费处理
public void onMessage(MessageExt message) {
System.out.printf("------- MessageExtConsumer received message, msgId:%s, body:%s %n ", message.getMsgId(), new String(message.getBody()));
}
@Override
// 设置从当前时间点开始消费消息
public void prepareStart(DefaultMQPushConsumer consumer) {
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_TIMESTAMP); consumer.setConsumeTimestamp(UtilAll.timeMillisToHumanString3(System.currentTimeMillis()));
}
}
对于事务消息与普通消息发送只在消息发布端区别,在消费端编写代码是没有区别的,所以这里只介绍消息发送端。发送事务消息需要在发送端做如下的编程处理:
5.1 定义回查实现类
实现RocketMQLocalTransactionListener接口,并将它使用注解@RocketMQTransactionListener来声明:
@RocketMQTransactionListener(txProducerGroup = TX_PGROUP_NAME)
public class TransactionListenerImpl implements RocketMQLocalTransactionListener{
@Override
// 实现执行本地事务的逻辑,并返回本地事务执行状态
public LocalTransactionState executeLocalTransaction(Message msg, Object arg){
// 实现执行本地事务的逻辑
...
// 可以根据具体的本地事务的执行情况返回 RocketMQLocalTransactionState.COMMIT, ROLLBACK 或UNKNOWN 三种状态
return ...
}
@Override
// 实现本地事务回查的逻辑,并返回本地事务执行状态
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
// 实现本地事务回查的逻辑
...
// 可以根据具体的本地事务的执行情况返回 RocketMQLocalTransactionState.COMMIT, ROLLBACK 或UNKNOWN 三种状态
return ...
}
}
@RocketMQTransactionListener注解用来定义TransactionListener的可配置属性:
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented@Component
public @interface RocketMQTransactionListener {
/**
* 定义事务消息发送者组名
* Declare the txProducerGroup that is used to relate callback event to the listener, rocketMQTemplate must send a
* transactional message with the declared txProducerGroup.
*
*
It is suggested to use the default txProducerGroup if your system only needs to define a TransactionListener class.
*/
String txProducerGroup() default RocketMQConfigUtils.ROCKETMQ_TRANSACTION_DEFAULT_GLOBAL_NAME;
// 以下定义事务回查ExecutorService的属性信息
/**
* Set ExecutorService params -- corePoolSize
*/
int corePoolSize() default 1;
/**
* Set ExecutorService params -- maximumPoolSize
*/
int maximumPoolSize() default 1;
/**
* Set ExecutorService params -- keepAliveTime
*/
long keepAliveTime() default 1000 * 60; //60ms
/**
* Set ExecutorService params -- blockingQueueSize
*/
int blockingQueueSize() default 2000;}
5.2 在RocketMQTemplate中使用特定的方法来发送事务消息
// 构造Spring Message请求消息
Message msg = MessageBuilder.withPayload("Hello RocketMQ " + i).
setHeader(RocketMQHeaders.KEYS, "KEY_" + i).build();
// 指定在@RocketMQTransactionListener中声明的txProducerGroup, 使用这个事务发布者组来发送事务消息和回查状态
SendResult sendResult = rocketMQTemplate.sendMessageInTransaction(TX_PGROUP_NAME, msg, null);
如果社区的朋友有兴趣,欢迎大家提建议或者PR来改进和加强spring-boot代码。在源代码库的README(https://github.com/apache/rocketmq-spring/blob/master/README_zh_CN.md)
部分,我们整理了一部分的FAQ的问题,如果有更多的疑问请给我们留言或Email,我们会整理到网站,来让大家更方便的使用。
大家有疑问也可以留言,会为大家一一作答。
官微:扫二维码关注Apache RocketMQ官方微信公众号,获得RocketMQ技术干活和最新资讯。
钉钉:扫二维码进入Apache RocketMQ社区官方钉钉群,随时与创始人团队及业内顶级消息中间件专家互动,更有每周一期的技术公开课直播,获得最前沿的技术干货。