Hangfire有个机制可以确保所有任务都会被执行,如果当服务器停机了一段时间重新启动时,在此期间的周期任务会几乎同时执行。而大部分时候,我们希望同个周期任务每段时间只运行一个就行了。

或者是如果周期任务设置得过于频繁,当之前的任务还没执行完,我们也不希望继续添加周期任务进队列去排队执行。

Hangfire有提供一个扩展https://docs.hangfire.io/en/latest/background-processing/throttling.html

同个DisableConcurrentExecution我们可以限制同一个任务每次只会执行一个,但是如果有任务正在执行,这时候又有新任务过来,新任务并不会被删除而是处于排队状态,等待前面的任务执行完。

而且,如果我们的任务用了同一个方法作为入口时(或者说我们需要根据方法的参数来确定是否为同一个任务),此时这个控制就不适用了。

参考了https://gist.github.com/sbosell/3831f5bb893b20e82c72467baf8aefea,我们可以用过滤器来实现,将运行期间进来的任务给取消掉。

代码的具体实现为:

 1     /// <summary>
2 /// 禁用多个排队项目
3 /// <remarks>同个任务取消并行执行,期间进来的任务不会等待,会被取消</remarks>
4 /// </summary>
5 public class DisableMultipleQueuedItemsFilter : JobFilterAttribute, IClientFilter, IServerFilter
6 {
7 private static readonly TimeSpan LockTimeout = TimeSpan.FromSeconds(5);
8 private static readonly TimeSpan FingerprintTimeout = TimeSpan.FromHours(4);//任务执行超时时间
9
10 public void OnCreating(CreatingContext filterContext)
11 {
12 var recurringJobId = filterContext.GetJobParameter<string>("RecurringJobId");
13 if (!string.IsNullOrEmpty(recurringJobId)&&!AddFingerprintIfNotExists(filterContext.Connection, recurringJobId))
14 {
15 filterContext.Canceled = true;
16 }
17 }
18
19 public void OnPerformed(PerformedContext filterContext)
20 {
21 var recurringJobId = filterContext.GetJobParameter<string>("RecurringJobId");
22 if (!string.IsNullOrEmpty(recurringJobId))
23 {
24 RemoveFingerprint(filterContext.Connection, recurringJobId);
25 }
26 }
27
28 private static bool AddFingerprintIfNotExists(IStorageConnection connection, string recurringJobId)
29 {
30 using (connection.AcquireDistributedLock(GetFingerprintLockKey(recurringJobId), LockTimeout))
31 {
32 var fingerprint = connection.GetAllEntriesFromHash(GetFingerprintKey(recurringJobId));
33
34 if (fingerprint != null &&
35 fingerprint.ContainsKey("Timestamp") &&
36 DateTimeOffset.TryParse(fingerprint["Timestamp"], null, DateTimeStyles.RoundtripKind, out var timestamp) &&
37 DateTimeOffset.UtcNow <= timestamp.Add(FingerprintTimeout))
38 {
39 // 有任务还未执行完,并且没有超时
40 return false;
41 }
42
43 // 没有任务执行,或者该任务已超时
44 connection.SetRangeInHash(GetFingerprintKey(recurringJobId), new Dictionary<string, string>
45 {
46 { "Timestamp", DateTimeOffset.UtcNow.ToString("o") }
47 });
48
49 return true;
50 }
51 }

在OnCreating方法中,我们读取RecurringJobId的值,获取周期任务的id(同样的id代表同一个周期任务),然后以这个id为key去设置一个超时。如果在此期间,如果拿到了key的值,以及设置的时间还未超时的话,我们通过设置filterContext.Canceled = true取消掉此任务。

使用connection.AcquireDistributedLock在设置键值时添加分布式锁,使用connection.SetRangeInHash键RecurringJobId作为key,当前时间作为值保存。以此来确保在FingerprintTimeout的超时时间内,同个RecurringJobId的任务只能创建一个。或者等任务执行完后在OnPerformed方法中释放掉这个键值。

在OnPerformed方法中,将我们在创建方法中设置的RecurringJobId key和对应的时间给删除,这样OnCreating可以继续创建同一个RecurringJobId 的任务。

或者是普通触发的任务,这时候没有RecurringJobId 我们希望可以同个参数来控制,同样的参数不能同时执行。我们可以通过这个方法来生成相应的key

 1         private static string GetFingerprint(Job job)
2 {
3 var parameters = string.Empty;
4 if (job?.Arguments != null)
5 {
6 parameters = string.Join(".", job.Arguments);
7 }
8 if (job?.Type == null || job.Method == null)
9 {
10 return string.Empty;
11 }
12 var payload = $"{job.Type.FullName}.{job.Method.Name}.{parameters}";
13 var hash = SHA256.Create().ComputeHash(System.Text.Encoding.UTF8.GetBytes(payload));
14 var fingerprint = Convert.ToBase64String(hash);
15 return fingerprint;
16 }

这样我们就能确保我们希望的同一个任务不会同时在执行,而且周期任务也不会继续在队列中排队

Hangfire只允许同时运行同一个任务的更多相关文章

  1. WPF 同一个程序 只允许 同时运行一个

            方法2  当程序已经运行了 再运行这个程序时,则显示当前这个窗体  http://code.3rbang.com/cshape-run-one/ VS2013附件:http://fil ...

  2. PyCharm中运行同一个python程序时选择平行窗口运行

    问题描述 当我们进行Socket编程时,客户端可能有多个,原则上如果有n个客户端,那么我们就要编辑n客户端的代码.然而其实我们每个客户端的代码都是相同,如果编辑n遍,将会相当的浪费空间. 解决办法 学 ...

  3. [C/C++] 只允许程序运行一个实例

    原理是创建一个内核对象之后 如果再创建一个同名的对象 就会给代码中的GetLastError函数对应的变量修改为 ERROR_ALREADY_EXISTS (但是不影响"创建"对象 ...

  4. VC 实现程序只运行一个实例,并激活已运行的程序

    转载:http://blog.sina.com.cn/s/blog_4b44e1c00100bh69.html 进程的互斥运行:CreateMutex函数实现只运行一个程序实例 正常情况下,一个进程的 ...

  5. [VC]在VC++中实现让程序只运行一个实例的方法且实现该实例

    方法一: 有时候在开发应用程序时,希望控制程序运行唯一的实例.例如,最常用的mp3播放软 件Winamp,由于它需要独占计算机中的音频设备,因此该程序只允许自身运行唯一的一个例程.在Visual C+ ...

  6. C# WPF开机自启动和只允许一个程序运行

    本文出自:https://www.cnblogs.com/2186009311CFF/p/10024949.html 在App.xaml.cs填充一下内容,即可实现只允许一个运行,且不解锁屏幕的情况下 ...

  7. ASP.NET Core开发-后台任务利器Hangfire使用

    ASP.NET Core开发系列之后台任务利器Hangfire 使用. Hangfire 是一款强大的.NET开源后台任务利器,无需Windows服务/任务计划程序. 可以使用于ASP.NET 应用也 ...

  8. hangfire+bootstrap ace 模板实现后台任务管理平台

    前言 前端时间刚开始接触Hangfire就翻译了一篇官方的教程[翻译+山寨]Hangfire Highlighter Tutorial,后来在工作中需要实现一个异步和定时执行的任务管理平台,就结合bo ...

  9. iOS运行时 -- Runtime(摘抄自网络)

    运行时(iOS) 一.什么是运行时(Runtime)? 运行时是苹果提供的纯C语言的开发库(运行时是一种非常牛逼.开发中经常用到的底层技术) 二.运行时的作用? 能获得某个类的所有成员变量 能获得某个 ...

随机推荐

  1. P5858 「SWTR-03」Golden Sword

    题面: Link 题面有点长,不想粘了,QAQ. 题解: 一句话题意,你有 \(n\) 件物品需要依次放进去,每个物品放进去之后会得到一定的权值,为当前锅炉里面的物品的数量乘以 \(a_i\) 每次在 ...

  2. PHP添加新扩展包的步骤

    1.找到PHP解压包,将   php.ini-development   这个文件复制一份,并修改后缀名为  .ini 2.将这个文件打开,将此处注释解开, 3.配置你扩展的该包的位置 4.如果显示不 ...

  3. 「DevOps 转型与实践」沙龙回顾第二讲

    背景介绍 本期分享内容为<平台化 DevOps-云计算与云原生模式下 DevOps 的建设实践>.目前,DevOps 越来越成为大家当前建设的热点,伴随着基础设施的转型和应用框架的转型,更 ...

  4. Sticks(UVA - 307)【DFS+剪枝】

    Sticks(UVA - 307) 题目链接 算法 DFS+剪枝 1.这道题题意就是说原本有一些等长的木棍,后来把它们切割,切割成一个个最长为50单位长度的小木棍,现在想让你把它们组合成一个个等长的大 ...

  5. ubuntu1804 snort base

    1.环境准备 apt安装 sudo apt-get update -y sudo apt-get dist-upgrade -y sudo apt-get install -y zlib1g-dev ...

  6. linux 内存泄露检测工具

    Valgrind Memcheck 一个强大开源的程序检测工具 下载地址:http://valgrind.org/downloads/current.html Valgrind快速入门指南:http: ...

  7. 多测师_肖sir_git _004(版本控制器)

    gitgit 是一个开源的分布式版本控制系统,用于敏捷高效的处理任何大小的项目.git是linux torvalds 为了帮助管理linux内核开发的一个开放源码的版本控制软件.git与常用的版本控制 ...

  8. spring boot:spring security用mysql数据库实现RBAC权限管理(spring boot 2.3.1)

    一,用数据库实现权限管理要注意哪些环节? 1,需要生成spring security中user类的派生类,用来保存用户id和昵称等信息, 避免页面上显示用户昵称时需要查数据库 2,如果需要在页面上显示 ...

  9. arcgis activeX 安全提示消除办法

    点击任何的一个ArcToolBox 工具,光标落在参数输入框时,会提示 "在此页面上的ActiveX控件和本页上的其他部分的交互可能不安全.你想允许这种交互操作吗? " 消除办法 ...

  10. python GDAL 读写shp文件

    gdal包用于处理栅格数据,ogr用于处理矢量数据. 1 #!C:\Program Files\pythonxy\python\python.exe 2 #-*- coding:gb2312 -*- ...