• 【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
    
  • 相关阅读:
    activiti5.13工作流系列(一)-初识
    java通过http调用服务
    Eclipse快捷键大全(转载)
    java作用域-转
    ajax两种不同方式的不同结果
    MySQL索引背后的数据结构及算法原理 --转
    解决json包含html标签无法显示的问题
    js下的sleep实现
    json使用
    比较靠谱的网页分页代码-转
  • 原文地址:https://www.cnblogs.com/starfd/p/9198144.html
Copyright © 2020-2023  润新知