延時job設計

2021-09-01 16:04:38 字數 2790 閱讀 2906

目前系統系統job任務是每10分鐘重試一次,總共重試7次。所有需要重試的請求都在10分鐘之內堆積,如果請求量過大,這個時間點壓力都有可能落到資料庫上面,從而造成資料庫崩潰。

此外時間配置不夠智慧型化。如果兩小時之後所依賴的第三方系統穩定,而目前的重發機制相當於失效了。 基於上面兩個原因,提出了改進方案,使用延時job實現功能!

通過redis的zset資料型別來儲存資料,score是過期時間,value可以是封裝的記錄流水號。使用mysql來確保資料只能被消費一次。

3.1 插入記錄

實現值的插入很簡單。只需要將待重發資料先插入資料庫,庫里的狀態設定為待重發,然後在存入redis中,記錄初次過期的時間。

3.2 執行延時job

3.2資料補償

如果redis突然掛了,需要做補償措施,可以取表裡面近3天需要重試的記錄放到redis中,讓其再次重發。

1).如果在【插入記錄】資料的時候,插入資料庫報錯,這樣這條資料也無法進入redis中,需要人工處理資料庫異常。

2.) 如果在【插入記錄】插入資料庫成功,插入redis失敗,這個時候通過補償機制,將mysql中的記錄同步到redis中(同步頻率待確認)。

3).在【執行延時job】時,如果redis直接報錯,執行停止。執行停止肯定不可取。解決方案:可以使用乙個狀態量來判斷redis是否正常工作,如果正常工作就執行job,否則,讓執行緒sleep2個小時。

4).在【執行延時job】時,如果呼叫第三方介面網路不通,目前考慮將該次重試記錄入庫(這樣是否妥當?還是說這次請求不算,資料不入庫。),狀態是失敗,出錯訊息提示為網路異常。

5).在【執行延時job】時,刪除redis值失敗,由於redis中還有本來應該刪除的值,會再發一次請求(由於理房通那邊會校驗,再發一次也沒問題)。

6).在【執行延時job】時,入庫異常,庫裡面的資料一直存在。在資料補償的時候資料會放進redis中,就算資料庫掛的時間很長也沒問題。會用資料裡面存的時間加上乙個定值在放到redis中,作為下次要過期的時間。在redis刪除值時,只會刪除那些時間小於當前時間的記錄。

注意:新增資料採用非同步的方式。

5.1 sql

create table `t_trade_time_delay` (

`id` bigint (20) primary key comment '主鍵id', 

`request_id` bigint(20) not null default 0 comment '請求id', 

`current_time` datetime not null default current_timestamp comment '當次時間',

`current_level` int not null default 0 comment '當次級別',

`is_finished` tinyint (1) not null default '0' comment '是否完成,1 完成,以後不需要處理,0未完成,還需要處理', 

key `idx_request_id` (`procurement_request_id`) using btree comment '索引:請求id'

) engine = innodb default charset = utf8mb4 comment = '延時處理表';

5.2 關鍵字段說明

current_time  該字段存每次需要過期的時間

current_level  改字段存每次需要過期時間的等級。

舉例說明,    延時job設定的值為 :

level   time(秒)

1        5

2       30

3       60

4       600(十分鐘)

5       1800(30分鐘)

6        3600(1小時)

7        6*3600(6小時)

8        24*3600(1天)

9       2*24*3600(2天)

在今天10:00:00 將乙個請求編號3003,存入表中,此時 

request_id為3003,current_time 為10:00:05 ,current_level為1,is_finished為 0。

如果時間到了10:00:05,執行了這條記錄,結果還是失敗的,這時

request_id為3003,current_time 為10:00:35 ,current_level為2,is_finished為 0。

依次類推。。。

如果在level為3第三方介面通了。此時

request_id為3003,current_time 為10:01:35 ,current_level為3,is_finished為 1。

1.在生產環境中我們都是集群部署的,所以需要使用分布式鎖來實現資源的控制。可以你們會說,不是使用了redis嗎?它不是天然的適用於redis嗎?沒錯,往redis裡面插入中是沒問題的,但是考慮到我們的業務場景:第一步:從redis裡面獲取值。第二步:通過上一步拿到的值,呼叫下游介面。那麼問題就來了,在第一步的時候是沒有問題的,但是到了第二步,如果沒有鎖住,多個客戶端可能針對於同乙個請求體發多次請求,在下游沒有控制好冪等的情況下就gg了。加了分布式鎖控住第一步和第二步,這個問題就不存在了。

2.在生產中,我們目前的是在程式系統啟動完成之後起乙個執行緒死迴圈消費redis裡面的資料,這裡就需要考慮了,如果網路抖動或者redis先掛後好,我們的執行緒可能就掛了,也就是說雖然專案是一直啟動著,但是redis裡面的資料就是消費不了。我們採用了一種最笨的方案來解決這個問題。做乙個定時任務監控這個執行緒是否一直都是活的,如果執行緒掛了,就新建乙個再次去消費redis。執行緒的監控工具也有很多,jstack,jvisualvm等。

延時訊息大致設計

延時訊息,顧名思義就是傳送訊息後延遲多少時間接收。使用場景舉例,例如使用者買票後,出票後要給使用者發乙個反現金紅包,但是出票一般是非同步出票,所以我們可以設定乙個最大時間,例如30分鐘。在買票30分鐘後,根據出票結果決定是否發反現金紅包。此時就可以使用延時佇列,在使用者購票的時候傳送乙個30分鐘的延...

群延時和相位延時

翻譯自wikipedia 在訊號處理中,群延時是訊號通過裝置各個分量正弦波幅度經歷的延時,因此是各正弦波頻率的函式。相位延時,與幅度的延時相對照是各分量正弦波相位經過裝置的延時。當訊號通過放大器,揚聲器或者傳輸媒介時所有頻率分量會產生延時。不同頻率分量相位延時是不同的除非裝置是線性相位 線性相位和最...

mysql 延時佇列 rabbitmq 延時佇列

前言 某個產品 或者訂單,有個有效期 過了有效期要取消 方法一 寫個指令碼,用crontab 定時掃瞄 改變狀態 但是最低只能一分鐘 不適合 方法二 用swoole得毫秒定時器,每秒鐘去掃瞄表 明顯占用資源 mysql受不了 方法三 用rabbitmq延時佇列 一開始將其丟入mq 死信佇列,設定有效...