前言

关于调度,个人用的不太多,更多的重心不在这里,调度自己负责的并不多,或者仅仅是调用下,或者另有替代方案。。。
早期的,有Windows服务方式、Crontab方式等等。现在市场上的产品更为丰富了,功能全面,还带UI,界面也非常友好。。。
所以选择一款适合的应用起来,也没必要再去写一套,直接使用已经非常成熟的产品,方便又快捷。除非要求很高,除非很有必要。。。

经过网罗类似产品:Hangfire、XXL-JOB、ElasticJob、Azkaban、Airflow、DolphinScheduler...

In .NET,涉及到生态及语言环境,综合考量对比后,打算用 Hangfire、DolphinScheduler 这两个产品来一起学习一下。

一、Hangfire

在.NET中,听到的、反馈的、评价的等,第三方的调度产品,Hangfire 热度不低。所以,那么,首先来学习下 Hangfire。
我把官方文档整体看下来,我的理解以及实践应用,写下来,分享给大家。它的内容较简单,如果哪里写错了,提出来让我纠正
Pro 版嘛。。。收费的算了,咱还是按免费的来吧

有时候,我们需要定时完成一些固定的动作,重复性的动作,比如 阶段性的生成报表,比如 定时同步数据等。
有时候,我们需要完成一些任务,不是当前必须的任务,属于流程延展的任务,非及时性的动作。比如 线上购买商品后的短信通知。
有时候,我们需要某种作业顺利完成后的再触发新的作业任务,也就是两个作业任务存在先后关系。等等等等吧。

就譬如,一次性的作业任务、定时重复任务、延迟任务、触发性的...  那么来看看,Hangfire 是如何做到的!

1.1 产品介绍

Hangfire 是一个开源产品,可参考 Github,维护更新频繁,这使得我们在选择产品时很有保障。

Hangfire 是一款基于.NET平台的产品,支持 .NET Framework、.NET Core 等众多不同版本。

Hangfire 是一款集成于项目中的作业调度产品,而非独立运行的产品,我想这也是官方的产品定位。

Hangfire 官网:Hangfire – Background jobs and workers for .NET and .NET Core

Hangfire 也可认证第三方负责维护的扩展包,比如 基于MySQL的存储方式,参考 官方扩展包列表

Hangfire 也有自己的 Dashboard,可以很直观的看出,所有任务的执行状况。

作者:[Sol·wang] - 博客园,原文出处:https://www.cnblogs.com/Sol-wang/

1.2 产品定位

免费开源、基于.NET,集成在项目中,如NuGet方式,由项目代码触发调用,独立的库,多种作业方式,可设定不同的作业队列,互不影响的队列方式,不同的服务实例,共同的 Dashboard。多种存储方式,不仅支持官网提到的MSSQL/Redis等,也包括官网认证的由第三方提供及维护的MYSQL/ORACLE/PostgreSQL等的常见DB。官方提供的存储方式支持更多的功能,如并发设置/限流限速设置。

从开发者的角度切入,适合满足本服务的调度需求,使用集成包中的方法,所有调度任务均由本服务触发,本服务有绝对控制权。

1.3 产品三大块

Client

创建作业任务的入口,由你在业务代码中通过调用 Hangfire 不同的方法,创建出不同的作业类型,如 定时任务、单次任务、触发任务等。会将创建的作业任务详细信息,先存起来。

Storage

目前官网支持的存储方式有MSSQL/Redis,所以重启后继续运行不中断任务。它存储比如实例编号、创建的作业任务、异常机制、作业队列、并发限流等等。当然也有被官网认可的扩展存储方式,由第三方负责维护更新。可参考官网扩展。但官方提供的存储方式支持更多的功能,如并发设置,限流限速设置等。

Server

负责拉取已存储的作业数据,然后按照作业类型的方式,(或许用反射等)执行逻辑或方法并记录结果。所以是负责具体执行作业任务的后台服务。

官网图例展示了以上三者的关系:

1.4 集成到项目

用 VS 创建一个 WebApi,作为以下案例。

以下用 VS2022 / .NET6 / Hangfire v1.7 / SqlServer2019 版本集来学习。

1.4.1 安装与配置

️最简单的安装方式,Hangfire内包括了所有需要用到的包

dotnet add package Hangfire
dotnet add package Microsoft.Data.SqlClient

或者按需安装,需要什么装什么

dotnet add package Hangfire.Core
dotnet add package Hangfire.SqlServer
dotnet add package Microsoft.Data.SqlClient

