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. WJQ与机房

    sample input 5 6 7 2 3 1 1 5 0 6 0 0 8 6 6 5 3 4 3 7 8 2 4 0 0 6 9 sample output 20 样例解释: 分别以(2,1)为左 ...

  2. [KMP]字符串匹配算法

    算法介绍: KMP是一种用来处理字符串匹配问题的算法,给你两个字符串A.B,让你回答B是否为A的子串,或者A中有多少子串等于B. 这题最暴力的做法是:枚举A中与B相等的子串的左端点,再判断是否与B相等 ...

  3. 15.深入k8s:Event事件处理及其源码分析

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 源码版本是1.19 概述 k8s的Event事件是一种资源对象,用于展示集群内发生的情况 ...

  4. Mybatis中进行批量更新(updateBatch)

    更新多条数据,每条数据都不一样 背景描述:通常如果需要一次更新多条数据有两个方式,(1)在业务代码中循环遍历逐条更新.(2)一次性更新所有数据(更准确的说是一条sql语句来更新所有数据,逐条更新的操作 ...

  5. RLP序列化算法

    RLP RLP(Recursive Length Prefix)递归长度前缀编码,是由以太坊提出的序列化/反序列化标准,相比json格式体积更小,相比protobuf对多语言的支持更强. RLP将数据 ...

  6. 编程语言拟人:来自C++、Python、C语言的“傲娇”自我介绍!

    软件工程领域,酷爱编程的人很多,但另一些人总是对此避之不及.而构建软件无疑会让所有人压力山大,叫苦连连.   来看看这些流行编程语言的"内心独白",JAVA现实,C++傲娇,Rus ...

  7. CVE-2010-2883-CoolType.dll缓冲区溢出漏洞分析

    前言 此漏洞是根据泉哥的<漏洞战争>来学习分析的,网上已有大量分析文章在此只是做一个独立的分析记录. 复现环境 操作系统 -> Windows XP Sp3 软件版本 -> A ...

  8. docker启动服务

    1 rabbitmq docker启动服务---------------rabbitmq 2 mysql docker启动服务---------------mysql 3 redis docker启动 ...

  9. oh my zsh 安装

    date: "2020-10-18T12:36:00+08:00" title: "oh my zsh 安装" tags: ["zsh",& ...

  10. docker的常用操作之二:docker内无法解析dns之firewalld设置等

    一,如何启动一个已退出的容器? [root@localhost ~]# docker start storage4 说明:架构森林是一个专注架构的博客,地址:https://www.cnblogs.c ...