给Hangfire的webjob增长callback和动态判断返回结果功能设计

 

背景介绍

一般业务中须要用到定时执行功能,我用hangfire搭建了一个调度服务,这个调度服务是独立于业务逻辑的,具体能够参考文章:https://github.com/yuzd/Hangfire.HttpJob/wikigit

也就是说只要我有了这个调度服务后,只要提供给个人接口 我就能够调度它(好比在xx点xx分运行,或者每隔xx分运行,或者每周一8点运行等等)。github

可是有一个问题,对方的接口是调用成功仍是失败彻底取决于对方的接口设计!web

有的接口被设计成 请求的StatusCode 是200的是表明接口成功,非200的表明接口失败。ajax

有的接口被设计成返回的json结构有一个特定的字段来表明接口调用成功仍是失败,例如:success字段。好比返回的结构大概这样子: {"success":false,"data":"xxxx"}json

等等,这些都是case by case ,不一样的写接口的人定的规则可能不同,经过webjob的调用方式如何动态的验证成功仍是失败呢?c#

如上面提到动态的验证结果,我分了2种状况处理:要么看Response返回的statuscode,要么是看返回的结果里面的指定字段来判断!app

 

1. 根据Response返回的statuscode

statuscode很好作,我在设计Hangfire.HttpJob这个扩展插件时是能够在外部设置一个验证委托lua

默认的返回的statuscode 小于 400 则认为http请求是成功的,不然失败spa

image

 

2. 返回的结果里面的指定字段来判断

我采用EL表达式来实现的,EL表达式将请求的返回体设置为json变量,而后在表达式中能够直接以属性的方式到值,表达式返回布尔类型。插件

针对不一样的接口,我能够设置一个独立的表达式来进行判断!

 

首先在job添加的时候设置el表达式!以下图

 

  

 

如何写EL表达式:(很简单,返回的结构体是什么字段就能够用什么字段)

接口的返回体是一个string。我先将这个String转成json类型(dynamic)

而后是采用Spring.EL表达式实现的。 CallbackEL表达式的返回类型是布尔类型

返回体在表达式里面是有下面2个变量:

  • #resultBody 是返回体的 string
  • #result 是返回体的 json体(根据上面转的,若是上面是非json格式的那就不能使用这个变量了)

好比说我调用的httpjob 返回体是

{"Success":false,"Info":"test"}

那么我能够这么写

"CallbackEL": "#result.Success"

也能够这么写

"CallbackEL": "#result.Info.Equals('ok')"

 

表达式如何运行的?

Spring.EL是我从Spring.Net里面剥离出来的一个组件,能够从nuget里面引用,支持net45和netstandard2.0

          //检查是否有设置EL表达式
                if (!string.IsNullOrEmpty(item.CallbackEL))
                {
                    var elResult = InvokeSpringElCondition(item.CallbackEL, result, context, new Dictionary<string, object> { { "resultBody", result } });
                    if (!elResult)
                    {
                        throw new HttpStatusCodeException(item.CallbackEL, result);
                    }
                    RunWithTry(() => context.WriteLine($"【{Strings.CallbackELExcuteResult}:Ok 】" + item.CallbackEL));
                }

 

       /// <summary>
        /// 用EL表达式动态判断是否执行成功
        /// </summary>
        /// <returns></returns>
        private static bool InvokeSpringElCondition(string placeholder,string result, PerformContext context,Dictionary<string, object> param)
        {
            try
            {
                try
                {
                    param["result"] = JsonConvert.DeserializeObject<ExpandoObject>(result);
                }
                catch (Exception)
                {
                    //ignore
                }

                var parameterValue = ExpressionEvaluator.GetValue(null, placeholder, param);
               
                return (bool)parameterValue;
            }
            catch (Exception e)
            {
                context.WriteLine($"【{Strings.CallbackELExcuteError}】" + placeholder);
                context.WriteLine(e);
                return false;
            }
        }

 

 