️首先,在运行前,你要先创建一个空库,比如创建一个名为 HangfireTest 的数据库。那么接下来以这个空库,来初始化 Hangfire。

CREATE DATABASE HangfireTest;

appsettings 配置

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Hangfire": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"HangfireConnection": "Server=13.13.1.13;Database=HangfireTest;uid=sa;pwd=********;"
}
}

️Program 中的服务注册

public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// ...省略... # ️ 配置 Hangfire
builder.Services.AddHangfire(configuration => configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(builder.Configuration.GetConnectionString("HangfireConnection"))); # ️ 注册 Hangfire 服务
builder.Services.AddHangfireServer(); var app = builder.Build();
// ...省略... # ️ 启用 Dashboard
app.UseHangfireDashboard(); app.Run();
}

然后,在此项目的业务代码中,Hangfire 就可以直接的用了。

项目第一次 Run 起来,自动初始化DB

Dashboard 默认访问地址 /hangfire,也可自定义地址,如 app.UseHangfireDashboard("/myhangfire");

1.4.2 创建作业任务

一个 [ Say hi ] 的任务,很简单。假设我们在控制器中写下:

[HttpGet(Name = "Say")]
public void Get()
{
BackgroundJob.Enqueue(() => Console.WriteLine("Say hi"));
}

当然,我们要贴合实际应用,为此建立以下假设的功能类,用以协助案例演示。

# 演示的业务类,供示例调用
public class JobTest {
public void Enqueue_method(string uname) {
Console.WriteLine($"Hi {uname}, I'm Enqueue!");
}
public void Delayed_method() {
Console.WriteLine($"I'm Delayed!");
}
public void Recurring_method() {
Console.WriteLine($"I'm Recurring!");
}
}

️ 创建一次性作业,调用了上面 JobTest 类的 Enqueue_method 方法

# 仅执行一次后,作业失效
BackgroundJob.Enqueue<JobTest>(m => m.Enqueue_method(uname));

️ 创建延迟作业,设定延迟时间

# 7 秒后,再执行 Delayed_method 方法
BackgroundJob.Schedule<JobTest>((m) => m.Delayed_method(), TimeSpan.FromSeconds(7));

️ 创建定时作业,设定间隔时间

# 周期性 循环执行 Recurring_method 方法
RecurringJob.AddOrUpdate<JobTest>((x) => x.Recurring_method(), Cron.Minutely);
# Crontab 表达式 方式,设置间隔时间,每秒执行
RecurringJob.AddOrUpdate<JobTest>((x) => x.Recurring_method(), "0/1 * * * * ?");
# 带 标识编号 的重复作业任务,每分钟
RecurringJob.AddOrUpdate<JobTest>("Job_Rec_001", (x) => x.Recurring_method(), Cron.Minutely);

️ 临时执行一次定时作业,不影响重复作业的下次运行

# 临时执行下 上面名为 Job_Rec_001 的作业
RecurringJob.TriggerJob("Job_Rec_001");

️ 删除指定的定时作业,永久停止重复运行

# 删除 已存在的 名为 Job_Rec_001 的重复作业
RecurringJob.RemoveIfExists("Job_Rec_001");

️ 创建顺序作业,触发执行作业

# 先假设一个延迟的作业任务
var job_number = BackgroundJob.Schedule<JobTest>((m) => m.Delayed_method(), TimeSpan.FromSeconds(7));
# 当上个任务成功后,触发以下作业任务
BackgroundJob.ContinueJobWith(job_number, () => Console.WriteLine("Delayed > Continuation!"));

都很简单,加入到业务代码中即可。

1.5 高阶及应用

当然,默认的配置,已经很满足我们的需求,它一定是官方经过不断的测试得出的各种场景下的最优配置。有些时候,或许我们有自己的需要,细化,扩展,调优等的需求,那么下面我们来了解下其它有趣的东东。

1.5.1 服务配置

当然,之前只是默认配置项,那么下面来看看自定义的服务配置:

# ️ 注册 Hangfire 服务,服务自定义配置
builder.Services.AddHangfireServer(new BackgroundJobServerOptions {
# 主要关注项
Queues = string new[] { "default", "alpha" }, # 自定义队列名称,默认只有 default
WorkerCount = Environment.ProcessorCount * 5, # 执行作业的工作线程数量,后台服务的处理能力
# 次要配置项
ServerName = Guid.NewGuid().ToString(), # 服务名称,用于区别不同的实例 {Environment.MachineName}.{GUID}
ServerTimeout = TimeSpan.FromMinutes(1), # 后台执行超时,用户后台服务执行等待的最长时间
HeartbeatInterval = TimeSpan.FromMinutes(1), # 健康检测,用于检测服务的运行状况
SchedulePollingInterval=TimeSpan.FromMinutes(1) # 间隔检测队列中要执行的作业,周期性作业的检测间隔时间,默认15秒
});

