1 Introduction to asynchronous messages
Remote invocation mechanisms such as RMI and Hessian/burlap are synchronous. As shown in 17.1, when a client invokes a remote method, the client must wait until the remote method completes before it can continue execution. Even if the remote method does not return any information to the client, the client is blocked until the service completes.
The message is sent asynchronously, as shown in 17.2, and the client does not need to wait for the service to process the message or even wait for the message to be delivered. The client sends a message and then continues execution because the client assumes that the service can eventually receive and process the message.
1.1 Sending messages
There are two main concepts in an asynchronous message: The message broker and the destination (destination). When an app sends a message, it gives the message to a message agent.
Although different message systems provide different message routing patterns, there are two common destinations: Queue and Subject (topic). Each type is associated with a specific message model, namely the point-to-point model (queue) and the Publish/subscribe model (subject).
Point-to-point message model
In a point-to-point model, each message has a sender and a recipient, as shown in 17.3. When the message agent gets the message, it puts the message in a queue. When the receiver requests the next message in the queue, the message is fetched from the queue and delivered to the receiver. Because messages are dropped from the queue after they are delivered, this guarantees that the message can only be delivered to one recipient.
Figure 17.3 Message Queuing understands the decoupling of message senders and message receivers. Although the queue can have multiple receivers, each message can only be taken away by one recipient
Although each message in a message queue is only delivered to a single recipient, it does not mean that only one recipient can be used to get the message from the queue. In fact, you can usually use several receivers to process messages in the queue. However, each recipient will process the message that he or she receives.
Publish-Subscribe message Model
In the publish-subscribe message model, messages are sent to a topic. Like queues, multiple receivers can listen to a topic. However, unlike a queue, a message is no longer delivered to only one recipient, but all subscribers to the subject receive a copy of this message, as shown in 17.4.
Figure 17.4 is similar to a queue where a topic can decouple a message sender from a message receiver. Unlike queues, subject messages can be sent to multiple subject subscribers
1.2 Evaluating the benefits of asynchronous messages
The synchronous communication mechanism has several limitations on clients accessing remote services, most notably:
- Synchronous communication means waiting. When a client invokes a method of a remote service, it must wait for the remote method to finish before it can continue execution. If the client communicates with the remote service frequently, or if the remote service responds slowly, the performance of the client application is negatively impacted.
- The client is coupled to the remote service through the service interface. If the interface of the service changes, all clients of this service need to make corresponding changes.
- The client is coupled to the location of the remote service. The client must configure the network location of the service so that it knows how to interact with the remote service. If the network topology is tuned, the client also needs to reconfigure the new network location.
- The client is coupled with the availability of the service. If the remote service is unavailable, the client does not actually function correctly.
No Waiting
When sending a message using JMS, the client does not have to wait for the message to be processed or even delivered. The client only needs to send the message to the message agent, so that it can be assured that the message will be delivered to the appropriate destination.
Because there is no need to wait, the client can continue to perform other tasks. This method can effectively save time, so the performance of the client can be greatly improved.
message and decoupling oriented
Unlike RPC traffic for method calls, sending asynchronous messages is data-centric. This means that the client does not bind to a specific method signature. Any queue or topic Subscriber that can handle the data can handle messages sent by the client, and the client does not have to know any specifications for the remote service.
location Independent
Synchronous RPC services typically require a network address to locate. This means that the client does not have the flexibility to adapt to changes in the network topology. If the IP address of the service has changed, or if the service is configured to listen to other ports, the client must adjust accordingly, otherwise it will not be able to access the service.
In contrast, messaging clients do not have to know who will handle their messages, or where the service is located. The client simply needs to know which queue or topic to send the message to. So, as long as the service can get the message from a queue or topic, the messaging client simply doesn't need to focus on where the service comes from.
ensure delivery
In order for the client to communicate with the synchronization service, the service must listen for the specified IP address and port. If the service crashes or is unavailable for some reason, the client will not be able to continue processing.
However, when sending an asynchronous message, the client can be confident that the message will be posted. Even when a message is sent, the service cannot be used, and the message is stored until the service is ready to be used again.
2 Sending messages using JMS
The Java Messaging Service (Java message Services, JMS) is a Java standard that defines a common API for using message agents.
Spring provides support for JMS functionality through template-based abstraction, which is jmstemplate. With Jmstemplate, it is very easy to send queues and subject messages in the message producer, and it is also very easy to receive these messages on the side of the consumer message. Spring also provides the idea of message-driven Pojo: This is a simple Java object that can respond asynchronously to messages that arrive on a queue or topic.
We will discuss spring's support for JMS, including jmstemplate and message-driven Pojo. But before sending and receiving messages, we first need a message broker that can deliver messages between the producer and the consumer of the message.
2.1 Building a message agent in spring
ACTIVEMQ is a great open source message broker product and is the best choice for asynchronous messaging using JMS.
Create a connection factory
By default, Activemqconnectionfactory assumes that the ACTIVEMQ agent listens on port 61616 on localhost. There is no problem with the development environment, but in a production environment, ACTIVEMQ may be on different hosts and/or ports. If this is the case, we can use the Brokerurl property to specify the proxy URL:
<bean id="connectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory" p:brokerURL="tcp://localhost:61616" />
declaring ACTIVEMQ message destinations
In addition to connecting the factory, we also need the destination of the message delivery. The destination can be a queue or a topic, depending on the needs of the application.
Regardless of whether you are using a queue or a topic, we must configure the destination bean in spring with a specific message broker implementation class. For example, the following <bean>
declaration defines a activemq queue:
<bean id="spittleQueue" class="org.apache.activemq.command.ActiveMQQueue" c:_="spittle.alert.queue" />
Similarly, the following <bean>
declaration defines a activemq topic:
<bean id="spittleTopic" class="org.apache.activemq.command.ActiveMQTopic" c:_="spittle.alert.topic" />
The Activemq namespace provides another way to declare queues and topics. For queues, we can use <amq:quence>
elements to declare:
<amq:queue id="spittleQueue" physicalName="spittle.alert.queue" />
If it is a JMS topic, we can use <amq:topic>
elements to declare:
<amq:topic id="spittleTopic" physicalName="spittle.alert.topic" />
Regardless of the type, the name of the message channel is specified with the PhysicalName property.
2.2 Using the Spring JMS template
handling out-of-control JMS code
How lengthy and complex the traditional JDBC code is when dealing with joins, statements, result sets, and exceptions. Unfortunately, traditional JMS uses a similar programming model.
using the JMS template
Spring's solution for how to eliminate lengthy and repetitive JMS code is jmstemplate. Jmstemplate can create connections, get sessions, and send and receive messages. This allows us to focus on building the message to be sent or processing the received message.
In addition, Jmstemplate can handle all of the clumsy jmsexception exceptions thrown:
Table 17.1 Spring jmstemplate captures the standard jmsexception exception and re-throws it in spring's non-checked exception jmsexception subclass
In order to use jmstemplate, we need to declare it as a bean in the spring configuration file. The following XML can do the work:
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" c:_-ref="connectionFactory" />
Send Message
In the SPITTR application we want to build, one of the features is to remind other users (perhaps via e-mail) when creating spittle. We can directly implement this feature in places where the spittle is added. But figuring out who to send reminders to and actually sending these reminders can take a while, which can affect the performance of your app. When we add a new spittle, we want the app to be agile and respond quickly.
To send spittle reminders asynchronously when Spittle is created, let's introduce alertservice for SPITTR applications:
public interface AlertService { void sendSpittleAlert(Spittle spittle);}
public class AlertServiceImpl implements AlertService { private JmsOperations jmsOperations; public AlertServiceImpl(JmsOperations jmsOperations) { this.jmsOperations = jmsOperations; } public void sendSpittleAlert(final Spittle spittle) { jmsOperations.send( "spittle.alert.queue", new MessageCreator() { public Message createMessage(Session session) throws JMSException { return session.createObjectMessage(spittle); } } ); }}
Set Default Destination
If you want to specify the type of destination you want to create, you can assemble the destination bean for the queue or topic you created earlier:
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" c:_-ref="connectionFactory" p:defaultDestination-ref="spittleTopic"/>
Now, when we call Jmstemplate's Send () method, we can remove the first parameter:
jmsOperations.send( new MessageCreator() { //…)
Convert a message when it is sent
In addition to the Send () method, Jmstemplate also provides the Convertandsend () method. Unlike the Send () method, the Convertandsend () method does not require messagecreator as a parameter. This is because Convertandsend () creates a message for us using the built-in message converter (MSG converter).
When we use Convertandsend (), Sendspittlealert () can be reduced to one line of code in the method body:
public void sendSpittleAlert(Spittle spittle) { jmsOperations.convertAndSend(spittle); }
Receiving Messages
When the Jmstemplate receive () method is called, Jmstemplate attempts to get a message from the message agent. If no messages are available, the receive () method waits until the message is received. Figure 17.6 illustrates this interactive process.
In Convertandsend (), we've seen how to convert an object to a message. However, they can also be used on the receiving side, that is, using Jmstemplate's Receiveandconvert ():
public Spittle retrieveSpittleAlert() { return (Spittle) jmsOperations.receiveAndConvert(); }
The biggest disadvantage of receiving messages using Jmstemplate is that both the receive () and Receiveandconvert () methods are synchronous. This means that the recipient has to wait patiently for the message to arrive, so these methods will remain blocked until a message is available (or until the timeout is exceeded). Synchronous receive asynchronously sent messages, is it feeling weird?
This is where the message-driven Pojo authorizing. Let's look at how to use a component that responds to a message to receive messages asynchronously instead of waiting for the message to arrive.
2.3 Creating a message-driven Pojo
Configuring message Listeners
The trick to giving Pojo the ability to receive messages is to configure it as a message listener in spring. Spring's JMS namespace gives us everything we need. First, let's declare the processor as a bean:
<bean id="spittleHandler" class="spittr.alerts.SpittleAlertHandler" />
Then, in order to turn Spittlealerthandler into a message-driven pojo, we need to declare this bean as a message listener:
<jms:listener-container> <jms:listener destination="spittle.alert.queue" ref="spittleHandler" method="handleSpittleAlert" /></jms:listener-container>
Here, we include a message listener in the Message listener container. The Message Listener container (MSG listener container) is a special bean that monitors the JMS destination and waits for the message to arrive. Once a message arrives, it takes out the message and passes the message to any message listener that is interested in the message. 17.7 shows this interactive process.
Figure 17.7 the message listener container listens to queues and topics. When the message arrives, the message is forwarded to the message listener (for example, message-driven Pojo)
To configure the message listener container and message listener in spring, we used two elements in the spring JMS namespace. <jms:listener-container>
elements are included in the <jms:listener>
. The Connection-factory property here configures a reference to ConnectionFactory, and each of the containers <jms:listener>
uses the connection factory for message monitoring.
2.4 Using message-based RPC
Export a JMS-based service
If Httpinvokerserviceexporter can export services based on HTTP communication, then Jmsinvoker-serviceexporter should be able to export JMS-based services.
To demonstrate how Jmsinvokerserviceexporter works, consider the following alertserviceimpl.
, Alertserviceimpl is annotated with the @component annotation, so it is automatically discovered by spring and registered as an ID in the Spring app context
The Bean for Alertservice. When configuring Jmsinvokerserviceexporter, we will refer to this bean:
The properties of the exporter do not describe the details of how the service is based on JMS traffic. But the good news is that jmsinvokerserviceexporter can act as a JMS listener. Therefore, we use <jms:listenercontainer>
elements to configure it:
using a JMS-based service
In order to use the reminder service, we can configure Jmsinvokerproxyfactorybean as follows:
The ConnectionFactory and QueryName properties specify how RPC messages are delivered-in this case, in a given connection factory, a queue named Spitter.alert.queue in the message agent we configure. For Serviceinterface, specifies that the agent should expose the functionality through the Alertservice interface.
For years, JMS has been the mainstream messaging solution in Java applications. But for Java and spring developers, JMS is not the only option for messages. In the past few years
, Advanced Message Queuing Protocol (Queuing Protocol, AMQP) has received extensive attention.
3 using AMQP to implement message functionality
The AMQP line-layer protocol regulates the format of the message, which is followed when the message is transmitted between the producer and the consumer. This way, AMQP is better than jms--in collaboration, not only across different AMQP implementations, but also across languages and platforms. (AMQP is not limited to the Java language and platform, which means you've caught the point quickly.) )
3.1 AMQP Profile
In contrast, AMQP producers do not publish messages directly to the queue. AMQP introduces an indirect mechanism between the producer of the message and the queue that passes the message: Exchange. This relationship is shown in 17.8.
You can see that the producer of the message publishes the information to an exchange. Exchange binds to one or more queues, and it is responsible for routing information to the queue. The consumer of the information extracts the data from the queue and processes it.
AMQP defines four different types of exchange, each with a different routing algorithm that determines whether or not to put information in the queue. Depending on the exchange algorithm, it may use the routing key and/or parameters of the message and compare it to the routing key and parameter of the binding between Exchange and the queue. (routing key can be roughly understood as the recipient address of an email, specifying the intended recipient.) If the comparison results satisfy the corresponding algorithm, the message will be routed to the queue. Otherwise, it will not be routed to the queue.
The four standard AMQP exchange are as follows:
- Direct: If the routing key of the message matches the binding's routing key directly, the message will be routed to that queue;
- Topic: If the routing key of the message matches the routing key of the binding, the message will be routed to that queue;
- Headers: If the header information and values in the message parameter table match the bingding parameter table, the message will be routed to that queue;
- Fanout: Regardless of the message's routing key and the header information/value of the parameter table, the message will be routed to all queues.
3.2 Configuring spring support for AMQP messages
The first time we used the spring JMS abstraction, we first configured a connection factory. Similarly, a connection factory is configured before using Spring AMQP. However, instead of a JMS connection factory, you need to configure a connection factory for AMQP. More specifically, the RABBITMQ connection factory needs to be configured.
What is RABBITMQ
<connection-factory id="connectionFactory" host="${rabbitmq.host}" port="${rabbitmq.port}" username="${rabbitmq.username}" password="${rabbitmq.password}" />
declaring queues, exchange, and binding
Table 17.3 Spring AMQP's rabbit namespace contains elements that create queues, exchange, and binding them together
These configuration elements are associated with the
For example, if you want to declare a queue named Spittle.alert.queue, simply add the following two elements to the spring configuration:
<admin connection-factory="connectionFactory"/><queue id="spittleAlertQueue" name="spittle.alerts" />
For simple messages, we just need to do this. This is because there is a direct exchange without a name by default, and all queues are bound to this exchange, and the routing key is the same as the queue name. In this simple configuration, we can send the message to this non-named Exchange and set the routing key to spittle.alert.queue so that the message is routed to this queue. In fact, we re-created the JMS point-to-point model.
3.3 Sending messages using Rabbittemplate
The simplest way to configure rabbittemplate is to use elements of the rabbit namespace <template>
, as follows:
<template id="rabbitTemplate" connection-factory="connectionFactory" routing-key="spittle.alerts" />
Now, to send a message, we just need to inject the template bean into the Alertserviceimpl and use it to send the spittle. The following list of programs shows a new version of Alertserviceimpl, which uses rabbittemplate instead of jmstemplate to send spittle reminders.
public class AlertServiceImpl implements AlertService { private RabbitTemplate rabbit; @Autowired public AlertServiceImpl(RabbitTemplate rabbit) { this.rabbit = rabbit; } public void sendSpittleAlert(Spittle spittle) { rabbit.convertAndSend("spittle.alert.exchange", "spittle.alerts", spittle); }}
As you can see, now Sendspittlealert () calls Rabbittemplate's Convertandsend () method, where rabbittemplate is injected. It passed in three parameters: the name of Exchange, the routing key, and the object to be sent. Note that this does not specify where the message is routed, which queue to send to, and which consumer is expected to get the message.
The Convertandsend () method, which automatically converts an object to a message. It requires the help of a message converter to complete the task, and the default message converter is Simplemessageconverter, which applies to string, serializable instances, and byte arrays. Spring AMQP also provides several other useful message converters, including message converters that use JSON and XML data.
3.4 Receiving AMQP messages
use Rabbittemplate to receive messages
Rabbittemplate provides multiple ways to receive information. The simplest is the receive () method, which is located on the consumer side of the message, corresponding to the rabbittemplate's send () method. With the receive () method, we can get a message object from the queue:
Message message=rabbit.receive("spittle.alert.queue");
Or, if you want, you can also configure the default queue for getting messages, which is achieved by setting the queue property when configuring the template:
<template id="rabbitTemplate" connection-factory="connectionFactory" exchange="spittle.alert.exchange" routing-key="spittle.alerts" queue="spittle.alert.queue"/>
In this case, when we call the receive () method, we do not need to set any parameters to get the message from the default queue:
Message message=rabbit.receive();
After getting to the message object, we may need to convert the byte array in its Body property to the desired object. Like converting a domain object to a message when it is sent, it is also cumbersome to convert the received message to a domain object. Therefore, we can consider using the Rabbittemplate Receiveandconvert () method as an alternative:
Spittle spittle = (Spittle)rabbit.receiveAndConvert();
The Receiveandconvert () method uses the same message converter as the Sendandconvert () method to convert the message object to the original type.
Both the receive () and Receiveandconvert () methods are returned immediately, and NULL is obtained if there are no waiting messages in the queue. This requires us to manage polling (polling) and the necessary threads for monitoring the queue.
Instead of having to synchronize polling and wait for a message to arrive, Spring AMQP also provides support for message-driven Pojo, which reminds us of the same features in spring JMS. Let's look at how message-driven AMQP Pojo can receive messages.
Defining message-driven AMQP POJO
public class SpittleAlertHandler { public void handleSpittleAlert(Spittle spittle) { System.out.println(spittle.getMessage()); }}
We also need to declare spittlealerthandler as a bean in the Spring application context:
<beans:bean id="spittleListener" class="spittr.alerts.SpittleAlertHandler" />
Finally, we need to declare a listener container and listener that can call Spittlealerthandler when the message arrives. In the JMS-based MDP, we did the same thing, but the AMQP-based MDP has a slight difference in configuration:
<listener-container connection-factory="connectionFactory" > <listener ref="spittleListener" method="handleSpittleAlert" queues="spittleAlertQueue" /> </listener-container>
which
<queue id="spittleAlertQueue" name="spittle.alert.queue" />
Source
Https://github.com/myitroad/spring-in-action-4/tree/master/Chapter_17
List of attachments
- Alertsebean.jpg
- Alertsi.jpg
- Amqp.jpg
- Asyn.jpg
- Jmsexcep1.jpg
- Jmsexcep2.jpg
- Jmsinvokerproxy.jpg
- Jmsrec.jpg
- Listenbean.jpg
- Mdp.jpg
- P2p.jpg
- Rabbitsn.jpg
- Syn.jpg
- Topic.jpg
17th Chapter-spring News