In January 2019, after six months of development, RocketMQ-Spring, a sub-project of Apache RocketMQ, released its version 2.0.1. This project encapsulated the RocketMQ client by using Spring Boot, allowing users to send and consume messages by writing codes through simple annotations and standard Spring Messaging APIs.
Two years later, RocketMQ-Spring 2.2.0 was officially released. In its two years of development, several RocketMQ-Spring versions have been updated. The RocketMQ-Spring-based Spring Cloud Stream RocketMQ Binder and Spring Cloud Bus RocketMQ have been published on the Spring official website. Due to the Spring proponent Baeldung's introduction on the usage of RocketMQ-Spring, more and more people from home and abroad began to use RocketMQ-Spring to send and receive messages. With the star number of RocketMQ-Spring repository surpassing that of Spring-Kafka and Spring-AMQP (Note: both are maintained by the Spring community) within just two years, RocketMQ has become one of the most popular ecosystems of Apache RocketMQ.
One of the reason for RocketMQ-Spring's popularity lies in the perfect combination of RocketMQ supporting diverse business scenarios and the microservice ecosystem Spring; the other reason is that RocketMQ-Spring itself strictly complies with the specifications of Spring Messaging API and supports diverse message types.
Spring Messaging provides a set of abstract APIs to regulate the modes of message senders and message consumers. Under the mode regulated by Spring Messaging, different message oriented middleware providers can provide their own Spring implementations: a Java Bean in the form of XXXTemplate can be implemented on the message sender to provide multiple message sending methods based on the automatic configuration options of Spring Boot; there is an XXXMessageListener interface (which is usually implemented by using an annotation to declare a message-driven POJO) on the message consumer. It can provide callback methods to listen to and consume messages. The automation options of Spring Boot and some customized attributes are also suitable for this interface.
RocketMQ-Spring provides corresponding APIs based on the Spring Messaging API specifications and the RocketMQ features. On the message sender, RocketMQ-Spring sends messages by implementing the RocketMQTemplate. As shown in the following figure, RocketMQTemplate inherits the AbstractMessageSendingTemplate abstraction class to support the message conversion and sending with the Spring Messaging API standard. These methods will eventually be proxied to the doSend method, and implemented by DefaultMQProducer after the doSend method calls syncSend method.
In addition to the methods in the Spring Messaging API specification, RocketMQTemplate also implements some RocketMQ native client methods to support richer message types. It is noticeable that, compared with native clients that need users to build RocketMQ Message (such as serializing objects into byte arrays and putting the arrays into Message object) on their own, RocketMQTemplate can directly send out the objects, strings or byte arrays as parameters (object serialization is completed in RocketMQ-Spring). Normal sending and receiving will be enabled after determining the corresponding Schema on the consumer side.
RocketMQTemplate Send API:
SendResult syncSend(String destination, Object payload)
SendResult syncSend(String destination, Message<?> message)
void asyncSend(String destination, Message<?> message, SendCallback sendCallback)
void asyncSend(String destination, Message<?> message, SendCallback sendCallback)
……
On the consumer, a class containing the annotation of @RocketMQMessageListener needs to be implemented (requiring the implementation of the RocketMQListener interface and the onMessage method, and then configure properties such as topic and consumerGroup in the annotation). The Listener will be placed one-on-one into the DefaultRocketMQListenerContainer container object, and the container object will encapsulate RocketMQListener into a specific concurrent or sequential interface inside RocketMQ for implementation based on the consumption mode (concurrency or sequence). Then, create a RocketMQ DefaultPushConsumer object in the container, start and listen to the customized Topic messages, convert the determined Schema object, and call back the onMessage method of the Listener.
@Service
@RocketMQMessageListener(topic = "${demo.rocketmq.topic}", consumerGroup = "string_consumer", selectorExpression = "${demo.rocketmq.tag}")
public class StringConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.printf("------- StringConsumer received: %s \n", message);
}
}
In addition to the Push interface, the RocketMQ Lite Pull Consumer is implemented by RocketMQ-Spring in the latest v2.2.0. By configuring the consumer in the configuration file, the Receive method of RocketMQTemplate can be used to actively pull messages.
Configuration file resource/application.properties:
rocketmq.name- server=localhost: 9876
rocketmq.consumer. Group= my-group1
rocketmq.consumer.topic=test
Pull Consumer Code:
while(!isStop) {
List<String> messages = rocketMQTemplate.receive(String.class);
System.out.println(messages);
}
RocketMQ-Spring is fully consistent with RocketMQ native client in terms of message types, which include synchronous, asynchronous, one-way, sequential, delayed, batch, transactional, and Request-Reply messages. The transactional messages and Request-Reply messages are introduced in detail in this section.
Unlike that in Spring Message, the transactional message of RocketMQ adopts the RocketMQ native transactional message solution. As shown below, a class containing the annotation of @RocketMQTransactionListener needs to be implemented when a transactional message is sent. The executeLocalTransaction and checkLocalTransaction methods should also be implemented in order to execute a local transaction and check the local transaction execution result.
// Build a SpringMessage for sending in transaction
Message msg = MessageBuilder.withPayload(..)...;
// In sendMessageInTransaction(), the first parameter transaction name ("test")
// must be same with the @RocketMQTransactionListener's member field 'transName'
rocketMQTemplate.sendMessageInTransaction("test-topic", msg, null);
// Define transaction listener with the annotation @RocketMQTransactionListener
@RocketMQTransactionListener
class TransactionListenerImpl implements RocketMQLocalTransactionListener {
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
// ... local transaction process, return bollback, commit or unknown
return RocketMQLocalTransactionState.UNKNOWN;
}
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
// ... check transaction status and return bollback, commit or unknown
return RocketMQLocalTransactionState.COMMIT;
}
}
In version 2.1.0, RocketMQ-Spring refactors the implementation of transactional messages. As shown in the figure below, in the earlier version, each group corresponds to a TransactionProducer, while in the later version, each RocketMQTemplate corresponds to a TransationProducer. This solves the problem of multiple transactional messages used with concurrency. If users need to use multiple transactional messages in a single process, they can use ExtRocketMQTemplate and specify rocketMQTemplateBeanName as the BeanName of ExtRocketMQTemplate in the corresponding RocketMQTransactionListener annotation. Note that generally, it is recommended that one RocketMQTemplate is used in one process. ExtRocketMQTemplate can be used in the scenarios when the same process requires multiple Producers or LitePullConsumers. Users can specify configurations such as nameserver and group different from those of the standard RocketMQTemplate for ExtRocketMQTemplate.
Request-Reply messages are supported in RocketMQ-Spring v2.1.0,. A Request-Reply message refers to a state that the message is waiting to be sent after being delivered by an upstream service, until the consumer returns a result to the sender. In the RocketMQ-Spring, the sender sends messages through the sendAndRceivce of RocketMQTemplate which includes synchronous and asynchronous modes as shown below. In the asynchronous mode, the callback is completed by implementing RocketMQLocalRequestCallback.
// Send a request synchronously and wait for a return value of the String type.
String replyString = rocketMQTemplate.sendAndReceive("stringRequestTopic", "request string", String.class);
// Send a request asynchronously and wait for a return value of the User type.
User("requestUserName",(byte) 9), new RocketMQLocalRequestCallback<User>() {
@Override public void onSuccess(User message) {
……
}
@Override public void onException(Throwable e) {
……
}
});
On the consumer, a class containing the @RocketMQMessageListener annotation still needs to be implemented. However, the interface to be implemented is the RocketMQReplyListener interface (comparing with the RocketMQListener interface for normal messages). Among this process, T indicates the type of the received value and R indicates the type of the returned value. The interface needs to implement the onMessage method with the returned value, which is returned to the corresponding Producer.
@Service
@RocketMQMessageListener(topic = "stringRequestTopic", consumerGroup = "stringRequestConsumer")
public class StringConsumerWithReplyString implements RocketMQReplyListener<String, String> {
@Override
public String onMessage(String message) {
……
return "reply string";
}
}
RocketMQ-Spring follows the idea of Spring which is Convention over configuration. With the initiator (Spring Boot Starter), all the features of all RocketMQ client can be integrated by introducing dependency (groupId:org.apache.rocketmq and artifactId:rocketmq-spring-boot-starter) in the pom file . Messages can be received and sent with simple annotations. For more detailed use and FAQs, see RocketMQ-Spring Github Wiki.
According to statistics, since the RocketMQ-Spring's first official release, it has fixed 16 bugs and completed 37 improvements, including important optimizations such as transactional message refactoring, message filtering, message serialization, multi-instance RocketMQTemplate optimization. More people are welcomed to participate in the construction of the RocketMQ community, and the story of RocketMQ and Spring Boot is to be continued.
Source: RocketMQ Wechat Official Account
Fluid with JindoFS: An Acceleration Tool for Alibaba Cloud OSS
Lossless Migration of Business Traffic with Cloud Controller Manager for Kubernetes
507 posts | 48 followers
FollowAlibaba Developer - August 19, 2021
Alibaba Cloud Native Community - December 1, 2021
Alibaba Cloud Community - March 9, 2022
Alibaba Cloud Native Community - April 24, 2023
Alibaba Cloud Native Community - January 5, 2023
Alibaba Cloud Native Community - November 23, 2022
507 posts | 48 followers
FollowMSE provides a fully managed registration and configuration center, and gateway and microservices governance capabilities.
Learn MoreMulti-source metrics are aggregated to monitor the status of your business and services in real time.
Learn MoreApsaraMQ for RocketMQ is a distributed message queue service that supports reliable message-based asynchronous communication among microservices, distributed systems, and serverless applications.
Learn MoreAccelerate and secure the development, deployment, and management of containerized applications cost-effectively.
Learn MoreMore Posts by Alibaba Cloud Native Community