1.5.2 作业队列

作业队列,互不影响的多个作业队列,正如上节服务中配置的那样,在启动时定义好有哪些可用的作业队列,这些定义的作业队列怎么用呢?

参考 [创建作业任务] 小节中的 JobTest 类,我们把这个业务类的方法打上Queue("alpha")队列名称,也相当于此作业的归属队列:

public class JobTest {
[Queue("alpha")]
public void Enqueue_method(string uname) {
Console.WriteLine($"Hi {uname}, I'm Enqueue!");
}
}

如此,把每个作业归属到各自队列,有各自的先后顺序,互不影响,这在过多的应用场景上做划分很有用。

1.5.3 异常重试机制

由于种种原因,或许外部因素,或许内部逻辑,网络/硬件/第三方等等,可能会遇到作业执行失败的情况,那么失败后咋办,有没有可以弥补的措施呢?当然,重试机制是标配,让意外状况降到更低,更能确保应用的正常运转,来看看 Hangfire 重试机制的设定。

同样,继续参考 [创建作业任务] 小节中的 JobTest 类,我们把这个业务类的方法设定自动重试属性:AutomaticRetry

  • Attempts:重试次数,默认 10 次
  • DelaysInSeconds:重试间隔,一个秒数数组,也就是说,每次的间隔都可以设置
public class JobTest {
[Queue("alpha")]
[AutomaticRetry(Attempts = 5, DelaysInSeconds = new int[]{ 2, 5, 10 })]
public void Enqueue_method(string uname) {
Console.WriteLine($"Hi {uname}, I'm Enqueue!");
}
}

1.5.4 机制原理闲聊

前面也提到过三大块,客户端、存储与执行服务,那么这里要说的是,,,先不说 先来看看DB中存的什么,{"t":"System.Console, mscorlib","m":"WriteLine","p":["System.String"],"a":["Say hi"]},源代码???

也就是说,客户端并没有马上立即着手执行,而是先把要执行的内容保存起来,那么谁来执行。Hangfire-Server,是的,Hangfire服务会执行到这个内容代码的,尽管客户端与服务两者的时间甚短,分工很明确。由此,又引出两点,第一,服务哪来的,集成在应用中的,也就是与应用同一进程中的线程,那么,既然这样,Hangfire服务的运行会影响到应用本身的运行,对不啦。第二,既然相当于是应用本身运行的已保存代码,代码难免有引用关系,那么,不同的应用,或者说多个应用,用同一个Hangfire库呢,绝对不行,其它应用不可能有保存代码中调用的方法等之类的引用。也就是说,Hangfire只能与服务一对一的存在。那么集群呢,单服务部署多个实例呢,当然也可以,保存的代码在不同的服务实例中都可以运行。

所以啊,我认为啊,Hangfire 更多的是面向开发人员,随意嵌入到业务逻辑中,针对单个服务,对不啦~

二、DolphinScheduler

2.1 产品简介

这个产品呢...
其实这里也不是为了学习这个产品,它有足够多的功能,这里就主要来看些与调度有关的了。比如,以管理员的角色,架构者的身份,面对整个平台,包括对外产品/内部产品/各服务/PC/移动/DB等等吧,从全局进行的作业调度,为实现某个额外的环节等。这个产品的侧重点主要是自定义流程,那么这里也包含有关于调度的内容,比如,网络请求,当然也能定义为调度了,我可以用它来向我内部地址定时拉取推送数据等,再比如,执行DB脚本,也可以做为调度的内容项,我可以定时做数据转移或报表计算等。DolphinScheduler 支持的内容多了,并可自动并行串行...

所以呢,把 DolphinScheduler 拿过来说说。有兴趣,看看官网的定义。几个特点:自定义 推拽 制作流程,支持的流程节点任务类型超多,SHELL/SQL/HTTP/Spark/HIVE/云/逻辑节点/甚至机器学习等等,好多我没用过(就很全吧);节点并行或串行运行等等;支持上十种告警方式,Email/Telegram/钉钉/微信等等,是不是很中国,对的,国人的作品,被 Apache 收录为孵化项目。再回过头来继续说说它的特点,管理运行自定义流程,定时运行/暂停/恢复/下线等的管理维护。上十万个任务的稳定运行。。。

