前言:在電商系統中,可能有這樣一個需求,訂單下單之後30分鐘後,如果用戶沒有付錢,則系統自動取消訂單。如果用常規的定時器定時去查詢,這會造成很大的消耗(頻繁訪問數據庫)。
這裏選擇RabbitMQ來實現類似的功能(使用隊列的TTL特性)
1.這種模式大概流程,我們需要將消息先發送到ttl
延遲隊列內,當消息到達過期時間後會自動轉發到ttl
隊列內配置的轉發Exchange
以及RouteKey
綁定的隊列內完成消息消費。
2.添加maven依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>ncy>
3.yml配置
spring:
#rabbitmq消息隊列配置信息
rabbitmq:
host: localhost
port: 5672
username: root
password: root
#消息發送和接收確認
publisher-confirms: true
publisher-returns: true
listener:
direct:
acknowledge-mode: manual
simple:
acknowledge-mode: manual
server:
port: 9999
4.創建一個常量類,存放隊列和交換機名字--QueueContent
/**
* @Auther: cjw
* @Date: 2018/6/28 17:17
* @Description:
*/
public class QueueContent {
/**
* 普通消息通知隊列名稱
*/
public static final String MESSAGE_QUEUE_NAME="message.ordinary.queue";
/**
* ttl(延時)消息通知隊列名稱
*/
public static final String MESSAGE_TTL_QUEUE_NAME="message.ttl.queue";
/**
* 普通交換機名稱
*/
public static final String DIRECT_EXCHANGE_NAME="message.ordinary.exchange";
/**
* ttl(延時)交換機名稱
*/
public static final String TOPIC_EXCHANGE_NAME="message.ttl.exchange";
}
5.建立一個隊列枚舉類-QueueEnum
@Getter
public enum QueueEnum {
/**
* 消息通知隊列
*/
MESSAGE_QUEUE(QueueContent.DIRECT_EXCHANGE_NAME, QueueContent.MESSAGE_QUEUE_NAME, QueueContent.MESSAGE_QUEUE_NAME),
/**
* 消息通知ttl隊列
*/
MESSAGE_TTL_QUEUE(QueueContent.TOPIC_EXCHANGE_NAME, QueueContent.MESSAGE_TTL_QUEUE_NAME, QueueContent.MESSAGE_TTL_QUEUE_NAME);
/**
* 交換名稱
*/
private String exchange;
/**
* 隊列名稱
*/
private String name;
/**
* 路由鍵
*/
private String routeKey;
QueueEnum(String exchange, String name, String routeKey) {
this.exchange = exchange;
this.name = name;
this.routeKey = routeKey;
}
public String getExchange() {
return exchange;
}
public void setExchange(String exchange) {
this.exchange = exchange;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRouteKey() {
return routeKey;
}
public void setRouteKey(String routeKey) {
this.routeKey = routeKey;
}
}
6.建立隊列配置類-RabbitMqConfiguration
@Configuration
public class RabbitMqConfiguration {
/**
* 普通消息交換機配置
*
* @return
*/
@Bean
DirectExchange messageDirect() {
return (DirectExchange) ExchangeBuilder
.directExchange(QueueEnum.MESSAGE_QUEUE.getExchange())
.durable(true)
.build();
}
/**
* 延時消息交換機配置
*
* @return
*/
@Bean
DirectExchange messageTtlDirect() {
return (DirectExchange) ExchangeBuilder
.directExchange(QueueEnum.MESSAGE_TTL_QUEUE.getExchange())
.durable(true)
.build();
}
/**
* 普通消息隊列配置
*
* @return
*/
@Bean
public Queue messageQueue() {
return new Queue(QueueEnum.MESSAGE_QUEUE.getName());
}
/**
* TTL消息隊列配置
*
* @return
*/
@Bean
Queue messageTtlQueue() {
return QueueBuilder
.durable(QueueEnum.MESSAGE_TTL_QUEUE.getName())
// 配置到期後轉發的交換
.withArgument("x-dead-letter-exchange", QueueEnum.MESSAGE_QUEUE.getExchange())
// 配置到期後轉發的路由鍵
.withArgument("x-dead-letter-routing-key", QueueEnum.MESSAGE_QUEUE.getRouteKey())
.build();
}
/**
* 普通隊列和普通交換機的綁定-routekey
*
* @param messageDirect 消息中心交換配置
* @param messageQueue 消息中心隊列
* @return
*/
@Bean
Binding messageBinding(DirectExchange messageDirect, Queue messageQueue) {
return BindingBuilder
.bind(messageQueue)
.to(messageDirect)
.with(QueueEnum.MESSAGE_QUEUE.getRouteKey());
}
/**
* ttl隊列和ttl交換機的綁定-routekey
*
* @param messageTtlQueue
* @param messageTtlDirect
* @return
*/
@Bean
public Binding messageTtlBinding(Queue messageTtlQueue, DirectExchange messageTtlDirect) {
return BindingBuilder
.bind(messageTtlQueue)
.to(messageTtlDirect)
.with(QueueEnum.MESSAGE_TTL_QUEUE.getRouteKey());
}
}
上面就是隊列和交換機的綁定
1.聲明瞭普通消息通知隊列
的相關Exchange
、Queue
、Binding
等配置,將message.ordinary.queue隊列通過路由鍵message.ordinary.queue綁定到了message.ordinary.exchange交換上。
2.聲明瞭延時消息通知隊列
的相關Exchange
、Queue
、Binding
等配置,將message.ttl.queue隊列通過message.ttl.queue路由鍵綁定到了message.ttl.exchange交換上。
7.編寫消息發送---MessageProvider
@Component
public class MessageProvider implements RabbitTemplate.ConfirmCallback {
static Logger logger = LoggerFactory.getLogger(MessageProvider.class);
/**
* RabbitMQ 模版消息實現類
*/
protected RabbitTemplate rabbitTemplate;
public MessageProvider(RabbitTemplate rabbitTemplate){
this.rabbitTemplate = rabbitTemplate;
this.rabbitTemplate.setMandatory(true);
this.rabbitTemplate.setConfirmCallback(this);
}
private String msgPojoStr;
/**
* 發送延遲消息
* @param messageContent
*/
public void sendMessage(MessagePojo messageContent) {
if (messageContent != null){
//這裏用於消費者消費消息的時候處理具體業務
if (StringUtils.isEmpty(messageContent.getClassName())){
logger.error("處理業務的類名不能爲空");
return;
}
messageContent.setMessageId(UUID.randomUUID().toString());
messageContent.setCreateTime(DateUtil.datetoString(new Date()));
String msg = JSON.toJSONString(messageContent);
msgPojoStr = msg;
logger.info("延遲:{}秒寫入消息隊列:{},消息內容:{}", messageContent.getDelay(), QueueEnum.MESSAGE_TTL_QUEUE.getRouteKey(), msg);
// 執行發送消息到指定隊列
CorrelationData correlationData = new CorrelationData(messageContent.getMessageId());
rabbitTemplate.convertAndSend(QueueEnum.MESSAGE_TTL_QUEUE.getExchange(), QueueEnum.MESSAGE_TTL_QUEUE.getRouteKey(), msg, message -> {
// 設置延遲毫秒值
message.getMessageProperties().setExpiration(String.valueOf(messageContent.getDelay()*1000));
return message;
},correlationData);
}else {
logger.warn("消息內容爲空!!!!!");
}
}
/**
* 發送確認
*/
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
System.out.println(" 回調id:" + correlationData);
if (b) {
System.out.println(msgPojoStr+":消息發送成功");
} else {
logger.warn(msgPojoStr+":消息發送失敗:" + s);
}
}
}
8.消息實體--MessagePojo
@Data
public class MessagePojo implements Serializable {
//定時過期時間(單位:秒)馬上消費,設置爲0
private int delay;
//處理類名(必填項)
private String className;
//消息參數
private Map<String, Object> params;
private String createTime;
private String messageId;
public MessagePojo() {
}
public MessagePojo(int delay, String className,Map<String, Object> params) {
this.delay = delay;
this.className = className;
this.params = params;
}
}
以上是消息的定義和發送,下一篇講消費者的處理 https://blog.csdn.net/u010096717/article/details/82149936