1、RabbitMQ與Spring的框架整合之Spring Boot實戰。
首先創建maven項目的RabbitMQ的消息生產者rabbitmq-springboot-provider項目,配置pom.xml配置文件,如下所示:
1 <project xmlns="http://maven.apache.org/POM/4.0.0" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 4 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 <groupId>com.bie</groupId> 7 <artifactId>rabbitmq-springboot-provider</artifactId> 8 <version>0.0.1-SNAPSHOT</version> 9 10 <parent> 11 <groupId>org.springframework.boot</groupId> 12 <artifactId>spring-boot-starter-parent</artifactId> 13 <version>2.0.2.RELEASE</version> 14 <relativePath /> <!-- lookup parent from repository --> 15 </parent> 16 17 <properties> 18 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 19 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 20 <java.version>1.8</java.version> 21 </properties> 22 23 <dependencies> 24 <dependency> 25 <groupId>org.springframework.boot</groupId> 26 <artifactId>spring-boot-starter</artifactId> 27 </dependency> 28 29 <dependency> 30 <groupId>org.springframework.boot</groupId> 31 <artifactId>spring-boot-starter-test</artifactId> 32 <scope>test</scope> 33 </dependency> 34 <dependency> 35 <groupId>org.springframework.boot</groupId> 36 <artifactId>spring-boot-starter-amqp</artifactId> 37 </dependency> 38 </dependencies> 39 40 <build> 41 <plugins> 42 <plugin> 43 <groupId>org.springframework.boot</groupId> 44 <artifactId>spring-boot-maven-plugin</artifactId> 45 </plugin> 46 </plugins> 47 </build> 48 49 </project>
修改rabbitmq-springboot-provider的配置文件application.yml,如下所示:
1 spring: 2 rabbitmq: 3 addresses: 192.168.110.133:5672 # rabbitmq服務器的ip地址和端口號 4 username: guest # rabbitmq服務器的賬號 5 password: guest # rabbitmq服務器的密碼 6 virtual-host: / # rabbitmq服務器的虛擬主機 7 connection-timeout: 8 15000 # rabbitmq服務器連接超時時間 9 # publisher-confirms,實現一個監聽器用於監聽Broker端給我們返回的確認請求。RabbitTemplate.ConfirmCallback。 10 publisher-confirms: true # 消息確認模式 11 # publisher-returns,保證消息對Broker端是可達的,如果出現路由鍵不可達的情況, 12 # 則使用監聽器對不可達的消息進行後續處理,保證消息的路由成功,RabbitTemplate.ReturnCallback 13 publisher-returns: true # 消息返回模式 14 template: 15 mandatory: true # 配置mandatory=true保證監聽有效。 16
創建配置類,可以將bean添加到容器中,開啓註解掃描。
1 package com.bie.config; 2 3 import org.springframework.context.annotation.ComponentScan; 4 import org.springframework.context.annotation.Configuration; 5 6 /** 7 * 8 * @author biehl 9 * 10 */ 11 @Configuration // 配置類,可以將bean添加到容器中 12 @ComponentScan(basePackages = { "com.bie.*" }) // 掃描包註解 13 public class RabbitMQProducerConfig { 14 15 }
創建實體類,用於測試消息的發送。
1 package com.bie.po; 2 3 import java.io.Serializable; 4 5 /** 6 * 7 * @author biehl 8 * 9 */ 10 public class Order implements Serializable { 11 12 /** 13 * 14 */ 15 private static final long serialVersionUID = 1L; 16 17 private String id; 18 private String name; 19 20 public String getId() { 21 return id; 22 } 23 24 public void setId(String id) { 25 this.id = id; 26 } 27 28 public String getName() { 29 return name; 30 } 31 32 public void setName(String name) { 33 this.name = name; 34 } 35 36 public Order(String id, String name) { 37 super(); 38 this.id = id; 39 this.name = name; 40 } 41 42 @Override 43 public String toString() { 44 return "Order [id=" + id + ", name=" + name + "]"; 45 } 46 47 public Order() { 48 super(); 49 } 50 51 }
創建RabbitMQ的生產者,用於消息的發送。
1 package com.bie.producer; 2 3 import java.text.SimpleDateFormat; 4 import java.util.Date; 5 import java.util.Map; 6 import java.util.UUID; 7 8 import org.springframework.amqp.rabbit.core.RabbitTemplate; 9 import org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback; 10 import org.springframework.amqp.rabbit.core.RabbitTemplate.ReturnCallback; 11 import org.springframework.amqp.rabbit.support.CorrelationData; 12 import org.springframework.beans.factory.annotation.Autowired; 13 import org.springframework.messaging.Message; 14 import org.springframework.messaging.MessageHeaders; 15 import org.springframework.messaging.support.MessageBuilder; 16 import org.springframework.stereotype.Component; 17 18 import com.bie.po.Order; 19 20 /** 21 * 22 * @author biehl 23 * 24 */ 25 @Component 26 public class RabbitMQProducerMessage { 27 28 @Autowired 29 private RabbitTemplate rabbitTemplate; 30 31 // publisher-confirms,實現一個監聽器用於監聽Broker端給我們返回的確認請求。RabbitTemplate.ConfirmCallback。 32 // publisher-returns,保證消息對Broker端是可達的,如果出現路由鍵不可達的情況,則使用監聽器對不可達的消息進行後續處理,保證消息的路由成功,RabbitTemplate.ReturnCallback 33 // 注意:在發送消息的時候對template進行配置mandatory=true保證監聽有效。 34 // 生產端還可以配置其他屬性,比如發送重試、超時時間、次數、間隔等等。 35 36 // 回調函數,confirm確認 37 final ConfirmCallback confirmCallback = new ConfirmCallback() { 38 39 @Override 40 public void confirm(CorrelationData correlationData, boolean ack, String cause) { 41 System.out.println("correlationData : " + correlationData); 42 System.out.println("ack : " + ack); 43 if (!ack) { 44 System.out.println("異常處理,將後續繼續處理......."); 45 } 46 System.out.println(); 47 } 48 49 }; 50 51 // 回調函數,return返回 52 final ReturnCallback returnCallback = new ReturnCallback() { 53 54 @Override 55 public void returnedMessage(org.springframework.amqp.core.Message message, int replyCode, String replyText, 56 String exchange, String routingKey) { 57 System.err.println("return exchange: " + exchange + ", routingKey: " + routingKey + ", replyCode: " 58 + replyCode + ", replyText: " + replyText); 59 } 60 61 }; 62 63 /** 64 * 發送消息的方法 65 * 66 * @param message 67 * @param properties 68 */ 69 public void send(Object message, Map<String, Object> properties) { 70 // 設置消息頭信息 71 MessageHeaders messageHeaders = new MessageHeaders(properties); 72 // 創建消息 73 Message msg = MessageBuilder.createMessage(message, messageHeaders); 74 // 消息確認和消息返回機制的回調 75 rabbitTemplate.setConfirmCallback(confirmCallback); 76 rabbitTemplate.setReturnCallback(returnCallback); 77 // id + 時間戳的格式,保證全局唯一性 78 CorrelationData correlationData = new CorrelationData(); 79 String id = UUID.randomUUID().toString(); 80 // 唯一性id,做ack可靠性投遞的時候、補償策略的時候,根據該id可以找到唯一條消息。 81 correlationData.setId(id); 82 String exchange = "exchange-1"; // 交換機名稱。需要自己創建好該交換機,然後創建一個隊列,使用路由鍵將該交換機和隊列進行綁定即可。 83 String routingkey = "springboot.helloRabbitmq"; // 路由鍵 84 rabbitTemplate.convertAndSend(exchange, routingkey, msg, correlationData); 85 } 86 87 /** 88 * 89 * @param order 90 */ 91 public void sendOrder(Order order) { 92 // 消息確認和消息返回機制的回調 93 rabbitTemplate.setConfirmCallback(confirmCallback); 94 rabbitTemplate.setReturnCallback(returnCallback); 95 // id + 時間戳的格式,保證全局唯一性 96 CorrelationData correlationData = new CorrelationData(); 97 String id = UUID.randomUUID().toString(); 98 correlationData.setId(id); 99 rabbitTemplate.convertAndSend("exchange-1", "springboot.def", order, correlationData); 100 } 101 102 }
創建主啓動類,進行項目的啓動,如下所示:
1 package com.bie; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 6 /** 7 * 8 * @author biehl 9 * 10 */ 11 @SpringBootApplication 12 public class SpringBootRabbitMQProviderApplication { 13 14 public static void main(String[] args) { 15 SpringApplication.run(SpringBootRabbitMQProviderApplication.class, args); 16 } 17 18 }
創建生產者的測試類,進行生產者消息的發送。
1 package com.bie.springboot; 2 3 import java.text.SimpleDateFormat; 4 import java.util.Date; 5 import java.util.HashMap; 6 import java.util.Map; 7 8 import org.junit.Test; 9 import org.junit.runner.RunWith; 10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.boot.test.context.SpringBootTest; 12 import org.springframework.test.context.junit4.SpringRunner; 13 14 import com.bie.po.Order; 15 import com.bie.producer.RabbitMQProducerMessage; 16 17 @RunWith(SpringRunner.class) 18 @SpringBootTest 19 public class ApplicationTests { 20 21 @Autowired 22 private RabbitMQProducerMessage rabbitMQProducerMessage; 23 24 private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 25 26 @Test 27 public void rabbitMQProducerMessage() throws Exception { 28 Map<String, Object> properties = new HashMap<>(); 29 properties.put("idCard", "410725195545815685x"); 30 properties.put("createdate", simpleDateFormat.format(new Date())); 31 for (int i = 0; i < 1000; i++) { 32 rabbitMQProducerMessage.send("Hello RabbitMQ For Spring Boot!" + i, properties); 33 } 34 35 // 線程休眠,消息ack確認 36 // Thread.sleep(500000); 37 } 38 39 @Test 40 public void rabbitMQProducerOrder() throws Exception { 41 Order order = new Order("001", "第一個訂單"); 42 rabbitMQProducerMessage.sendOrder(order); 43 } 44 45 }
生產者發送消息,可以在RabbitMQ的管控臺進行觀察效果的。上面這種方式,需要手動創建交換機,隊列,以及使用路由鍵將交換機和隊列進行綁定。可以在管控臺進行交換機、隊列、以及使用路由鍵將交換機和隊列進行綁定的。
2、首先創建maven項目的RabbitMQ的消息消費者rabbitmq-springboot-consumer項目,配置pom.xml配置文件,如下所示:
1 <project xmlns="http://maven.apache.org/POM/4.0.0" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 4 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 <groupId>com.bie</groupId> 7 <artifactId>rabbitmq-springboot-consumer</artifactId> 8 <version>0.0.1-SNAPSHOT</version> 9 10 <parent> 11 <groupId>org.springframework.boot</groupId> 12 <artifactId>spring-boot-starter-parent</artifactId> 13 <version>2.0.2.RELEASE</version> 14 <relativePath /> <!-- lookup parent from repository --> 15 </parent> 16 17 <properties> 18 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 19 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 20 <java.version>1.8</java.version> 21 </properties> 22 23 <dependencies> 24 <dependency> 25 <groupId>org.springframework.boot</groupId> 26 <artifactId>spring-boot-starter</artifactId> 27 </dependency> 28 29 <dependency> 30 <groupId>org.springframework.boot</groupId> 31 <artifactId>spring-boot-starter-test</artifactId> 32 <scope>test</scope> 33 </dependency> 34 <dependency> 35 <groupId>org.springframework.boot</groupId> 36 <artifactId>spring-boot-starter-amqp</artifactId> 37 </dependency> 38 </dependencies> 39 40 <build> 41 <plugins> 42 <plugin> 43 <groupId>org.springframework.boot</groupId> 44 <artifactId>spring-boot-maven-plugin</artifactId> 45 </plugin> 46 </plugins> 47 </build> 48 49 </project>
由於生產者端和消費者端是分項目開發的,但是配置類RabbitMQProducerConfig和實體類Order都一樣,主啓動類修改一下名稱即可,這裏就省略了。
1 package com.bie.consumer; 2 3 import java.util.Map; 4 5 import org.springframework.amqp.rabbit.annotation.Exchange; 6 import org.springframework.amqp.rabbit.annotation.Queue; 7 import org.springframework.amqp.rabbit.annotation.QueueBinding; 8 import org.springframework.amqp.rabbit.annotation.RabbitHandler; 9 import org.springframework.amqp.rabbit.annotation.RabbitListener; 10 import org.springframework.amqp.support.AmqpHeaders; 11 import org.springframework.messaging.Message; 12 import org.springframework.messaging.handler.annotation.Headers; 13 import org.springframework.messaging.handler.annotation.Payload; 14 import org.springframework.stereotype.Component; 15 16 import com.bie.po.Order; 17 import com.rabbitmq.client.Channel; 18 19 /** 20 * 21 * @author biehl 22 * 23 * 1、簽收模式,首先配置手動確認模式,用於ack的手工處理,這樣我們可以保證消息的可靠性送達, 24 * 或者在消費端消費失敗的時候可以做到重回隊列,根據業務記錄日誌等處理。 25 * 26 * 2、可以設置消費端的監聽個數和最大個數,用於控制消費端的併發情況 27 * 28 * 3、消費端最重要的就是註解@RabbitListener註解的使用。消息端監聽註解。 29 * 30 * 該註解是一個組合註解,裏面可以註解配置@QueueBinding、@Queue、@Exchange。 31 * 32 * 直接通過這個組合註解一次性搞定消費端交換機、隊列、綁定、路由、並且配置監聽功能等等。 33 * 34 */ 35 @Component 36 public class RabbitMQConsumerMessage { 37 38 @RabbitListener(bindings = @QueueBinding( 39 40 value = @Queue(value = "${order.queue.name}", durable = "${order.queue.durable}"), 41 42 exchange = @Exchange(value = "${order.exchange.name}", durable = "${order.exchange.durable}", type = "${order.exchange.type}", ignoreDeclarationExceptions = "${order.exchange.ignoreDeclarationExceptions}"), 43 44 key = "${order.key}" 45 46 ) 47 48 ) 49 @RabbitHandler 50 public void onMessage(Message message, Channel channel) throws Exception { 51 System.out.println("消費者: " + message.getPayload()); 52 Long deliveryTag = (Long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG); 53 // 手工ack 54 channel.basicAck(deliveryTag, false); 55 } 56 57 /** 58 * 59 * order.queue.name=queue-2 order.queue.durable=true 60 * order.exchange.name=exchange-1 order.exchange.durable=true 61 * order.exchange.type=topic order.exchange.ignoreDeclarationExceptions=true 62 * order.key=springboot.* 63 * 64 * @param order 65 * @param channel 66 * @param headers 67 * @throws Exception 68 */ 69 @RabbitListener(bindings = @QueueBinding( 70 71 value = @Queue(value = "${order.queue.name}", durable = "${order.queue.durable}"), 72 73 exchange = @Exchange(value = "${order.exchange.name}", durable = "${order.exchange.durable}", type = "${order.exchange.type}", ignoreDeclarationExceptions = "${order.exchange.ignoreDeclarationExceptions}"), 74 75 key = "${order.key}" 76 77 ) 78 79 ) 80 @RabbitHandler // @Payload指定實際消息體內容,可以定義到形參上。 81 public void onOrderMessage(@Payload Order order, Channel channel, @Headers Map<String, Object> headers) 82 throws Exception { 83 System.out.println("消費端order: " + order.getId()); 84 Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG); 85 // 手工ACK 86 channel.basicAck(deliveryTag, false); 87 } 88 89 }
直接啓動消費者的啓動類,然後在生產者測試類開始發送消息,消費端就可以監聽到了消息。
作者:別先生
博客園:https://www.cnblogs.com/biehongli/
如果您想及時得到個人撰寫文章以及著作的消息推送,可以掃描上方二維碼,關注個人公衆號哦。