2.2 部署运行

也不是多严谨的部署过程了,仅为学习,所以就用 Docker 方式咯,快捷嘛。生产环境就得认真对待了。

关于涉及到的 Docker 安装就不多说了,可参考以往文章 Docker 笔记汇总。DolphinScheduler 官网文档中有容器的运行说明,这里就用最简单的方式运行容器apache/dolphinscheduler-standalone-server来学习吧。

我们的目的是用不同的任务类型自定义流程,来吧。

️先运行 Docker 容器

docker run -dit --name dolp-server -p 12345:12345 -p 25333:25333 apache/dolphinscheduler-standalone-server:3.2.1

️登录产品

浏览器访问:http://{your-address}:12345/dolphinscheduler/ui/login
默认账号密码:admin / dolphinscheduler123

️产品首页

2.3 自定义流程

一个简单的流程制作示例,体验下 DolphinScheduler 的自定义过程。从中感受下 DolphinScheduler 为我们带来了什么。

️看我做的一个简单的示例流程:

️是的,来说说上图自定义流程的各节点任务吧:

  • SHELL 节点1 - 是的,我想通过 SHELL 脚本来操作服务器,比如对服务文件资源的操作
  • HTTP 节点2 - 我设想的是,通过一个请求,拿到返回数据。这在我们的产品平台上很常见
  • MSSQL 节点3 - 我设想的是,拿到上一个HTTP任务节点请求的数据,再存储到DB中,等等吧

通过拖拽设定,可以实现一些有趣的功能,挺好的。

️为自定义流程设定运行时间,妥妥的任务调度

国人的产品,也就是说,中文文档很齐全,阅读起来很顺畅...

2.4 价值与定位

果然,DolphinScheduler 为每种不同的服务,通过鼠标的操作,实现打通壁垒,建立联系,制定流程的运行策略,当然更多的功能等待探究。。。就像之前说的那样,做为一个管理者/运维/架构等,从全局出发,面对各类运行的产品,为满足不同的业务需要或数据资源等的操作。

这里没有去更深入的探究,只是做了一个示例和了解,大概知道是怎么一回事,做为我们今后工作中,关于调度的一个可选项...

三、对比总结

所以呢,本篇:

一个是 Hangfire,从开发者的角度切入,主要以代码方式,实现关于调度;

另一个是 DolphinScheduler,从全局的角度,低代码方式,各服务实现互通,以达到业务场景需要的调度。

... 更多优秀的有趣的实现或方式,后续再追加吧。

微服务 - 作业调度 · Hangfire集成式 · 仪表盘 · DolphinScheduler分布式 · 定义流程的更多相关文章

  1. 基于容器微服务的PaaS云平台设计(一) 实现容器微服务和持续集成

    版权声明:本文为博主原创文章,欢迎转载,转载请注明作者.原文超链接 ,博主地址:http://www.cnblogs.com/SuperXJ/ 前言:关于什么是容器微服务PaaS和容器微服务PaaS的 ...

  2. .NetCore微服务Surging新手傻瓜式 入门教程 学习日志---先让程序跑起来(一)

    原文:.NetCore微服务Surging新手傻瓜式 入门教程 学习日志---先让程序跑起来(一) 写下此文章只为了记录Surging微服务学习过程,并且分享给广大想学习surging的基友,方便广大 ...

  3. .NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二)

    原文:.NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二) 先上项目解决方案图: 以上可以看出项目结构可以划分为4大块,1是surging的核心底层,2,3,4都可以 ...

  4. .NET Core微服务之基于Steeltoe使用Zipkin实现分布式追踪

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 =>  Steeltoe目录快速导航: 1. 基于Steeltoe使用Spring Cloud Eureka 2. 基于Steelt ...

  5. 微服务架构学习与思考(09):分布式链路追踪系统-dapper论文学习

    一.技术产生的背景 1.1 背景 先来了解一下分布式链路追踪技术产生的背景. 在现在这个发达的互联网世界,互联网的规模越来越大,比如 google 的搜索,Netflix 的视频流直播,淘宝的购物等. ...

  6. 【一起学源码-微服务】Hystrix 源码二:Hystrix核心流程:Hystix非降级逻辑流程梳理

    说明 原创不易,如若转载 请标明来源! 欢迎关注本人微信公众号:壹枝花算不算浪漫 更多内容也可查看本人博客:一枝花算不算浪漫 前言 前情回顾 上一讲我们讲了配置了feign.hystrix.enabl ...

  7. 【一起学源码-微服务】Hystrix 源码三:Hystrix核心流程:Hystix降级、熔断等原理剖析

    说明 原创不易,如若转载 请标明来源! 欢迎关注本人微信公众号:壹枝花算不算浪漫 更多内容也可查看本人博客:一枝花算不算浪漫 前言 前情回顾 上一讲我们讲解了Hystrix在配合feign的过程中,一 ...

  8. SpringBoot应用集成微服务组件Nacos

    目录 springboot与微服务组件nacos Nacos服务快速启动 STS4 开发工具 Maven 环境配置 STS4开发工具引入Maven配置 Maven Repo配置阿里云镜像源 Sprin ...

  9. Dubbo和Spring Cloud微服务架构'

    微服务架构是互联网很热门的话题,是互联网技术发展的必然结果.它提倡将单一应用程序划分成一组小的服务,服务之间互相协调.互相配合,为用户提供最终价值.虽然微服务架构没有公认的技术标准和规范或者草案,但业 ...

  10. 微服务架构-选择Spring Cloud,放弃Dubbo

    Spring Cloud 在国内中小型公司能用起来吗?从 2016 年初一直到现在,我们在这条路上已经走了一年多. 在使用 Spring Cloud 之前,我们对微服务实践是没有太多的体会和经验的.从 ...

