天天看點

給Hangfire的webjob增加callback和動态判斷傳回結果功能設計

背景介紹

通常業務中需要用到定時執行功能,我用hangfire搭建了一個排程服務,這個排程服務是獨立于業務邏輯的,具體可以參考文章:https://github.com/yuzd/Hangfire.HttpJob/wiki

也就是說隻要我有了這個排程服務後,隻要提供給我的接口 我就可以排程它(比如在xx點xx分運作,或者每隔xx分運作,或者每周一8點運作等等)。

但是有一個問題,對方的接口是調用成功還是失敗完全取決于對方的接口設計!

有的接口被設計成 請求的StatusCode 是200的是代表接口成功,非200的代表接口失敗。

有的接口被設計成傳回的json結構有一個特定的字段來代表接口調用成功還是失敗,例如:success字段。比如傳回的結構大概這樣子: {"success":false,"data":"xxxx"}

等等,這些都是case by case ,不同的寫接口的人定的規則可能不一樣,通過webjob的調用方式如何動态的驗證成功還是失敗呢?

如上面提到動态的驗證結果,我分了2種情況處理:要麼看Response傳回的statuscode,要麼是看傳回的結果裡面的指定字段來判斷!

1. 根據Response傳回的statuscode

statuscode很好做,我在設計Hangfire.HttpJob這個擴充插件時是可以在外部設定一個驗證委托

預設的傳回的statuscode 小于 400 則認為http請求是成功的,否則失敗

給Hangfire的webjob增加callback和動态判斷傳回結果功能設計

2. 傳回的結果裡面的指定字段來判斷

我采用EL表達式來實作的,EL表達式将請求的傳回體設定為json變量,然後在表達式中可以直接以屬性的方式到值,表達式傳回布爾類型。

針對不同的接口,我可以設定一個獨立的表達式來進行判斷!

首先在job添加的時候設定el表達式!如下圖

給Hangfire的webjob增加callback和動态判斷傳回結果功能設計

如何寫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設計

給Hangfire的webjob增加callback和動态判斷傳回結果功能設計

如上圖 可以自行添加 Success 或者 Fail 作為回調

如果定義了Success 那麼父job執行成功沒有報錯則運作 Success回調

如果定義了Fail 那麼父job執行失敗 則運作 Fail回調

Success 裡面還可以定義 Success 和 Fail

Fail 裡面還可以定義 Success 和 Fail 如下圖:

給Hangfire的webjob增加callback和動态判斷傳回結果功能設計

回調的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的!

給Hangfire的webjob增加callback和動态判斷傳回結果功能設計

如果: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 認為失敗!

總結:如果回調 則會按照設定的回調一路走下去,看最後一個回調是否成功。如果成功 則認為整個鍊路執行成功,否則認為失敗!

回調的代碼實作是一個遞歸的方式調用

給Hangfire的webjob增加callback和動态判斷傳回結果功能設計

為了實作回調能夠把上一個運作的結果最為參數,開發了占位符(placeholder)功能

也為了更好的擴充占位符功能,

首先要介紹下 dashbord裡面的 全局配置 功能 如下圖:

  • 全局配置 :存儲在目前目錄下的 hangfire_global.json 檔案(可以在StartUp代碼修改HangfireHttpJobOptions.GlobalSettingJsonFilePath值指定其他地方

這個功能為了介紹重複的配置,可以集中配置一些參數,然後給各個job去使用!

給Hangfire的webjob增加callback和動态判斷傳回結果功能設計
給Hangfire的webjob增加callback和動态判斷傳回結果功能設計

占位符功能采用Spring.EL表達式實作的。

字元串中placeholder替換邏輯

  • 第一步:把字元串中的 ${xxx} 的xxx全部替換成 全局配置裡面的值
  • 第二步:把字元串中的 #{yyy} 的yyy全部按照SpringEL表達式邏輯運作後的值進行替換

比如:上圖中你在全局配置了一個參數叫test

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

在運作時會被替換成 =》    你好呀:1

例如:使用父job的傳回值傳給 callback

給Hangfire的webjob增加callback和動态判斷傳回結果功能設計

如果運作失敗傳給callback是報錯資訊

例如:使用時間替換

可以直接在 #{} 方法裡面用DateTime這個變量 這個變量和c#一樣的功能

比如

  • #{DateTime.Now} 代表運作時的目前時間+時分秒
  • #{DateTime.Today} 代表運作時的當天
  • #{DateTime.Today.AddDays(-1)} 代表運作時的昨天
  • #{DateTime.Today.AddDays(1)} 代表運作時的明天
給Hangfire的webjob增加callback和動态判斷傳回結果功能設計

總結:

以上 hangfire的webjob排程擴充元件(https://github.com/yuzd/Hangfire.HttpJob/wiki)

已經非常靈活了,基于hangfire的核心排程功能,加上webjob的調用方式,很友善的把業務邏輯分離出來!

不管業務接口如何寫,基于EL表達式都可以準确的判斷出來執行成功還是失敗,根據回調功能很友善的執行鍊式調用和錯誤通知!

如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”将是我最大的寫作動力!歡迎各位轉載,轉載文章之後須在文章頁面明顯位置給出作者和原文連接配接,謝謝。