springboot整合rabbitmq實戰,各類模式詳情及實例。
大家好,我是酷酷的韓~
版權聲明:本文爲博主(酷酷的韓)原創文章,未經博主允許不得轉載
一.RabbitMQ簡介.
RabbitMQ是採用Erlang語言實現AMQP(Advanced Message Queuing Protocol,高級消息隊列協議)的消息中間件,它最初起源於金融系統,用於在分佈式系統中存儲轉發消息。(這是百度來的,簡單來說就是用作信息轉發的消息中間件,下面將會從簡介到實例實戰進行分享)
二.什麼是消息中間件?
1.消息中間件是消息傳輸過程中保存消息的容器,提供路由,保證信息傳遞,即使消費者出問題,消息仍然保存一直等到被消費,但保存消息時間也是有期限的。
2.採用異步處理方式:發送消息無需等待反應結果。
3.應用程序之間爲松耦合關係,發送者和接受者不必去了解對方,只需要確認消息,過分些說,發送者和接收者不必同時在線,各自做自己的事情就可以啦。
三.RabbitMQ使用場景?
1.比如註冊賬號時需要發短信或者郵箱短信去認證,此時將發送短信或者郵箱短信發入隊列中,任去消費即可,這是異步處理得一個例子。
2.比如在電商中,訂單和庫存。在傳統模式中,比如提交個訂單,訂單會先記錄信息然後將信息發到庫存,庫存處理完然後才能返回結果,這需要一段較長時間(程序眼中),這時採用rabbitmq中間件,生成訂單不需要再去將信息發到庫存了,只需要拋到隊列中,任去消費即可。節省時間,加強用戶體驗。
3. 流量削峯,在秒殺場景中一般在秒殺活動中應用廣泛,在秒殺活動中一般會因爲流量過大,導致應用掛掉。爲了解決這個問題,一般在應用前端加入消息隊列。用戶請求,服務器收到後,首先寫入消息隊列,加入消息隊列長度超過最大值,則直接拋棄用戶請求跳轉到失敗頁面(這就是大家秒殺失敗的原因,哈哈哈哈哈哈)。
四.RabbitMQ概念說明。
Broker:消息隊列服務器實體。
Exchange:消息轉換機,消息轉發通過這個進行,它指定消息按照什麼規則,路由到哪個隊列。
Queue:消息隊列載體。
Binding:作用是將Exchange和Queue按照路由規則綁定起來,形成某種關係。
Routing key:路由關鍵字,exchange通過這個關鍵字進行消息投遞。
vhost:虛擬主機,一個broker可以開設多個vhost,用作不同用戶的權限分離。
producter:消息生產者,可以投遞消息的程序。
consumer:消息消費者,接受消息的程序。
channel:消息通道,在客戶端每個連接裏,可建立多個channel,每個channel代表一個會話任務。
五.RabbitMQ各類模式詳解。
1.direct(直連)
直連模式 一對一的綁定關係, Routing key(路由關鍵字)和Binding key只有完全匹配,才能消費成功。
2.fanout (廣播)
發送到該交換機的所有信息都將轉發到與該exchange綁定的queue中。
3.topic
在exchange中routing key 和binding key相匹配就可以綁定成功,可以一對一也可一對多。
bingding key 可以存在兩種特殊字符,# :匹配一個或多個單詞。 *:匹配一個單詞。
4.headers
Exchange不依賴於routing key與binding key的匹配規則來路由消息,而是根據發送的消息內容中的headers屬性進行匹配。
六.RabbitMQ各類模式實例。
1.引入pom文件。
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
2.topic爲例(其它的與其模式相同,路由綁定key和綁定key不同)
(1)生產者
發送類:
@Component
@Slf4j
public class TestTopicSender {
/**
* 自動注入RabbitTemplate模板類
*/
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 發送消息方法調用: 構建Message消息
*
* @param message
* @param properties
* @throws Exception
*/
public void send(Object message, Map<String, Object> properties) throws Exception {
MessageHeaders mhs = new MessageHeaders(properties);
Message msg = MessageBuilder.createMessage(message, mhs);
rabbitTemplate.setConfirmCallback(confirmCallback);
rabbitTemplate.setReturnCallback(returnCallback);
//id + 時間戳 全局唯一
CorrelationData correlationData = new CorrelationData(System.currentTimeMillis() + "");
rabbitTemplate.convertAndSend(TestTopicConfig.EXCHANGE, TestTopicConfig.ROUTINGKEY_V1, msg, correlationData);
}
/**
* 回調函數: confirm確認
*/
final RabbitTemplate.ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
log.info("correlationData:{}; ack:{}; cause:{}", correlationData, ack, cause);
if (!ack) {
log.error("回調函數: confirm確認異常correlationData:{}; ack:{}; cause:{}", correlationData, ack, cause);
}
}
};
/**
* 回調函數: return返回
*/
final RabbitTemplate.ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(org.springframework.amqp.core.Message message, int replyCode, String replyText,
String exchange, String routingKey) {
log.info("return exchange: " + exchange + ", routingKey: "
+ routingKey + ", replyCode: " + replyCode + ", replyText: " + replyText);
}
};
配置類:
//交換機名稱
public final static String EXCHANGE = "test-Exchanges";
//路由key
public final static String ROUTINGKEY_V1 = "test-V1.hjq";
public final static String ROUTINGKEY_V2 = "test-V2.hjq";
//隊列名稱
public final static String QUEUE = "queue-test";
//是否持久化
public final static String DERABLE = "true";
//消息路由規則
public final static String TYPE = "topic";
// 忽略聲明異常
public final static String IGNOREDECEXCEPTION = "true";
/**
* 綁定的路由鍵或模式。
* *(星號):可以(只能)匹配一個單詞
* #(井號):可以匹配多個單詞(或者零個)
*/
public final static String KEY_V1 = "test-V1.#";
public final static String KEY_V2 = "test-V2.#";
定時器發送:
@Service
@Slf4j
public class TestScheduleService {
@Autowired
private TestTopicSender testTopicSender;
@Scheduled(cron = "0/10 * * * * ? ")
public void test() {
System.out.println("11");
TestVo testVo = new TestVo();
testVo.setUuid(StrUtil.genUUID());
try {
testTopicSender.send(JSONObject.toJSONString(testVo), null);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
(2)消費者
@Component
@Slf4j
public class TestCustomerRecever {
@Autowired
@Qualifier(value = "dealDataLogTask")
private DealDataLogTask dealDataLogTask;
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = TestTopicConfig.QUEUE,//隊列名稱
durable = TestTopicConfig.DERABLE),//是否持久化
exchange = @Exchange(value = TestTopicConfig.EXCHANGE, //交換機名稱
durable = TestTopicConfig.DERABLE,//是否持久化o
type = TestTopicConfig.TYPE, //消息路由規則
ignoreDeclarationExceptions = TestTopicConfig.IGNOREDECEXCEPTION), // 忽略聲明異常
key = TestTopicConfig.KEY_V1 // 綁定的路由鍵或模式。
)
)
@RabbitHandler
public void onMessage(Message message, Channel channel) throws Exception {
log.info("消費端Payload: " + message.getPayload());
Long deliveryTag = (Long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
//TODO 處理業務邏輯
String msg = (String) message.getPayload();
dealTestMsg(msg);
//手工ACK
channel.basicAck(deliveryTag, false);
}
public void dealTestMsg(String text) {
TestVo testVo = JSON.parseObject(text, TestVo.class);
dealDataLogTask.doTestTask(testVo);
}
配置類:
//交換機名稱
public final static String EXCHANGE = "test-Exchanges";
//路由key
public final static String ROUTINGKEY_V1 = "test-V1.hjq";
public final static String ROUTINGKEY_V2 = "test-V2.hjq";
//隊列名稱
public final static String QUEUE = "queue-test";
//是否持久化
public final static String DERABLE = "true";
//消息路由規則
public final static String TYPE = "topic";
// 忽略聲明異常
public final static String IGNOREDECEXCEPTION = "true";
/**
* 綁定的路由鍵或模式。
* *(星號):可以(只能)匹配一個單詞
* #(井號):可以匹配多個單詞(或者零個)
*/
public final static String KEY_V1 = "test-V1.#";
public final static String KEY_V2 = "test-V2.#";
針對direct模式,在send類中將生產者和消費者路由綁定的key一致,fanout中在同一個exchange中即可。酷酷的韓一直在,有問題留言即可,我會第一時間看到回覆的。
你必須成功,因爲你不能失敗。------酷酷的韓