随机推荐

  1. debian12 linux root能用lightdm登陆xfce桌面,普通用户不能用lightdm登陆xfce桌面,闪一下黑屏和一个_,又回到lightdm

    Fn+Ctrl+F3,进入tty,发现登陆普通用户后再使用startxfce4可以直接进桌面 下面参照 https://forums.opensuse.org/t/normal-user-can-no ...

  2. 谈谈 OI 中的查重

    鉴于最近洛谷的公开赛出现的重题引起的纠纷,我打算整理一下此类问题的危害和做法. 也许有时候无意的重题不会被处罚,但我想也应该尽量避免来换取选手的更好体验. Part 0 什么是重题 原题大致可分为完全 ...

  3. Android实现简单登录界面

    LoginActivity 1 package com.example.myapplication; 2 3 import android.os.Bundle; 4 import android.vi ...

  4. ET介绍——分布式Actor模型

    Actor模型 Actor介绍 在讨论Actor模型之前先要讨论下ET的架构,游戏服务器为了利用多核一般有两种架构,单线程多进程跟单进程多线程架构.两种架构本质上其实区别不大,因为游戏逻辑开发都需要用 ...

  5. [Unity3D] 使用LineRenderer实现尾迹与虚线效果

    Unity3D 使用LineRenderer绘制尾迹与虚线 1.添加LineRenderer组件 先创建一个3D对象,然后点击Add Component选项 搜索并添加LineRenderer组件 添 ...

  6. set集合的HashSet 类

    set是一个没有重复元素, 没有存储顺序的集合,它主要有三个实现类,如下: HashSet 类 HashSet 类是按照哈希算法来存储集合中的元素,当向 Set 集合中添加一个元素时,HashSet ...

  7. C++ 条件与 If 语句:掌握逻辑判断与流程控制精髓

    C++ 条件和 If 语句 您已经知道 C++ 支持数学中的常见逻辑条件: 小于:a < b 小于或等于:a <= b 大于:a > b 大于或等于:a >= b 等于:a = ...

  8. C# 布尔值和条件语句:入门指南和实用示例

    C# 布尔值 在编程中,通常需要一个只能有两个值之一的数据类型,比如: 是 / 否 开 / 关 真 / 假 为此,C# 有一个 bool 数据类型,可以取 true 或 false 的值. 布尔值 使 ...

  9. 基于C语言的面向对象设计模式(持续更新)

    前言 首先这篇文章只是初步的尝试,不涉及过于高深的编程技巧:同时需要表明的是,面向对象只是一种思想,不局限于什么样的编程语言,不可否认的是基于面向对象特性而设计的语言确实要比面向过程式的语言更加容易进 ...

  10. Spring框架之IOC和AOP底层原理

    ​1.1简介 Spring:春天-->软件行业的春天 2002,首次推出了Spring框架的雏:interface21框架! Spring框架即以interface21框架为基础,经过重新设计, ...