yls
2020/5/10
Spring Boot整合rabbitmq
rabbitmq的基本概念和其它相關知識請自主去官網學習
rabbitmq官網,
本文只介紹rabbitmq在springboot中如何使用
新增依賴包
<!--rabbitmq客戶端 start-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
<!--rabbitmq客戶端 end-->
新增配置檔案 application.yml
spring:
rabbitmq: #rabbit連線配置資訊
host: 39.97.234.52
port: 5672
username: admin
password: admin
virtual-host: /vhost_1
rabbitmq五種模式的使用
1. 簡單佇列
- 建立消費者
@Component
public class MQ {
/**
* 簡單佇列
* autoDelete = "true" 表示沒有生產者和消費者連線時自動刪除
* durable = "true" 表示佇列持久化,預設就是 true
* @param msg
*/
@RabbitListener(queuesToDeclare = @Queue(value = "simpleQueue",autoDelete = "true", durable = "true"))
public void simpleQueue(String msg) {
System.out.println("接收 " + msg);
}
}
- 建立生產者
@SpringBootTest
public class RabbitTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void simpleQueue() {
rabbitTemplate.convertAndSend("simpleQueue", "this is simpleQueue")
System.out.println("simple success");
}
}
2. 工作佇列,實現了能者多勞
- 建立消費者
@Component
public class MQ {
/**
* 工作佇列,多個消費者消費一個佇列
* <p>
* AMQP預設實現消費者確認模式,原文如下
* It's a common mistake to miss the basicAck and Spring AMQP helps to avoid this through its default configuration.
* The consequences are serious. Messages will be redelivered when your client quits (which may look like random redelivery),
* but RabbitMQ will eat more and more memory as it won't be able to release any unacked messages.
* <p>
* Fair dispatch vs Round-robin dispatching
* 官網說: AMQP預設實現消費者fair轉發,也就是能者多勞,原文如下(應該是說反了,預設的是250,但是是Round-robin dispatching)
* However, "Fair dispatch" is the default configuration for Spring AMQP.
* The AbstractMessageListenerContainer defines the value for DEFAULT_PREFETCH_COUNT to be 250.
* If the DEFAULT_PREFETCH_COUNT were set to 1 the behavior would be the round robin delivery as described above.
*/
//設定消費者的確認機制,並達到能者多勞的效果
@Bean("workListenerFactory")
public RabbitListenerContainerFactory myFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory containerFactory =
new SimpleRabbitListenerContainerFactory();
containerFactory.setConnectionFactory(connectionFactory);
//自動ack,沒有異常的情況下自動傳送ack
//auto 自動確認,預設是auto
//MANUAL 手動確認
//none 不確認,發完自動丟棄
containerFactory.setAcknowledgeMode(AcknowledgeMode.AUTO);
//拒絕策略,true回到佇列 false丟棄,預設是true
containerFactory.setDefaultRequeueRejected(true);
//預設的PrefetchCount是250,採用Round-robin dispatching,效率低
//setPrefetchCount 為 1,即可啟用fair 轉發
containerFactory.setPrefetchCount(1);
return containerFactory;
}
/**
* 若不使用自定義containerFactory = "workListenerFactory",預設的輪詢消費效率低
*
* @param s
*/
@RabbitListener(queuesToDeclare = @Queue("workQueue"), containerFactory = "workListenerFactory")
public void workQueue1(String s) {
System.out.println("workQueue 1 " + s);
}
@RabbitListener(queuesToDeclare = @Queue("workQueue"), containerFactory = "workListenerFactory")
public void workQueue2(String s) throws InterruptedException {
Thread.sleep(1000);
System.out.println("workQueue 2 " + s);
}
}
- 建立生產者
@SpringBootTest
public class RabbitTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void workQueue() {
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("workQueue", i);
}
System.out.println("workQueue success");
}
}
3. 訂閱模式
- 建立消費者
@Component
public class MQ {
/**
* 訂閱模式 fanout
*/
@RabbitListener(bindings = {
@QueueBinding(value = @Queue, //臨時路由
exchange = @Exchange(value = "exchange1", type = ExchangeTypes.FANOUT))
})
public void fanout(String s) {
System.out.println("訂閱模式1 " + s);
}
@RabbitListener(bindings = {
@QueueBinding(value = @Queue, exchange = @Exchange(value = "exchange1", type = ExchangeTypes.FANOUT))
})
public void fanout2(String s) {
System.out.println("訂閱模式2 " + s);
}
}
- 建立生產者
@SpringBootTest
public class RabbitTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void fanOut() {
rabbitTemplate.convertAndSend("exchange1", "", "fan out......");
}
}
4. 路由模式
- 建立消費者
@Component
public class MQ {
/**
* 路由模式 DIRECT
*/
@RabbitListener(bindings = {
@QueueBinding(value = @Queue, //臨時路由
exchange = @Exchange(value = "exchange2", type = ExchangeTypes.DIRECT),
key = {"error", "info"} //路由鍵
)
})
public void router(String s) {
System.out.println("路由模式1 " + s);
}
@RabbitListener(bindings = {
@QueueBinding(value = @Queue,
exchange = @Exchange(value = "exchange2", type = ExchangeTypes.DIRECT),
key = {"error"} //路由鍵
)
})
public void router2(String s) {
System.out.println("路由模式2 " + s);
}
}
- 建立生產者
@SpringBootTest
public class RabbitTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void router() {
rabbitTemplate.convertAndSend("exchange2", "info", "router");
System.out.println("router");
}
}
5. 主題模式
- 建立消費者
@Component
public class MQ {
/**
* topic topics
*/
@RabbitListener(bindings = {
@QueueBinding(value = @Queue,
exchange = @Exchange(value = "exchange3", type = ExchangeTypes.TOPIC),
key = {"user.#"} //路由鍵
)
})
public void topic2(String s) {
System.out.println("topic2 " + s);
}
}
- 建立生產者
@SpringBootTest
public class RabbitTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void topic() {
rabbitTemplate.convertAndSend("exchange3", "user.name", "hhh");
System.out.println("topic");
}
}
預設訊息是持久化的,也可以設定不持久化,以簡單佇列示例
@SpringBootTest
public class RabbitTest {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* AMQP 預設訊息是持久化的,但只有在佇列也是持久化時才有作用,原文如下:
* Messages are persistent by default with Spring AMQP.
* Note the queue the message will end up in needs to be durable as well,
* otherwise the message will not survive a broker restart as a non-durable queue does not itself survive a restart.
* <p>
* MessageProperties類中原始碼如下:
* static {
* DEFAULT_DELIVERY_MODE = MessageDeliveryMode.PERSISTENT;
* DEFAULT_PRIORITY = 0;
* }
* <p>
* 如何設定訊息不持久化?
* 設定訊息不持久化,預設是持久化的,這裡只為記錄如何設定訊息不持久化,一般不設定
* 傳送訊息時,新增 MessagePostProcessor即可,這裡使用 lambda 表示式
* (message) -> {
* message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT);
* return message;
* }
* <p>
* 完整示例如下:
* rabbitTemplate.convertAndSend("simpleQueue", "this is simpleQueue",
* (message) -> {
* message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT);
* return message;
* });
*/
@Test
public void simpleQueue() {
rabbitTemplate.convertAndSend("simpleQueue", "this is simpleQueue",
(message) -> {
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT);
return message;
});
System.out.println("simple success");
}
}
如何設定生產者訊息確認,避免訊息傳送失敗而丟失(確認分為兩步,一是確認是否到達交換器,二是確認是否到達佇列。)
- 在配置檔案中新增
spring:
rabbitmq: #rabbit連線配置資訊
publisher-returns: true #開啟訊息從 交換機----》佇列傳送失敗的回撥
publisher-confirm-type: correlated #開啟訊息從 生產者----》交換機的回撥
- 新增配置類
@Component
public class ProducerConfig {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void pre() {
/**
*
* 訊息傳送到交換機的回撥
*
* public void confirm(CorrelationData correlationData, boolean b, String s) {
*
* System.out.println("訊息唯一標識:"+correlationData);
* System.out.println("確認結果:"+ b);
* System.out.println("失敗原因:"+ s);
* }
*
*/
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
System.out.println("setConfirmCallback-------------------");
System.out.println("correlationData: " + correlationData);
System.out.println(ack);
System.out.println(cause);
if (ack) {
System.out.println("傳送成功");
} else {
System.out.println("傳送失敗");
// 可以記錄下來,也可以重新傳送訊息。。。
}
});
/**
*
* 訊息從交換機傳送到佇列的回撥,只有傳送失敗時才會回撥
* public void returnedMessage(Message message, int i, String s, String s1, String s2) {
* System.out.println("訊息主體 message : "+message);
* System.out.println("訊息主體 message : "+ i);
* System.out.println("描述:"+ s);
* System.out.println("訊息使用的交換器 exchange : "+ s1);
* System.out.println("訊息使用的路由鍵 routing : "+ s2);
* }
*/
rabbitTemplate.setReturnCallback((Message message, int replyCode, String replyText, String exchange, String routingKey) -> {
System.out.println("setReturnCallback---------------------");
System.out.println("訊息主體 message : " + message);
System.out.println("響應碼 replyCode: " + replyCode);
System.out.println("響應內容 replyText:" + replyText);
System.out.println("訊息使用的交換器 exchange : " + exchange);
System.out.println("訊息使用的路由鍵 routeKey : " + routingKey);
//也可以重新傳送訊息
rabbitTemplate.convertAndSend(exchange, routingKey, new String(message.getBody()));
System.out.println("重新傳送訊息: -----" + new String(message.getBody()));
});
/**
* 網上都說必須設定rabbitTemplate.setMandatory(true),才能觸發ReturnCallback回撥,
* 我嘗試了一下,並不需要設定為true,交換機傳送訊息給佇列失敗時,也能觸發回撥
*/
//rabbitTemplate.setMandatory(true);
}
}
程式碼github地址:https://github.com/1612480331/Spring-Boot-rabbitmq