【LongIntervalRetries】讓我們來實現支付寶的異步回調方案


功能說明

LongIntervalRetries是基於Quartz.Net的一個長時間間隔重試的類庫,其主要解決何時執行以及執行結果反饋的問題。

產生的原因

簡單的說,我們提供了一系列的API供第三方調用,但因為實際API對應的業務處理時間較長,所以為了增加吞吐量,實際的業務邏輯並沒包含在API服務器,而是分布在了不同的服務器上進行處理,在業務處理結束后,再通過調用第三方提供的回調Url通知處理結果,為了保證回調正確,那么我們需要有這么一個策略:如果回調失敗,我們需要在指定時間間隔之后再次回調,如此反復直至回調成功或者達到重復次數上限。上述回調方案是不是很熟悉?嗯,好吧,直白的講,回調這一部分我們借鑒(抄襲)了支付寶支付時的回調方案,所以我們要解決的,就是通過代碼來實現回調這部分的業務場景,於是也就有了LongIntervalRetries

為什么不使用Polly

Polly是.NET基金會下的彈性和瞬態故障處理庫,其解決的問題天然符合我們回調的業務場景,但為什么在此處卻不被采用呢,原因如下:

  • Polly的重試機制是個短時間內的機制,在其重試機制時間內,當前Task一般是通過await阻塞的,而對於我們的場景來說,這明顯是不合適的,我們的場景並不應該發生在當前線程內
  • Polly並不支持程序重啟時的重試恢復,這一點在我們的業務場景中及其重要,總不能服務器重啟后,我們還沒回調成功的業務就全部丟了吧

設計思路及演變

一開始我們的設計思路非常簡單,就是如何定時觸發回調這個業務代碼,但之后發現,為什么我們要僅限於回調呢?回調只是一個業務場景,但我們完全有可能有其它業務場景,恰恰我們也的確存在這樣的業務場景,我們需要向第三方服務商進行一些請求,該請求同樣耗時較長,該場景是不是很熟悉?只不過與我們作為服務商不同,該服務商居然沒提供回調方案,它需要我們自己定時去回調!而同樣是這個第三方,其業務請求參數具有相當的定制性,其需要我們預先做很多業務性的預處理,也就是需要做一些順序性的工作后,我們才能得到完整的請求參數。
於是我們的設計思路開始調整,最終得出該封裝應當具備的功能點:

  • 其內部應該封裝掉如何定時觸發這個功能
  • 其應該具備同時支持多種業務策略(策略模式)
  • 其應該允許設置什么時候來觸發要執行的策略
  • 其應該支持服務啟動時自動恢復未結束任務的能力
  • 其應該具備最終執行結果通知的能力

為什么會考慮基於Quartz.Net

無論是定時觸發,還是業務策略,以及設置觸發時間,這些都很明顯的具備Job特性,而Quartz.Net本身就是一個Job類庫,而且其本身允許進行並發線程數量設置,如果基於它,明顯我們可以不用考慮線程相關的問題,這可以省掉我們很大的工作量

類庫相關

該類庫在github上的地址為:https://github.com/fdstar/LongIntervalRetries
該類庫目前為v1.0.0版本,其nuget地址為:https://www.nuget.org/packages/LongIntervalRetries/

快速使用

此處僅是簡單的代碼示例,后續會有詳細的使用說明
首先我們需要聲明Job

    public class SomeJob: IJob
    {
        public virtual Task Execute(IJobExecutionContext context)
        {
            return Task.FromResult(1);//默認LongIntervalRetries是通過Job是否產生異常來判斷是否執行成功的
        }
    }

然后我們可以將這個Job注冊到LongIntervalRetries,同時設置重試策略,以及注冊事件監控執行結果,完整的示例如下

var retry = new StdRetry();
//聲明並注冊重試規則
string simpleRuleName = "SimpleRepeatRetryRule";
var simpleRepeatRule = new SimpleRepeatRetryRule(simpleRuleName, 5, TimeSpan.FromSeconds(2));
retry.RuleManager.AddRule(simpleRepeatRule);
var registerInfo = new RetryJobRegisterInfo
{
    //指定要采用的重試規則,如果不設置,則默認使用已注冊的第一項
    UsedRuleName = simpleRuleName,
    //需要傳遞給IJob的上下文數據
    JobMap = new Dictionary<string, object>
    {
        {"SomeKey","SomeValue" }
    },
    //開始執行時間,如果不指定則表示立刻執行
    StartAt = DateTimeOffset.UtcNow.AddSeconds(3),
};
//注冊要執行的Job
retry.RegisterJob<SomeJob>(registerInfo);
//注冊每次Job執行后的通知事件
retry.RegisterEvent<SomeJob>(e =>
{//Some code
});
retry.Start();//啟動Quartz服務
//啟動服務后仍可以RegisterJob、RegisterEvent


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM