一個比較麻煩的限流需求

又是火星人發表於2024-06-07

主要環節

1、前端接受使用者請求,傳送給後端建立任務;
2、後端接受請求後,建立任務記錄(初始狀態),將記錄傳送到訊息佇列;
2、消費者拿到任務後,將任務置為running,然後向外部平臺傳送請求,並向延時佇列(5分鐘)傳送訊息;
3、外部平臺收到請求後,(正常)會在2分鐘內向我方的接收介面提交結果,也可能(5分鐘)超時無響應;
4、我方接收介面收到響應後,完成任務記錄的後續工作,成功--輸出結果,失敗--置為失敗;
5、若外部平臺未能在時限內返回結果,延時佇列負責將任務置為失敗

實現細節:

1、使用訊息佇列是為了避免使用定時任務,定時任務間隔不好把控,多個定時任務程序還會導致任務的重複消費;
2、由於外部平臺的併發限制為n,如果消費者例項數為x,每個例項併發為y,則必須保證全域性併發數為x * y = n;
3、消費執行緒有唯一的consumerTag,可利用consumerTag建立redisson鎖,獲取鎖才進行任務消費(儲存consumerTag),收到外部平臺的反饋後解鎖;
4、根據1-2-3的步驟,在上一個訊息完成或過期之前,當前訊息的消費執行緒會等待鎖,保證了x * y = n;
5、redisson是可重入鎖,需要使用tryLockAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId)傳入隨機的threadId,否則每次都能立刻重入上一次請求的鎖,限流目的無法達到。
6、如果超時未能收到外部平臺的反饋,可以透過設定leaseTime自動釋放鎖,如果不設定leaseTime,只要客戶端不失去連線, 鎖會由看門狗自動續期持有;
7、延時佇列的主要工作是將任務置為失敗,是必不可少的,也可以由它來實現鎖的釋放。

其他主體

1、rabbitmq
2、springboot的rabbitmq客戶端
3、redisson
4、延時佇列

相關文章