New project
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.tedu</groupId> <artifactId>rabbitmq-springboot</artifactId> <version>0.0.1-SNAPSHOT</version> <name>rabbitmq-springboot</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</project>
application.yml
spring: rabbitmq: host: 192.168.64.140 username: admin password: admin
main program
Delete automatically created main program
We create a package for each mode, create their own main program in each package, and test them separately
Simple mode
main program
The Queue class provided by Spring is the encapsulation object of the Queue, which encapsulates the parameter information of the Queue
The automatic configuration class of RabbitMQ will discover these Queue instances and define these queues in the RabbitMQ server
package cn.tedu.m1; import org.springframework.amqp.core.Queue; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } @Bean public Queue task_queue() { /* * The following forms can be used: * new Queue("helloworld") - Persistent, non exclusive, non automatic deletion * new Queue("helloworld",false,false,false,null) */ return new Queue("helloworld",false); } }
producer
AmqpTemplate is an encapsulation tool of rabbitmq client API, which provides a simple way to perform message operations
AmqpTemplate is automatically created by the auto configuration class
package cn.tedu.m1; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class SimpleSender { @Autowired AmqpTemplate t; public void send() { // Here, a message is sent to the helloworld queue t.convertAndSend("helloworld", "Hello world!! "+System.currentTimeMillis()); System.out.println("Message sent"); } }
consumer
Receive messages from the specified queue through @ RabbitListener
Use the @ RebbitHandler annotation to process messages
package cn.tedu.m1; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component @RabbitListener(queues = "helloworld") public class SimpleReceiver { @RabbitHandler public void receive(String msg) { System.out.println("received: "+msg); } }
Another form can be used here:
@Component public class SimpleReceiver { @RabbitListener(queues = "helloworld") public void receive(String msg) { System.out.println("received: "+msg); } }
In addition, queues can also be defined directly in the @ RabbitListener annotation:
@RabbitListener(queuesToDeclare = @Queue(name = "helloworld",durable = "false"))
Test class
Create a test class in the directory where the test code is stored
package cn.tedu.m1; import java.util.Scanner; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class SimpleTests { @Autowired SimpleSender simpleSender; @Test void test1() throws Exception { simpleSender.send(); System.out.println("[Press enter to end]"); new Scanner(System.in).nextLine(); } }
Working mode
main program
Create a task in the main program_ Persistent queue for queue
package cn.tedu.m2; import org.springframework.amqp.core.Queue; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } @Bean public Queue task_queue() { // The queue parameters created by this construction method are: persistent, non exclusive, and non automatic deletion return new Queue("task_queue"); } }
producer
package cn.tedu.m2; import java.util.Scanner; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class WorkSender { @Autowired AmqpTemplate t; public void send() { while (true) { System.out.print("input:"); String s = new Scanner(System.in).nextLine(); //spring sets the DeliveryMode of the message to PERSISTENT by default, t.convertAndSend("task_queue", s); } } }
In the rabbitmq api encapsulated by spring boot, the message sent is a persistent message by default
If you want to send non persistent messages, you need to make the following settings when sending messages:
- Use MessagePostProcessor preprocessor parameters
- Gets the property object of the message from the message
- Set DeliveryMode to non persistent in the property
//If you need to set the message to be non persistent, you can get the property object of the message and modify its deliveryMode property t.convertAndSend("task_queue", (Object) s, new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { MessageProperties props = message.getMessageProperties(); props.setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT); return message; } });
consumer
package cn.tedu.m2; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class WorkReceiver1 { @RabbitListener(queues="task_queue") public void receive1(String s) throws Exception { System.out.println("receiver1 - received: "+s); for (int i = 0; i < s.length(); i++) { if (s.charAt(i) == '.') { Thread.sleep(1000); } } } @RabbitListener(queues="task_queue") public void receive2(String s) throws Exception { System.out.println("receiver2 - received: "+s); for (int i = 0; i < s.length(); i++) { if (s.charAt(i) == '.') { Thread.sleep(1000); } } } }
Test class
package cn.tedu.m2; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class WorkTests { @Autowired WorkSender workSender; @Test void test1() throws Exception { workSender.send(); } }
ack mode
Three confirmation modes are provided in spring boot:
- NONE - automatic confirmation using rabbitmq
- AUTO - using rabbitmq's manual confirmation, springboot will automatically send a confirmation receipt (default)
- MANUAL - MANUAL confirmation using rabbitmq and must be performed manually
In the default AUTO mode, if the method of processing the message throws an exception, it means that the message has not been processed correctly, and the message will be sent again
Set ack mode
spring: rabbitmq: listener: simple: # acknowledgeMode: NONE # Automatic confirmation of rabbitmq acknowledgeMode: AUTO # For the manual confirmation of rabbitmq, springboot will automatically send a confirmation receipt (default) # acknowledgeMode: MANUAL # For the manual confirmation of rabbitmq, springboot does not send the receipt, but must encode and send the receipt by itself
Perform confirmation manually
If it is set to MANUAL mode, the confirmation operation must be performed manually
@RabbitListener(queues="task_queue") public void receive1(String s, Channel c, @Header(name=AmqpHeaders.DELIVERY_TAG) long tag) throws Exception { System.out.println("receiver1 - received: "+s); for (int i = 0; i < s.length(); i++) { if (s.charAt(i) == '.') { Thread.sleep(1000); } } // Send confirmation receipt manually c.basicAck(tag, false); }
Grab quantity
In the working mode, in order to distribute data reasonably, qos needs to be set to 1, only one message is received at a time, and the next message is received after processing
spring boot is set through prefetch attribute. The default value of the modified attribute is 250
spring: rabbitmq: listener: simple: prefetch: 1 # qos=1, default 250
Publish and subscribe mode
main program
Create a FanoutExcnahge instance to encapsulate the FanoutExcnahge type switch definition information
The auto configuration class of spring boot will automatically discover the switch instance and define the switch in the RabbitMQ server
package cn.tedu.m3; import org.springframework.amqp.core.FanoutExchange; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } @Bean public FanoutExchange fanoutExchange() { return new FanoutExchange("logs"); } }
producer
The producer sends data to the specified switch logs
There is no need to specify the queue name or routing key, even if it is specified, it is invalid, because the fanout switch will send data to all bound queues instead of selective sending
package cn.tedu.m3; import java.util.Scanner; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Publisher { @Autowired AmqpTemplate t; public void send() { while (true) { System.out.print("input:"); String s = new Scanner(System.in).nextLine(); // Specifies to send to the logs switch without specifying the queue name or routing key t.convertAndSend("logs","",s); } } }
consumer
The consumer needs to do the following:
- Define random queues (randomly named, non persistent, exclusive, auto delete)
- Define the switch (it can be omitted and has been defined in the main program)
- Bind queue to switch
spring boot completes the above operations through annotations:
@RabbitListener(bindings = @QueueBinding( //Binding settings are performed here value = @Queue, //Random queues are defined here. The default attributes are random naming, non persistent, exclusive, and automatic deletion exchange = @Exchange(name = "logs", declare = "false") //The logs switch is specified because it has been defined in the main program, so it is not defined here ))
package cn.tedu.m3; import org.springframework.amqp.rabbit.annotation.Exchange; import org.springframework.amqp.rabbit.annotation.Queue; import org.springframework.amqp.rabbit.annotation.QueueBinding; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class Subscriber { @RabbitListener(bindings = @QueueBinding(value = @Queue, exchange = @Exchange(name = "logs", declare = "false"))) public void receive1(String s) throws Exception { System.out.println("receiver1 - received: "+s); } @RabbitListener(bindings = @QueueBinding(value = @Queue, exchange = @Exchange(name = "logs", declare = "false"))) public void receive2(String s) throws Exception { System.out.println("receiver2 - received: "+s); } }
Test class
package cn.tedu.m3; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class PublishSubscribeTests { @Autowired Publisher publisher; @Test void test1() throws Exception { publisher.send(); } }
Routing mode
Similar to the publish and subscribe mode code, only the following three adjustments are made:
- Using direct switch
- When the queue is bound to the switch, set the binding key
- When sending a message, specify the routing key
main program
In the main program, the DirectExcnahge object is used to encapsulate the switch information. The spring boot automatic configuration class will automatically discover this object and define the switch on the RabbitMQ server
package cn.tedu.m4; import org.springframework.amqp.core.DirectExchange; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } @Bean public DirectExchange fanoutExchange() { return new DirectExchange("direct_logs"); } }
producer
The producer sends a message to the specified switch and specifies the routing key
package cn.tedu.m4; import java.util.Scanner; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class RouteSender { @Autowired AmqpTemplate t; public void send() { while (true) { System.out.print("Input message:"); String s = new Scanner(System.in).nextLine(); System.out.print("Enter routing key:"); String key = new Scanner(System.in).nextLine(); // The second parameter specifies the routing key t.convertAndSend("direct_logs",key,s); } } }
consumer
The consumer defines the random queue through annotation, binds it to the switch, and specifies the binding key:
@RabbitListener(bindings = @QueueBinding( // Binding settings are made here value = @Queue, // Define queue, randomly named, non persistent, exclusive, auto delete exchange = @Exchange(name = "direct_logs", declare = "false"), // Specify the bound switch. The queue has been defined in the main program. It is not defined here key = {"error","info","warning"} // Set binding key ))
package cn.tedu.m4; import org.springframework.amqp.rabbit.annotation.Exchange; import org.springframework.amqp.rabbit.annotation.Queue; import org.springframework.amqp.rabbit.annotation.QueueBinding; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class RouteReceiver { @RabbitListener(bindings = @QueueBinding(value = @Queue,exchange = @Exchange(name = "direct_logs", declare = "false"),key = {"error"})) public void receive1(String s) throws Exception { System.out.println("receiver1 - received: "+s); } @RabbitListener(bindings = @QueueBinding(value = @Queue, exchange = @Exchange(name = "direct_logs", declare = "false"),key = {"error","info","warning"})) public void receive2(String s) throws Exception { System.out.println("receiver2 - received: "+s); } }
Test class
package cn.tedu.m4; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class RouteTests { @Autowired RouteSender sender; @Test void test1() throws Exception { sender.send(); } }
Theme mode
The topic mode is just a routing mode with special rules. The code is basically the same as the routing mode, with the following adjustments:
- Using topic switch
- Use special binding and routing key rules
main program
package cn.tedu.m5; import org.springframework.amqp.core.TopicExchange; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } @Bean public TopicExchange fanoutExchange() { return new TopicExchange("topic_logs"); } }
producer
package cn.tedu.m5; import java.util.Scanner; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class TopicSender { @Autowired AmqpTemplate t; public void send() { while (true) { System.out.print("Input message:"); String s = new Scanner(System.in).nextLine(); System.out.print("Enter routing key:"); String key = new Scanner(System.in).nextLine(); t.convertAndSend("topic_logs",key,s); } } }
consumer
package cn.tedu.m5; import org.springframework.amqp.rabbit.annotation.Exchange; import org.springframework.amqp.rabbit.annotation.Queue; import org.springframework.amqp.rabbit.annotation.QueueBinding; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class TopicReceiver { @RabbitListener(bindings = @QueueBinding(value = @Queue,exchange = @Exchange(name = "topic_logs", declare = "false"),key = {"*.orange.*"})) public void receive1(String s) throws Exception { System.out.println("receiver1 - received: "+s); } @RabbitListener(bindings = @QueueBinding(value = @Queue, exchange = @Exchange(name = "topic_logs", declare = "false"),key = {"*.*.rabbit","lazy.#"})) public void receive2(String s) throws Exception { System.out.println("receiver2 - received: "+s); } }
Test class
package cn.tedu.m5; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class TopicTests { @Autowired TopicSender sender; @Test void test1() throws Exception { sender.send(); } }
RPC asynchronous call
main program
Two queues are defined in the main program
- Queue to send call information: rpc_queue
- Queue that returns results: randomly named
package cn.tedu.m6; import java.util.UUID; import org.springframework.amqp.core.Queue; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } @Bean public Queue sendQueue() { return new Queue("rpc_queue",false); } @Bean public Queue rndQueue() { return new Queue(UUID.randomUUID().toString(), false); } }
Server
From rpc_queue receives the call data, performs the operation, calculates the Fibonacci number, and returns the calculation result
@Rabbitlistener annotation for methods with return values:
- The replyTo attribute is automatically obtained
- Get correlationId property automatically
- Send the calculation result to the queue specified by replyTo attribute, and carry the correlationId attribute
package cn.tedu.m6; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class RpcServer { @RabbitListener(queues = "rpc_queue") public long getFbnq(int n) { return f(n); } private long f(int n) { if (n==1 || n==2) { return 1; } return f(n-1) + f(n-2); } }
client
Get random queue name using SPEL expression: '#{rndQueue.name}'
When sending call data, the random queue name and correlationId are carried
Receive the call result from the random queue and get the correlationId
package cn.tedu.m6; import java.util.UUID; import org.springframework.amqp.AmqpException; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessagePostProcessor; import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.support.AmqpHeaders; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; @Component public class RpcClient { @Autowired AmqpTemplate t; @Value("#{rndQueue.name}") String rndQueue; public void send(int n) { // When sending the call information, set the message properties through the pre message processor, and add the return queue name and association id t.convertAndSend("rpc_queue", (Object)n, new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { MessageProperties p = message.getMessageProperties(); p.setReplyTo(rndQueue); p.setCorrelationId(UUID.randomUUID().toString()); return message; } }); } //Receive calculation results from random queue @RabbitListener(queues = "#{rndQueue.name}") public void receive(long r, @Header(name=AmqpHeaders.CORRELATION_ID) String correlationId) { System.out.println("\n\n"+correlationId+" - received: "+r); } }
Test class
package cn.tedu.m6; import java.util.Scanner; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class TopicTests { @Autowired RpcClient client; @Test void test1() throws Exception { while (true) { System.out.print("Find the Fibonacci number: "); int n = new Scanner(System.in).nextInt(); client.send(n); } } }