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. matlab中exist 检查变量、脚本、函数、文件夹或类的存在情况

    参考: 1.https://ww2.mathworks.cn/help/matlab/ref/exist.html?searchHighlight=exist&s_tid=doc_srchti ...

  2. 编程体系结构(06):Java面向对象

    本文源码:GitHub·点这里 || GitEE·点这里 一.基础概念 1.面向对象概念 面向对象编程的主要思想是把构成问题的各个事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙一 ...

  3. MYSQL 50 基础题 (转载)

    MYSQL 50 基础题 (转载) 前言:最近在强化MYSQL 能力 答案在(也是转载处) https://www.cnblogs.com/kangxinxin/p/11585935.html 下面是 ...

  4. appium 环境安装指引

    1.安装Appium-Python-Client Pip install Appium-Python-Client 2.安装nodejs https://nodejs.org/ 安装成功验证:node ...

  5. Vuejs上传

    下载 Vuejs上传Vuejs上传 多部分上传Vue组件. 上传器可以选择上传多部分的文件. 这是关于最大的上传尺寸,允许你上传大文件. 如果prop multiple为真,文件列表将在选择文件时呈现 ...

  6. docker 搭建LNMP网站平台

    准备好镜像 1.创建网络 docker network create lnmp 测试环境需删除全部之前起的容器 docker rm -f $(docker ps -a |awk '{print $1} ...

  7. cmd备份数据库,还原数据库,仅限于php

    第一:先备份数据库 1.进入cmd(黑盒子) 2.进入phpstudy所在的盘 3.cd E: 3.cd phpstudy; 4.cd PHPTutorial 5.cd mysql; 6.cd bin ...

  8. 手写一个HTTP框架:两个类实现基本的IoC功能

    jsoncat: 仿 Spring Boot 但不同于 Spring Boot 的一个轻量级的 HTTP 框架 国庆节的时候,我就已经把 jsoncat 的 IoC 功能给写了,具体可以看这篇文章&l ...

  9. Android开发签名证书的生成

    现在都说互联网寒冬,其实只要自身技术能力够强,咱们就不怕!我这边专门针对Android开发工程师整理了一套[Android进阶学习视频].[全套Android面试秘籍].[Android知识点PDF] ...

  10. nginx安全: 配置http基本验证(Basic Auth)(nginx 1.18.0)

    一,http基本验证的作用: 1,http基本身份验证会从浏览器弹出登录窗口, 简单明了,容易理解, 对于面向终端用户的前台来说,不够友好, 但对于内部员工操作的后台还是很有用,通常作为一层安全措施应 ...