Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 中文一区在线,国内一级毛片直播www性,国产超a动作大片

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          干貨:網(wǎng)頁跳轉(zhuǎn)的寫法大全及其用途區(qū)別

          干貨:網(wǎng)頁跳轉(zhuǎn)的寫法大全及其用途區(qū)別

          時(shí)候,我們會希望網(wǎng)頁自動(dòng)跳轉(zhuǎn),應(yīng)用場景包括:

          • 提交表單后自動(dòng)轉(zhuǎn)到另外一個(gè)頁面,
          • 頁面地址變了,希望訪問老地址時(shí)能自動(dòng)轉(zhuǎn)到新地址,等等。

          下面總結(jié)下如何在前端頁面中控制跳轉(zhuǎn)的方法:

          利用html的refresh

          <meta http-equiv="refresh" content="0;url=index.html"> 
          

          其中0表示0秒以后跳轉(zhuǎn),可以自行設(shè)定時(shí)間。

          利用js的href屬性

          window.location.href='index.html';
          

          如果要設(shè)定延遲時(shí)間,則加上setTimeout

          setTimeout("javascript:location.href='index.html'", 5000);
          

          利用js的navigate方式

          window.navigate("index.html");
          

          自動(dòng)刷新頁面

          在上述方式中,如果跳轉(zhuǎn)的頁面就是本頁面,那么就是自動(dòng)刷新頁面的功能。
          

          或者使用reload

          location.reload()
          

          跳轉(zhuǎn)到上一頁,下一頁的方式

          window.history.go(-1);
          

          其中 -1 表示上一頁,如果沒有負(fù)號的就是表示下一頁

          如果不是1而是 2,3,4......n 則表示前進(jìn)或者后退 n 頁

          后退還可以用

          window.history.back();
          

          兩者的區(qū)別是:

          go(-1):返回上一頁,原頁面表單中的內(nèi)容會丟失;

          back():返回上一頁,原頁表表單中的內(nèi)容會保留。

          前進(jìn)則對應(yīng)的是:

          history.forward():
          

          此外,還有一個(gè)參數(shù) history.length 記錄了頁面前進(jìn)的序號,如果等于0表示第一頁

          怎么選擇

          至此,自動(dòng)跳轉(zhuǎn)頁面、刷新頁面、前后切換的方法都齊了!方法多了就有了選擇恐懼癥?

          基本原則:

          單純的頁面跳轉(zhuǎn)建議就用html的refresh方法,無需js代碼,很簡潔。

          如果比較復(fù)雜,涉及js代碼的業(yè)務(wù)功能,再加上跳轉(zhuǎn)功能的,就用js的各種方法。

          此外還要考慮頁面是否刷新的問題,希望刷新就用go,否則用back/forward

          延時(shí)消息(定時(shí)消息)指的在 分布式異步消息場景 下,生產(chǎn)端發(fā)送一條消息,希望在指定延時(shí)或者指定時(shí)間點(diǎn)被消費(fèi)端消費(fèi)到,而不是立刻被消費(fèi)。

          延時(shí)消息適用的業(yè)務(wù)場景非常的廣泛,在分布式系統(tǒng)環(huán)境下,延時(shí)消息的功能一般會在下沉到中間件層,通常是 MQ 中內(nèi)置這個(gè)功能或者內(nèi)聚成一個(gè)公共基礎(chǔ)服務(wù)。

          本文旨在探討常見延時(shí)消息的實(shí)現(xiàn)方案以及方案設(shè)計(jì)的優(yōu)缺點(diǎn)。

          實(shí)現(xiàn)方案

          1. 基于外部存儲實(shí)現(xiàn)的方案

          這里討論的外部存儲指的是在 MQ 本身自帶的存儲以外又引入的其他的存儲系統(tǒng)。

          基于外部存儲的方案本質(zhì)上都是一個(gè)套路,將 MQ 和 延時(shí)模塊 區(qū)分開來,延時(shí)消息模塊是一個(gè)獨(dú)立的服務(wù)/進(jìn)程。延時(shí)消息先保留到其他存儲介質(zhì)中,然后在消息到期時(shí)再投遞到 MQ。當(dāng)然還有一些細(xì)節(jié)性的設(shè)計(jì),比如消息進(jìn)入的延時(shí)消息模塊時(shí)已經(jīng)到期則直接投遞這類的邏輯,這里不展開討論。

          下述方案不同的是,采用了不同的存儲系統(tǒng)。

          基于 數(shù)據(jù)庫(如MySQL)

          基于關(guān)系型數(shù)據(jù)庫(如MySQL)延時(shí)消息表的方式來實(shí)現(xiàn)。

          CREATE TABLE `delay_msg` (
            `id` bigint unsigned NOT NULL AUTO_INCREMENT,
            `delivery_time` DATETIME NOT NULL COMMENT '投遞時(shí)間',
            `payloads` blob COMMENT '消息內(nèi)容',
            PRIMARY KEY (`id`),
            KEY `time_index` (`delivery_time`)
          )

          通過定時(shí)線程定時(shí)掃描到期的消息,然后進(jìn)行投遞。定時(shí)線程的掃描間隔理論上就是你延時(shí)消息的最小時(shí)間精度。

          優(yōu)點(diǎn):

          • 實(shí)現(xiàn)簡單;

          缺點(diǎn):

          • B+Tree索引不適合消息場景的大量寫入;

          基于 RocksDB

          RocksDB 的方案其實(shí)就是在上述方案上選擇了比較合適的存儲介質(zhì)。

          RocksDB 在筆者之前的文章中有聊過,LSM 樹根更適合大量寫入的場景。滴滴開源的DDMQ中的延時(shí)消息模塊 Chronos 就是采用了這個(gè)方案。

          DDMQ 這個(gè)項(xiàng)目簡單來說就是在 RocketMQ 外面加了一層統(tǒng)一的代理層,在這個(gè)代理層就可以做一些功能維度的擴(kuò)展。延時(shí)消息的邏輯就是代理層實(shí)現(xiàn)了對延時(shí)消息的轉(zhuǎn)發(fā),如果是延時(shí)消息,會先投遞到 RocketMQ 中 Chronos 專用的 topic 中。延時(shí)消息模塊 Chronos 消費(fèi)得到延時(shí)消息轉(zhuǎn)出到 RocksDB,后面就是類似的邏輯了,定時(shí)掃描到期的消息,然后往 RocketMQ 中投遞。

          這個(gè)方案老實(shí)說是一個(gè)比較重要的方案。因?yàn)榛?RocksDB 來實(shí)現(xiàn)的話,從數(shù)據(jù)可用性的角度考慮,你還需要自己去處理多副本的數(shù)據(jù)同步等邏輯。

          優(yōu)點(diǎn):

          • RocksDB LSM 樹很適合消息場景的大量寫入;

          缺點(diǎn):

          • 實(shí)現(xiàn)方案較重,如果你采用這個(gè)方案,需要自己實(shí)現(xiàn) RocksDB 的數(shù)據(jù)容災(zāi)邏輯;

          基于 Redis

          再來聊聊 Redis 的方案。下面放一個(gè)比較完善的方案。

          本方案來源于: https://www.cnblogs.com/lylife/p/7881950.html

          • Messages Pool 所有的延時(shí)消息存放,結(jié)構(gòu)為KV結(jié)構(gòu),key為消息ID,value為一個(gè)具體的message(這里選擇Redis Hash結(jié)構(gòu)主要是因?yàn)閔ash結(jié)構(gòu)能存儲較大的數(shù)據(jù)量,數(shù)據(jù)較多時(shí)候會進(jìn)行漸進(jìn)式rehash擴(kuò)容,并且對于HSET和HGET命令來說時(shí)間復(fù)雜度都是O(1))
          • Delayed Queue是16個(gè)有序隊(duì)列(隊(duì)列支持水平擴(kuò)展),結(jié)構(gòu)為ZSET,value 為 messages pool中消息ID,score為過期時(shí)間**(分為多個(gè)隊(duì)列是為了提高掃描的速度)**
          • Worker 代表處理線程,通過定時(shí)任務(wù)掃描 Delayed Queue 中到期的消息

          這個(gè)方案選用 Redis 存儲在我看來有以下幾點(diǎn)考慮,

          • Redis ZSET 很適合實(shí)現(xiàn)延時(shí)隊(duì)列
          • 性能問題,雖然 ZSET 插入是一個(gè) O(logn) 的操作,但是Redis 基于內(nèi)存操作,并且內(nèi)部做了很多性能方面的優(yōu)化。

          但是這個(gè)方案其實(shí)也有需要斟酌的地方,上述方案通過創(chuàng)建多個(gè) Delayed Queue 來滿足對于并發(fā)性能的要求,但這也帶來了多個(gè) Delayed Queue 如何在多個(gè)節(jié)點(diǎn)情況下均勻分配,并且很可能出現(xiàn)到期消息并發(fā)重復(fù)處理的情況,是否要引入分布式鎖之類的并發(fā)控制設(shè)計(jì)?

          在量不大的場景下,上述方案的架構(gòu)其實(shí)可以蛻化成主從架構(gòu),只允許主節(jié)點(diǎn)來處理任務(wù),從節(jié)點(diǎn)只做容災(zāi)備份。實(shí)現(xiàn)難度更低更可控。

          定時(shí)線程檢查的缺陷與改進(jìn)

          上述幾個(gè)方案中,都通過線程定時(shí)掃描的方案來獲取到期的消息。

          定時(shí)線程的方案在消息量較少的時(shí)候,會浪費(fèi)資源,在消息量非常多的時(shí)候,又會出現(xiàn)因?yàn)閽呙栝g隔設(shè)置不合理導(dǎo)致延時(shí)時(shí)間不準(zhǔn)確的問題。可以借助 JDK Timer 類中的思想,通過 wait-notify 來節(jié)省 CPU 資源。

          獲取中最近的延時(shí)消息,然后wait(執(zhí)行時(shí)間-當(dāng)前時(shí)間),這樣就不需要浪費(fèi)資源到達(dá)時(shí)間時(shí)會自動(dòng)響應(yīng),如果有新的消息進(jìn)入,并且比我們等待的消息還要小,那么直接notify喚醒,重新獲取這個(gè)更小的消息,然后又wait,如此循環(huán)。

          2. 開源 MQ 中的實(shí)現(xiàn)方案

          再來講講目前自帶延時(shí)消息功能的開源MQ,它們是如何實(shí)現(xiàn)的

          RocketMQ

          RocketMQ 開源版本支持延時(shí)消息,但是只支持 18 個(gè) Level 的延時(shí),并不支持任意時(shí)間。只不過這個(gè) Level 在 RocketMQ 中可以自定義的,所幸來說對普通業(yè)務(wù)算是夠用的。默認(rèn)值為“1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”,18個(gè)level。

          通俗地講,設(shè)定了延時(shí) Level 的消息會被暫存在名為 SCHEDULE_TOPIC_XXXX 的topic中,并根據(jù) level 存入特定的queue,queueId=delayTimeLevel – 1,**即一個(gè)queue只存相同延時(shí)的消息,保證具有相同發(fā)送延時(shí)的消息能夠順序消費(fèi)。**broker會調(diào)度地消費(fèi)SCHEDULE_TOPIC_XXXX,將消息寫入真實(shí)的topic。

          下面是整個(gè)實(shí)現(xiàn)方案的示意圖,紅色代表投遞延時(shí)消息,紫色代表定時(shí)調(diào)度到期的延時(shí)消息:

          優(yōu)點(diǎn):

          • Level 數(shù)固定,每個(gè) Level 有自己的定時(shí)器,開銷不大
          • 將 Level 相同的消息放入到同一個(gè) Queue 中,保證了同一 Level 消息的順序性;不同 Level 放到不同的 Queue 中,保證了投遞的時(shí)間準(zhǔn)確性;
          • 通過只支持固定的Level,將不同延時(shí)消息的排序變成了固定Level Topic 的追加寫操作

          缺點(diǎn):

          • Level 配置的修改代價(jià)太大,固定 Level 不靈活
          • CommitLog 會因?yàn)檠訒r(shí)消息的存在變得很大

          Pulsar

          Pulsar 支持“任意時(shí)間”的延時(shí)消息,但實(shí)現(xiàn)方式和 RocketMQ 不同。

          通俗的講,Pulsar 的延時(shí)消息會直接進(jìn)入到客戶端發(fā)送指定的 Topic 中,然后在堆外內(nèi)存中創(chuàng)建一個(gè)基于時(shí)間的優(yōu)先級隊(duì)列,來維護(hù)延時(shí)消息的索引信息。延時(shí)時(shí)間最短的會放在頭上,時(shí)間越長越靠后。在進(jìn)行消費(fèi)邏輯時(shí)候,再判斷是否有到期需要投遞的消息,如果有就從隊(duì)列里面拿出,根據(jù)延時(shí)消息的索引查詢到對應(yīng)的消息進(jìn)行消費(fèi)。

          如果節(jié)點(diǎn)崩潰,在這個(gè) broker 節(jié)點(diǎn)上的 Topics 會轉(zhuǎn)移到其他可用的 broker 上,上面提到的這個(gè)優(yōu)先級隊(duì)列也會被重建。

          下面是 Pulsar 公眾號中對于 Pulsar 延時(shí)消息的示意圖。

          乍一看會覺得這個(gè)方案其實(shí)非常簡單,還能支持任意時(shí)間的消息。但是這個(gè)方案有幾個(gè)比較大的問題

          • **內(nèi)存開銷:**維護(hù)延時(shí)消息索引的隊(duì)列是放在堆外內(nèi)存中的,并且這個(gè)隊(duì)列是以訂閱組(Kafka中的消費(fèi)組)為維度的,比如你這個(gè) Topic 有 N 個(gè)訂閱組,那么如果你這個(gè) Topic 使用了延時(shí)消息,就會創(chuàng)建 N 個(gè) 隊(duì)列;并且隨著延時(shí)消息的增多,時(shí)間跨度的增加,每個(gè)隊(duì)列的內(nèi)存占用也會上升。(是的,在這個(gè)方案下,支持任意的延時(shí)消息反而有可能讓這個(gè)缺陷更嚴(yán)重)
          • **故障轉(zhuǎn)移之后延時(shí)消息索引隊(duì)列的重建時(shí)間開銷:**對于跨度時(shí)間長的大規(guī)模延時(shí)消息,重建時(shí)間可能會到小時(shí)級別。(摘自 Pulsar 官方公眾號文章)
          • 存儲開銷 :延時(shí)消息的時(shí)間跨度會影響到 Pulsar 中已經(jīng)消費(fèi)的消息數(shù)據(jù)的空間回收。打個(gè)比方,你的 Topic 如果業(yè)務(wù)上要求支持一個(gè)月跨度的延時(shí)消息,然后你發(fā)了一個(gè)延時(shí)一個(gè)月的消息,那么你這個(gè) Topic 中底層的存儲就會保留整整一個(gè)月的消息數(shù)據(jù),即使這一個(gè)月中99%的正常消息都已經(jīng)消費(fèi)了。

          對于前面第一點(diǎn)和第二點(diǎn)的問題,社區(qū)也設(shè)計(jì)了解決方案,在隊(duì)列中加入時(shí)間分區(qū),Broker 只加載當(dāng)前較近的時(shí)間片的隊(duì)列到內(nèi)存,其余時(shí)間片分區(qū)持久化磁盤,示例圖如下圖所示:

          但是目前,這個(gè)方案并沒有對應(yīng)的版本。可以在實(shí)際使用時(shí),規(guī)定只能使用較小時(shí)間跨度的延時(shí)消息,來減少前兩點(diǎn)缺陷的影響。

          至于第三個(gè)方案,估計(jì)是比較難解決的,需要在數(shù)據(jù)存儲層將延時(shí)消息和正常消息區(qū)分開來,單獨(dú)存儲延時(shí)消息。

          QMQ

          QMQ提供任意時(shí)間的延時(shí)/定時(shí)消息,你可以指定消息在未來兩年內(nèi)(可配置)任意時(shí)間內(nèi)投遞。

          把 QMQ 放到最后,是因?yàn)槲矣X得 QMQ 是目前開源 MQ 中延時(shí)消息設(shè)計(jì)最合理的。里面設(shè)計(jì)的核心簡單來說就是 多級時(shí)間輪 + 延時(shí)加載 + 延時(shí)消息單獨(dú)磁盤存儲

          如果對時(shí)間輪不熟悉的可以閱讀筆者的這篇文章 從 Kafka 看時(shí)間輪算法設(shè)計(jì)

          QMQ的延時(shí)/定時(shí)消息使用的是兩層 hash wheel 來實(shí)現(xiàn)的。第一層位于磁盤上,每個(gè)小時(shí)為一個(gè)刻度(默認(rèn)為一個(gè)小時(shí)一個(gè)刻度,可以根據(jù)實(shí)際情況在配置里進(jìn)行調(diào)整),每個(gè)刻度會生成一個(gè)日志文件(schedule log),因?yàn)镼MQ支持兩年內(nèi)的延時(shí)消息(默認(rèn)支持兩年內(nèi),可以進(jìn)行配置修改),則最多會生成 2 * 366 * 24=17568 個(gè)文件(如果需要支持的最大延時(shí)時(shí)間更短,則生成的文件更少)。 第二層在內(nèi)存中,當(dāng)消息的投遞時(shí)間即將到來的時(shí)候,會將這個(gè)小時(shí)的消息索引(索引包括消息在schedule log中的offset和size)從磁盤文件加載到內(nèi)存中的hash wheel上,內(nèi)存中的hash wheel則是以500ms為一個(gè)刻度 。

          總結(jié)一下設(shè)計(jì)上的亮點(diǎn):

          • 時(shí)間輪算法適合延時(shí)/定時(shí)消息的場景,省去延時(shí)消息的排序,插入刪除操作都是 O(1) 的時(shí)間復(fù)雜度;
          • 通過多級時(shí)間輪設(shè)計(jì),支持了超大時(shí)間跨度的延時(shí)消息;
          • 通過延時(shí)加載,內(nèi)存中只會有最近要消費(fèi)的消息,更久的延時(shí)消息會被存儲在磁盤中,對內(nèi)存友好;
          • 延時(shí)消息單獨(dú)存儲(schedule log),不會影響到正常消息的空間回收;

          總結(jié)

          本文匯總了目前業(yè)界常見的延時(shí)消息方案,并且討論了各個(gè)方案的優(yōu)缺點(diǎn)。希望對讀者有所啟發(fā)。

          原文 https://ricstudio.top/archives/delay-msg-designs

          • 應(yīng)用場景
          • 消息延遲推送的實(shí)現(xiàn)
          • 測試結(jié)果

          應(yīng)用場景

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

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

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

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

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

          在 RabbitMQ 3.6.x 之前我們一般采用死信隊(duì)列+TTL過期時(shí)間來實(shí)現(xiàn)延遲隊(duì)列,我們這里不做過多介紹,可以參考之前文章來了解:TTL、死信隊(duì)列

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

          首先我們創(chuàng)建交換機(jī)和消息隊(duì)列,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<>();
           //設(shè)置交換機(jī)支持延遲消息推送
           //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 的聲明中可以設(shè)置exchange.setDelayed(true)來開啟延遲隊(duì)列,也可以設(shè)置為以下內(nèi)容傳入交換機(jī)聲明的方法中,因?yàn)榈谝环N方式的底層就是通過這種方式來實(shí)現(xiàn)的。

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

          發(fā)送消息時(shí)我們需要指定延遲推送的時(shí)間,我們這里在發(fā)送消息的方法中傳入?yún)?shù) new MessagePostProcessor() 是為了獲得 Message對象,因?yàn)樾枰柚?Message對象的api 來設(shè)置延遲時(shí)間。

          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 + 時(shí)間戳 全局唯一
           CorrelationData correlationData=new CorrelationData("12345678909"+new Date());
           //發(fā)送消息時(shí)指定 header 延遲時(shí)間
           rabbitTemplate.convertAndSend(MQConfig.LAZY_EXCHANGE, "lazy.boot", message,
           new MessagePostProcessor() {
           @Override
           public Message postProcessMessage(Message message) throws AmqpException {
           //設(shè)置消息持久化
           message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
           //message.getMessageProperties().setHeader("x-delay", "6000");
           message.getMessageProperties().setDelay(6000);
           return message;
           }
           }, correlationData);
           }
          }
          

          我們可以觀察 setDelay(Integer i)底層代碼,也是在 header 中設(shè)置 s-delay。等同于我們手動(dòng)設(shè)置 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);
           }
          }
          消費(fèi)端進(jìn)行消費(fèi)
          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é)果

          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:

          轉(zhuǎn)載請注明出處,謝謝。https://www.cnblogs.com/haixiang/p/10966985.html


          主站蜘蛛池模板: 久久久精品人妻一区二区三区蜜桃 | 亚洲视频一区二区在线观看| 久久一区二区三区免费播放| 精品乱码一区二区三区在线| 亚欧在线精品免费观看一区| 久久国产精品最新一区| 中文字幕一区二区视频| 国产福利一区视频| 亚洲乱色熟女一区二区三区蜜臀| 免费无码一区二区| 精品一区二区三区影院在线午夜 | 无码av免费一区二区三区| 国产精品一区在线观看你懂的| 日本一区二区三区爆乳| 亚拍精品一区二区三区| 亚洲图片一区二区| 末成年女A∨片一区二区| 波多野结衣AV无码久久一区| 国产波霸爆乳一区二区| 香蕉一区二区三区观| 红杏亚洲影院一区二区三区| 国产福利电影一区二区三区久久久久成人精品综合 | 久久国产精品一区免费下载| 久久精品国产免费一区| 国产激情无码一区二区| 无码日韩精品一区二区免费暖暖| 高清一区二区三区免费视频| 无码人妻精品一区二区| 精品无码一区二区三区爱欲九九 | 日美欧韩一区二去三区| 精品91一区二区三区| 一区二区三区福利视频| 中文字幕亚洲一区二区va在线| 真实国产乱子伦精品一区二区三区| 久久国产精品免费一区| 无码人妻精品一区二区三区东京热| 日产精品久久久一区二区| 波多野结衣一区二区| 色精品一区二区三区| 精品亚洲综合在线第一区| 精品黑人一区二区三区|