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. HTML & CSS & JavaScript 从一个表格到一个灰阶颜色表 02

    工具1:HBuilder X 1.9.9.20190522 工具2:火狐浏览器 67.0.4 (64 位) 其实,我想使用表格,做一个这样的颜色表,如下图所示: 例 3:我们参照上图,基于上一个例子, ...

  2. uni-app引入iconfont字体图标

    1 首先进入你的iconfont项目 很好, 看见圈圈的吗 , 我说蓝色的,记住了,选到这个 ,然后点击下载本地项目, 解压完就是这个了 ,然后把 圈起来的放到你的项目文件里面 ,记得引入的时候路径别 ...

  3. JVM 第三篇:Java 类加载机制

    本文内容过于硬核,建议有 Java 相关经验人士阅读. 1. 什么是类的加载? 类的加载指的是将类的 .class 文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 ...

  4. Xnip Mac上方便好用的截图工具

    Xnip Mac上方便好用的截图工具 标注 Xnip 拥有齐全的标注功能,您可以对截取的图片进行标注,在标注的同时还能重新调整截图大小. 查看标注操作 GIF 滚动截图 Xnip 的滚动截图功能可以让 ...

  5. docker启动镜像报错

    docker启动镜像报错: docker: Error response from daemon: driver failed programming external connectivity on ...

  6. python与嵌入式的火花

    一.前言 近些年来python非常流行,Python是一种面向对象的解释性计算机程序设计语言,Python语法简介清晰,易读性以及可扩展性,Python具有丰富和强大的库,能够把用其他语言制作的各种模 ...

  7. Presto在滴滴的探索与实践

    ​桔妹导读:Presto在滴滴内部发展三年,已经成为滴滴内部Ad-Hoc和Hive SQL加速的首选引擎.目前服务6K+用户,每天读取2PB ~ 3PB HDFS数据,处理30万亿~35万亿条记录,为 ...

  8. HDU-1051 Wooden Sticks--线性动归(LIS)

    题目大意:有n根木棍(n<5000),每根木棍有一个长度l和重量w(l,w<10000),现在要对这些木头进行加工,加工有以下规则: 1.你需要1分钟来准备第一根木头. 2.如果下一根木头 ...

  9. 第五章 NFS、rsync等统一用户相关操作

    一.统一用户 1.httpd2.NFS挂载目录3.rsync 1.所有服务器统一创建用户 [root@web01 ~]# groupadd www -g 666[root@web01 ~]# user ...

  10. java刷题时常用容器详解

    当初学java时,只是简单的把java基础知识过了一遍就跑去刷题了,很多知识都是在刷题的过程中慢慢加深理解的. 由于每次刷题时,刷到与容器有关的我基本上都跑去百度了,例如百度一下:java中List的 ...