面試官:淘寶七天自動確認收貨,可以怎么實現(xiàn)?

淘寶七天自動確認收貨。在我們簽收商品后,物流系統(tǒng)會在七天后延時發(fā)送一個消息給支付系統(tǒng),通知支付系統(tǒng)將款打給商家,這個過程持續(xù)七天,就是使用了消息中間件的延遲推送功能。 12306 購票支付確認頁面。我們在選好票點擊確定跳轉(zhuǎn)的頁面中往往都會有倒計時,代表著 30 分鐘內(nèi)訂單不確認的話將會自動取消訂單。其實在下訂單那一刻開始購票業(yè)務系統(tǒng)就會發(fā)送一個延時消息給訂單系統(tǒng),延時30分鐘,告訴訂單系統(tǒng)訂單未完成,如果我們在30分鐘內(nèi)完成了訂單,則可以通過邏輯代碼判斷來忽略掉收到的消息。
使用 redis 給訂單設置過期時間,最后通過判斷 redis 中是否還有該訂單來決定訂單是否已經(jīng)完成。這種解決方案相較于消息的延遲推送性能較低,因為我們知道 redis 都是存儲于內(nèi)存中,我們遇到惡意下單或者刷單的將會給內(nèi)存帶來巨大壓力。 使用傳統(tǒng)的數(shù)據(jù)庫輪詢來判斷數(shù)據(jù)庫表中訂單的狀態(tài),這無疑增加了IO次數(shù),性能極低。 使用 jvm 原生的 DelayQueue ,也是大量占用內(nèi)存,而且沒有持久化策略,系統(tǒng)宕機或者重啟都會丟失訂單信息。
?
在 RabbitMQ 3.6.x 之前我們一般采用死信隊列+TTL過期時間來實現(xiàn)延遲隊列,我們這里不做過多介紹。

import?org.springframework.amqp.core.*;
import?org.springframework.context.annotation.Bean;
import?org.springframework.context.annotation.Configuration;
import?java.util.HashMap;
import?java.util.Map;
@Configuration
public?class?MQConfig?{
????public?static?final?String?LAZY_EXCHANGE?=?"Ex.LazyExchange";
????public?static?final?String?LAZY_QUEUE?=?"MQ.LazyQueue";
????public?static?final?String?LAZY_KEY?=?"lazy.#";
????@Bean
????public?TopicExchange?lazyExchange(){
????????//Map?pros?=?new?HashMap<>();
????????//設置交換機支持延遲消息推送
????????//pros.put("x-delayed-message",?"topic");
????????TopicExchange?exchange?=?new?TopicExchange(LAZY_EXCHANGE,?true,?false,?pros);
????????exchange.setDelayed(true);
????????return?exchange;
????}
????@Bean
????public?Queue?lazyQueue(){
????????return?new?Queue(LAZY_QUEUE,?true);
????}
????@Bean
????public?Binding?lazyBinding(){
????????return?BindingBuilder.bind(lazyQueue()).to(lazyExchange()).with(LAZY_KEY);
????}
}
exchange.setDelayed(true)來開啟延遲隊列,也可以設置為以下內(nèi)容傳入交換機聲明的方法中,因為第一種方式的底層就是通過這種方式來實現(xiàn)的。//Map
?pros?=?new?HashMap<>();
????????//設置交換機支持延遲消息推送
????????//pros.put("x-delayed-message",?"topic");
????????TopicExchange?exchange?=?new?TopicExchange(LAZY_EXCHANGE,?true,?false,?pros);
new MessagePostProcessor()?是為了獲得?Message對象,因為需要借助?Message對象的api 來設置延遲時間。import?com.anqi.mq.config.MQConfig;
import?org.springframework.amqp.AmqpException;
import?org.springframework.amqp.core.Message;
import?org.springframework.amqp.core.MessageDeliveryMode;
import?org.springframework.amqp.core.MessagePostProcessor;
import?org.springframework.amqp.rabbit.connection.CorrelationData;
import?org.springframework.amqp.rabbit.core.RabbitTemplate;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.stereotype.Component;
import?java.util.Date;
@Component
public?class?MQSender?{
????@Autowired
????private?RabbitTemplate?rabbitTemplate;
????//confirmCallback?returnCallback?代碼省略,請參照上一篇
????public?void?sendLazy(Object?message){
????????rabbitTemplate.setMandatory(true);
????????rabbitTemplate.setConfirmCallback(confirmCallback);
????????rabbitTemplate.setReturnCallback(returnCallback);
????????//id?+?時間戳?全局唯一
????????CorrelationData?correlationData?=?new?CorrelationData("12345678909"+new?Date());
????????//發(fā)送消息時指定?header?延遲時間
????????rabbitTemplate.convertAndSend(MQConfig.LAZY_EXCHANGE,?"lazy.boot",?message,
????????????????new?MessagePostProcessor()?{
????????????@Override
????????????public?Message?postProcessMessage(Message?message)?throws?AmqpException?{
????????????????//設置消息持久化
????????????????message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
????????????????//message.getMessageProperties().setHeader("x-delay",?"6000");
????????????????message.getMessageProperties().setDelay(6000);
????????????????return?message;
????????????}
????????},?correlationData);
????}
}
setDelay(Integer i)底層代碼,也是在 header 中設置 x-delay。等同于我們手動設置 headermessage.getMessageProperties().setHeader("x-delay", "6000");/**
?*?Set?the?x-delay?header.
?*?@param?delay?the?delay.
?*?@since?1.6
?*/
public?void?setDelay(Integer?delay)?{
????if?(delay?==?null?||?delay?0)?{
????????this.headers.remove(X_DELAY);
????}
????else?{
????????this.headers.put(X_DELAY,?delay);
????}
}
import?com.rabbitmq.client.Channel;
import?org.springframework.amqp.rabbit.annotation.*;
import?org.springframework.amqp.support.AmqpHeaders;
import?org.springframework.stereotype.Component;
import?java.io.IOException;
import?java.util.Map;
@Component
public?class?MQReceiver?{
????@RabbitListener(queues?=?"MQ.LazyQueue")
????@RabbitHandler
????public?void?onLazyMessage(Message?msg,?Channel?channel)?throws?IOException{
????????long?deliveryTag?=?msg.getMessageProperties().getDeliveryTag();
????????channel.basicAck(deliveryTag,?true);
????????System.out.println("lazy?receive?"?+?new?String(msg.getBody()));
????}
????```
##?測試結(jié)果[#](https://www.cnblogs.com/haixiang/p/10966985.html#3724420099)
```java
import?org.junit.Test;
import?org.junit.runner.RunWith;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.boot.test.context.SpringBootTest;
import?org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest
@RunWith(SpringRunner.class)
public?class?MQSenderTest?{
????@Autowired
????private?MQSender?mqSender;
????@Test
????public?void?sendLazy()?throws??Exception?{
????????String?msg?=?"hello?spring?boot";
????????mqSender.sendLazy(msg?+?":");
????}
}
lazy receive hello spring boot:作者:海向
來源:https://www.cnblogs.com/haixiang/p/10966985.html

