给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请求是成功的,否则失败

 

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设计

如上图 可以自行添加 Success 或者 Fail 作为回调

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

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

Success 里面还可以定义 Success 和 Fail

Fail 里面还可以定义 Success 和 Fail 如下图:

回调的Json参数

字段说明
Url请求Url
MethodPost,Get
DataPost时可以填,支持占位符(具体请看下面的介绍)
ContentTypeapplication/json
Timeout超时(毫秒)
BasicUserNamebasicauth用户名
BasicPasswordbasicauth密码
AgentClass基于jobAgent开发的httpjob需要填
Headerskey:value 的jsonstring "{"key":,"value"}"

回调执行的逻辑

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

举例:

如果: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去使用!

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

字符串中placeholder替换逻辑

  • 第一步:把字符串中的 ${xxx} 的xxx全部替换成 全局配置里面的值

  • 第二步:把字符串中的 #{yyy} 的yyy全部按照SpringEL表达式逻辑运行后的值进行替换

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

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

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

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

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

例如:使用时间替换

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

比如

  • #{DateTime.Now} 代表运行时的当前时间+时分秒

  • #{DateTime.Today} 代表运行时的当天

  • #{DateTime.Today.AddDays(-1)} 代表运行时的昨天

  • #{DateTime.Today.AddDays(1)} 代表运行时的明天

总结:

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/311719.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++实现双向链表

#include <iostream> using namespace std; typedef int ElemType;typedef struct DuLNode {//节点的创建ElemType data;struct DuLNode *prior, *next; } DuLNode, *DuLinkList;bool InitList(DuLinkList &L) {//初始化L new DuLNode;if (L NULL) {cout << …

word List32

word List32 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

spring aop实例讲解_Spring框架核心知识点

文章内容输出来源&#xff1a;拉勾教育Java高薪训练营前言&#xff1a;由于工作需要提升自身技术能力&#xff0c;在各方比较下&#xff0c;报名了拉勾教育的java高薪训练营&#xff0c;目前已经学了半个月啦&#xff0c;来说说自身学习的感受吧&#xff1a;课程内容有广度更有…

word List 33

word List 33 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

深入理解ASP.NET Core依赖注入

概述ASP.NET Core可以说是处处皆注入&#xff0c;本文从基础角度理解一下原生DI容器&#xff0c;及介绍下怎么使用并且如何替换官方提供的默认依赖注入容器。什么是依赖注入百度百科中对于依赖注入的定义&#xff1a;控制反转&#xff08;Inversion of Control&#xff0c;缩写…

word List 34

word List 34 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

redis api 中文文档_我的Redis学习资料库

最近经常有人问我&#xff0c;我这边学习Redis都有哪些资料&#xff0c;能不能发他们一下。作为一个习惯”慢慢啃”技术学习的人&#xff0c;我确实积累了大量资料&#xff0c;这里可以介绍给大家。注明&#xff1a;本文提供的书籍、视频、源码、文档等&#xff0c;都是非盈利性…

前后端分离架构一直没机会实战?1周完成Vue+Core WebApi移动商城实战(含源码)!...

疫情让企业受到重创&#xff01;就业形势更加严峻&#xff01;前后端分离架构成了当下最高频的招聘需求还没实战过前后端分离&#xff1f;花3分钟阅读本文&#xff0c;带你全面了解前后端分离&#xff0c;轻松面试拿高薪&#xff01;Web发展至今技术非常成熟&#xff0c;主流有…

算法---宝藏最优选择问题

贪心算法—宝藏最优选择问题 原理参考趣学算法 代码&#xff1a; #include"quickSort1.h" 快速排序代码 int quickSort1(int a[], int l, int h) {//快速排序int i l, j h, p a[l];while (i < j) {while (i<j&&a[j]>p) {//从右往左遍历查找比…

C# 的未来:主构造函数

我们上次提到主构造函数是 2014 年&#xff0c;当时&#xff0c; C# 6 和 VB 12 的候选列表上去掉了主构造函数。去年底&#xff0c;主构造函数重新出现&#xff0c;作为提案#2691 &#xff0c;现在成为 C# 9 的一个候选项。主构造函数背后的基本思想是减少初始化类所需的样板代…

python unpack_ip地址处理每天10行python代码系列

背景知识:ip地址(这里都是指的ipv4地址)是一个32位(4字节)的无符号整数。为方便记忆&#xff0c;把每个字节看做一个无符号数&#xff0c;并以.号隔开。比如常见的私有地址192.168.100.1, dns 114.114.114.114。下面这段代码&#xff0c;展示python如何处理ip地址的一些方法。i…

word List35

word List35 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

exceptionhandler注解_SpringMVC 中 @ControllerAdvice 注解的三种使用场景!

ControllerAdvice &#xff0c;很多初学者可能都没有听说过这个注解&#xff0c;实际上&#xff0c;这是一个非常有用的注解&#xff0c;顾名思义&#xff0c;这是一个增强的 Controller。使用这个 Controller &#xff0c;可以实现三个方面的功能&#xff1a;全局异常处理全局…

在家做科研是一种什么样的体验?

匿名提问&#xff1a;题主是 MSRA 的一名实习生&#xff0c;大四。在家咸鱼了半个月&#xff0c;这周打算督促自己继续科研&#xff01;&#xff08;再不火力全开对不起公司不停发的工资和房补&#xff09;。可是在家里似乎很难静下心读 paper&#xff0c;工作五分钟就可以奖励…

word List36

word List36 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

C++实现链式存储二叉树

#include <iostream> using namespace std; typedef char ElemType;typedef struct BiNode {//节点的创建ElemType data;struct BiNode *lchild, *rchild; } BiNode, *BiTree;void PreOrderTraverse(BiTree T) {//先序遍历if (T) {cout << T->data;PreOrderTrav…

37岁跳槽去阿里 p8_36岁阿里p8发帖:手下12个p7,17个p6,亲自写代码毫无中年危机...

关注微信公众号【程序员干货站】&#xff0c;可加入技术交流群~程序员热议的“35 岁中年危机”&#xff0c;其实很多人都没搞懂本质上是因为什么而焦虑。最近看脉脉上有阿里员工发帖称&#xff1a;“今年36岁&#xff0c;p8&#xff0c;手下29个人&#xff0c;12个p7,17个p6&am…

数据结构---判断一棵树是否是二叉搜索树

数据结构—判断一棵树是否是二叉搜索树 代码&#xff1a; #pragma once #define N 100 #define elemType BTree* #include<stdlib.h> typedef struct BTree {int data;struct BTree *lchild, *rchild; }BTree; typedef struct dQueue {elemType data;struct dQueue* ne…

【要闻】Kubernetes安全问题严峻、Linux v5.4安全性浅谈

以下为您奉上今天的开源领域要闻。谷歌提前发布Android 11首个开发者预览版谷歌通常会在三月推出即将发布的Android预览版本。但谷歌今年更早实现了该功能&#xff0c;现已发布了Android 11首个开发者预览版&#xff0c;目前仅支持部分Pixel手机。今年的重点是开发人员部分&…

旧键盘

题目背景 小明的键盘使用年限长&#xff0c;输入时键盘的按键时不时会有粘滞。出现粘滞情况时&#xff0c;上一个被按下的按键会被长按&#xff0c;而字符可能被输入一次或多次。 题目描述 给出小明将要输入的字符串s&#xff0c;和小明实际输入的字符串t&#xff0c;请你判断…