<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 8878字,需瀏覽 18分鐘

           ·

          2021-09-13 22:25

          來源 | https://www.cnblogs.com/haixiang/p/10966985.html

          目前常見的應用軟件都有消息的延遲推送的影子,應用也極為廣泛,例如:

          • 淘寶七天自動確認收貨。在我們簽收商品后,物流系統(tǒng)會在七天后延時發(fā)送一個消息給支付系統(tǒng),通知支付系統(tǒng)將款打給商家,這個過程持續(xù)七天,就是使用了消息中間件的延遲推送功能。
          • 12306 購票支付確認頁面。我們在選好票點擊確定跳轉的頁面中往往都會有倒計時,代表著 30 分鐘內訂單不確認的話將會自動取消訂單。其實在下訂單那一刻開始購票業(yè)務系統(tǒng)就會發(fā)送一個延時消息給訂單系統(tǒng),延時30分鐘,告訴訂單系統(tǒng)訂單未完成,如果我們在30分鐘內完成了訂單,則可以通過邏輯代碼判斷來忽略掉收到的消息。

          在上面兩種場景中,如果我們使用下面兩種傳統(tǒng)解決方案無疑大大降低了系統(tǒng)的整體性能和吞吐量:

          • 使用 redis 給訂單設置過期時間,最后通過判斷 redis 中是否還有該訂單來決定訂單是否已經完成。這種解決方案相較于消息的延遲推送性能較低,因為我們知道 redis 都是存儲于內存中,我們遇到惡意下單或者刷單的將會給內存帶來巨大壓力。
          • 使用傳統(tǒng)的數(shù)據(jù)庫輪詢來判斷數(shù)據(jù)庫表中訂單的狀態(tài),這無疑增加了IO次數(shù),性能極低。
          • 使用 jvm 原生的 DelayQueue ,也是大量占用內存,而且沒有持久化策略,系統(tǒng)宕機或者重啟都會丟失訂單信息。

          消息延遲推送的實現(xiàn)

          在 RabbitMQ 3.6.x 之前我們一般采用死信隊列+TTL過期時間來實現(xiàn)延遲隊列,我們這里不做過多介紹。

          在 RabbitMQ 3.6.x 開始,RabbitMQ 官方提供了延遲隊列的插件,可以下載放置到 RabbitMQ 根目錄下的 plugins 下。

          圖片

          首先我們創(chuàng)建交換機和消息隊列,application.properties 中配置與上一篇文章相同。

          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<String, Object> pros = new HashMap<>();
                  //設置交換機支持延遲消息推送
                  //pros.put("x-delayed-message""topic");
                  TopicExchange exchange = new TopicExchange(LAZY_EXCHANGE, truefalse, 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 的聲明中可以設置exchange.setDelayed(true)來開啟延遲隊列,也可以設置為以下內容傳入交換機聲明的方法中,因為第一種方式的底層就是通過這種方式來實現(xiàn)的。

          //Map<String, Object> pros = new HashMap<>();
                  //設置交換機支持延遲消息推送
                  //pros.put("x-delayed-message""topic");
                  TopicExchange exchange = new TopicExchange(LAZY_EXCHANGE, truefalse, pros);

          發(fā)送消息時我們需要指定延遲推送的時間,我們這里在發(fā)送消息的方法中傳入參數(shù) 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。等同于我們手動設置 header

          message.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()));

              }
              ```

          ## 測試結果[#](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 + ":");
              }
          }

          果然在 6 秒后收到了消息 lazy receive hello spring boot:


          — 【 THE END 】—
          本公眾號全部博文已整理成一個目錄,請在公眾號里回復「m」獲取!

          最近面試BAT,整理一份面試資料Java面試BATJ通關手冊,覆蓋了Java核心技術、JVM、Java并發(fā)、SSM、微服務、數(shù)據(jù)庫、數(shù)據(jù)結構等等。

          獲取方式:點“在看”,關注公眾號并回復 PDF 領取,更多內容陸續(xù)奉上。

          文章有幫助的話,在看,轉發(fā)吧。

          謝謝支持喲 (*^__^*)

          瀏覽 58
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天操天天舔 | 成人红色三级片网站 | 亚洲第一色播 | 色综合天天| 国产精品爽爽久久久久久 |