调用对象 ExpressionEvaluator 传 Dictionary<string, object> param 做为参数,使用#参数来引用。若是你的参数是string 那么能够写c#中string的全部方法好比 StarsWith,EndsWith,Equals 等等
若是你的参数类型是一个dynamic,那你就能够直接像使用js的对象属性同样

Callback功能设计

举例:

咱们调用了A接口,若是A接口成功咱们想把A接口的返回值做为请求参数再去调用B接口。

若是A接口失败在调用C接口通知错误!

 

参考ajax的callback设计

image

如上图 能够自行添加 Success 或者 Fail 做为回调

若是定义了Success 那么父job执行成功没有报错则运行 Success回调

若是定义了Fail 那么父job执行失败 则运行 Fail回调

Success 里面还能够定义 Success 和 Fail

Fail 里面还能够定义 Success 和 Fail 以下图:

image

回调的Json参数

字段 说明
Url 请求Url
Method Post,Get
Data Post时能够填,支持占位符(具体请看下面的介绍)
ContentType application/json
Timeout 超时(毫秒)
BasicUserName basicauth用户名
BasicPassword basicauth密码
AgentClass 基于jobAgent开发的httpjob须要填
Headers key:value 的jsonstring "{"key":,"value"}"

回调执行的逻辑

注意:回调不是做为新的的HangfireHttpJob执行的,是依附在最顶级的父Job的!

举例:

image

若是:JobA -》 Fail B -> Success BB

JobA自己执行错误的话则会走重试逻辑(若是开启重试的话),重试到顶后 进入 Fail B, Fail B 执行成功 则进入 Success BB。若是Success BB 执行成功,那么 JobA 则认为是成功的,不然认为失败!

若是:JobA -》 Fail B -> Fail BB

JobA自己执行错误的话则会走重试逻辑(若是开启重试的话),重试到顶后 进入 Fail B, Fail B 执行失败 则进入 Fail BB 。Fail BB 执行失败,那么 JobA 认为失败!

Fail B 执行成功 进入 Success C, Success C,执行成功 认为 JobA 认为成功,不然 Job A 认为失败!

总结:若是回调 则会按照设置的回调一路走下去,看最后一个回调是否成功。若是成功 则认为整个链路执行成功,不然认为失败!

回调的代码实现是一个递归的方式调用

 

 

 

为了实现回调可以把上一个运行的结果最为参数,开发了占位符(placeholder)功能

也为了更好的扩展占位符功能,

首先要介绍下 dashbord里面的 全局配置 功能 以下图:

  • 全局配置 :存储在当前目录下的 hangfire_global.json 文件(能够在StartUp代码修改HangfireHttpJobOptions.GlobalSettingJsonFilePath值指定其余地方

这个功能为了介绍重复的配置,能够集中配置一些参数,而后给各个job去使用!

image

image

占位符功能采用Spring.EL表达式实现的。

字符串中placeholder替换逻辑

  • 第一步:把字符串中的 ${xxx} 的xxx所有替换成 全局配置里面的值
  • 第二步:把字符串中的 #{yyy} 的yyy所有按照SpringEL表达式逻辑运行后的值进行替换

好比:上图中你在全局配置了一个参数叫test

Data:"你好呀:${test}"

在运行时会被替换成 =》    你好呀:1

例如:使用父job的返回值传给 callback

image

若是运行失败传给callback是报错信息

例如:使用时间替换

能够直接在 #{} 方法里面用DateTime这个变量 这个变量和c#同样的功能

好比

  • #{DateTime.Now} 表明运行时的当前时间+时分秒
  • #{DateTime.Today} 表明运行时的当天
  • #{DateTime.Today.AddDays(-1)} 表明运行时的昨天
  • #{DateTime.Today.AddDays(1)} 表明运行时的明天

image

总结:

以上 hangfire的webjob调度扩展组件(https://github.com/yuzd/Hangfire.HttpJob/wiki)

已经很是灵活了,基于hangfire的核心调度功能,加上webjob的调用方式,很方便的把业务逻辑分离出来!

无论业务接口如何写,基于EL表达式均可以准确的判断出来执行成功仍是失败,根据回调功能很方便的执行链式调用和错误通知!

相关文章
相关标签/搜索