解决方法:在执行的任务方法前加上Mutex特性即可,如果作业未完成,新作业开启的话,新作业会放入计划中的作业队列中,直到前面的作业完成。

必须使用Hangfire.Pro.Redis 和 Hangfire.SqlServer 作为数据库。

参考:https://github.com/HangfireIO/Hangfire/issues/1053

  [Mutex("DownloadVideo")]
public async Task DownloadVideo()
{
}

Mutex特性代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using Hangfire.Common;
using Hangfire.States;
using Hangfire.Storage; namespace Hangfire.Pro
{
/// <summary>
/// Represents a background job filter that helps to disable concurrent execution
/// without causing worker to wait as in <see cref="Hangfire.DisableConcurrentExecutionAttribute"/>.
/// </summary>
public class MutexAttribute : JobFilterAttribute, IElectStateFilter, IApplyStateFilter
{
private static readonly TimeSpan DistributedLockTimeout = TimeSpan.FromMinutes(); private readonly string _resource; public MutexAttribute(string resource)
{
_resource = resource;
RetryInSeconds = ;
} public int RetryInSeconds { get; set; }
public int MaxAttempts { get; set; } public void OnStateElection(ElectStateContext context)
{
// We are intercepting transitions to the Processed state, that is performed by
// a worker just before processing a job. During the state election phase we can
// change the target state to another one, causing a worker not to process the
// backgorund job.
if (context.CandidateState.Name != ProcessingState.StateName ||
context.BackgroundJob.Job == null)
{
return;
} // This filter requires an extended set of storage operations. It's supported
// by all the official storages, and many of the community-based ones.
var storageConnection = context.Connection as JobStorageConnection;
if (storageConnection == null)
{
throw new NotSupportedException("This version of storage doesn't support extended methods. Please try to update to the latest version.");
} string blockedBy; try
{
// Distributed lock is needed here only to prevent a race condition, when another
// worker picks up a background job with the same resource between GET and SET
// operations.
// There will be no race condition, when two or more workers pick up background job
// with the same id, because state transitions are protected with distributed lock
// themselves.
using (AcquireDistributedSetLock(context.Connection, context.BackgroundJob.Job.Args))
{
// Resource set contains a background job id that acquired a mutex for the resource.
// We are getting only one element to see what background job blocked the invocation.
var range = storageConnection.GetRangeFromSet(
GetResourceKey(context.BackgroundJob.Job.Args),
,
); blockedBy = range.Count > ? range[] : null; // We should permit an invocation only when the set is empty, or if current background
// job is already owns a resource. This may happen, when the localTransaction succeeded,
// but outer transaction was failed.
if (blockedBy == null || blockedBy == context.BackgroundJob.Id)
{
// We need to commit the changes inside a distributed lock, otherwise it's
// useless. So we create a local transaction instead of using the
// context.Transaction property.
var localTransaction = context.Connection.CreateWriteTransaction(); // Add the current background job identifier to a resource set. This means
// that resource is owned by the current background job. Identifier will be
// removed only on failed state, or in one of final states (succeeded or
// deleted).
localTransaction.AddToSet(GetResourceKey(context.BackgroundJob.Job.Args), context.BackgroundJob.Id);
localTransaction.Commit(); // Invocation is permitted, and we did all the required things.
return;
}
}
}
catch (DistributedLockTimeoutException)
{
// We weren't able to acquire a distributed lock within a specified window. This may
// be caused by network delays, storage outages or abandoned locks in some storages.
// Since it is required to expire abandoned locks after some time, we can simply
// postpone the invocation.
context.CandidateState = new ScheduledState(TimeSpan.FromSeconds(RetryInSeconds))
{
Reason = "Couldn't acquire a distributed lock for mutex: timeout exceeded"
}; return;
} // Background job execution is blocked. We should change the target state either to
// the Scheduled or to the Deleted one, depending on current retry attempt number.
var currentAttempt = context.GetJobParameter<int>("MutexAttempt") + ;
context.SetJobParameter("MutexAttempt", currentAttempt); context.CandidateState = MaxAttempts == || currentAttempt <= MaxAttempts
? CreateScheduledState(blockedBy, currentAttempt)
: CreateDeletedState(blockedBy);
} public void OnStateApplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
if (context.BackgroundJob.Job == null) return; if (context.OldStateName == ProcessingState.StateName)
{
using (AcquireDistributedSetLock(context.Connection, context.BackgroundJob.Job.Args))
{
var localTransaction = context.Connection.CreateWriteTransaction();
localTransaction.RemoveFromSet(GetResourceKey(context.BackgroundJob.Job.Args), context.BackgroundJob.Id); localTransaction.Commit();
}
}
} public void OnStateUnapplied(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
} private static DeletedState CreateDeletedState(string blockedBy)
{
return new DeletedState
{
Reason = $"Execution was blocked by background job {blockedBy}, all attempts exhausted"
};
} private IState CreateScheduledState(string blockedBy, int currentAttempt)
{
var reason = $"Execution is blocked by background job {blockedBy}, retry attempt: {currentAttempt}"; if (MaxAttempts > )
{
reason += $"/{MaxAttempts}";
} return new ScheduledState(TimeSpan.FromSeconds(RetryInSeconds))
{
Reason = reason
};
} private IDisposable AcquireDistributedSetLock(IStorageConnection connection, IEnumerable<object> args)
{
return connection.AcquireDistributedLock(GetDistributedLockKey(args), DistributedLockTimeout);
} private string GetDistributedLockKey(IEnumerable<object> args)
{
return $"extension:job-mutex:lock:{GetKeyFormat(args, _resource)}";
} private string GetResourceKey(IEnumerable<object> args)
{
return $"extension:job-mutex:set:{GetKeyFormat(args, _resource)}";
} private static string GetKeyFormat(IEnumerable<object> args, string keyFormat)
{
return String.Format(keyFormat, args.ToArray());
}
}
}

HangFire循环作业中作业因执行时间太长未完成新作业开启导致重复数据的问题的更多相关文章

  1. 解决 ffmpeg 在avformat_find_stream_info执行时间太长

    用ffmpeg做demux,网上很多参考文章.对于网络流,avformt_find_stream_info()函数默认需要花费较长的时间进行流格式探测,那么,如何减少探测时间内? 可以通过设置AVFo ...

  2. SQLServer 删除表中的重复数据

    create table Student(        ID varchar(10) not null,        Name varchar(10) not null, ); insert in ...

  3. Oracle、SQLServer 删除表中的重复数据,只保留一条记录

    原文地址: https://blog.csdn.net/yangwenxue_admin/article/details/51742426 https://www.cnblogs.com/spring ...

  4. 使用T-SQL找出执行时间过长的作业

        有些时候,有些作业遇到问题执行时间过长,因此我写了一个脚本可以根据历史记录,找出执行时间过长的作业,在监控中就可以及时发现这些作业并尽早解决,代码如下:   SELECT sj.name , ...

  5. spark SQL读取ORC文件从Driver启动到开始执行Task(或stage)间隔时间太长(计算Partition时间太长)且产出orc单个文件中stripe个数太多问题解决方案

    1.背景: 控制上游文件个数每天7000个,每个文件大小小于256M,50亿条+,orc格式.查看每个文件的stripe个数,500个左右,查询命令:hdfs fsck viewfs://hadoop ...

  6. 详解C#泛型(二) 获取C#中方法的执行时间及其代码注入 详解C#泛型(一) 详解C#委托和事件(二) 详解C#特性和反射(四) 记一次.net core调用SOAP接口遇到的问题 C# WebRequest.Create 锚点“#”字符问题 根据内容来产生一个二维码

    详解C#泛型(二)   一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...

  7. 《发际线总是和我作队》第八次团队作业:Alpha冲刺 第五天

    项目 内容 这个作业属于哪个课程 软件工程 这个作业的要求在哪里 实验十二 团队作业8:软件测试与Alpha冲刺实验十一 团队作业7:团队项目设计完善&编码 团队名称 发际线总和我作队 作业学 ...

  8. kettle作业中的js如何写日志文件

    在kettle作业中JavaScript脚本有时候也扮演非常重要的角色,此时我们希望有一些日志记录.下面是job中JavaScript记录日志的方式. job的js写日志的方法. 得到日志输出实例 o ...

  9. 【转】【SQL SERVER】怎样处理作业中的远程服务器错误(42000)

    (SQL SERVER)怎样处理作业中的远程服务器错误(42000) 问: 1.我创建了一个链接服务器. 2.在两台服务器之间创建了新的SQL用户. 3.编写了访问链接服务器的SQL语句,执行成功. ...

随机推荐

  1. echarts 调整图表 位置 的方法

    ###内部图表大小是与div容器大小位置相关的,如果想调整图表大小位置,调整div的属性就可以了### ###如果是想调整图表与div间上下左右留白,则设置grid属性就可以了### 如图所示: 具体 ...

  2. Calendar and GregorianCalendar

    1.GregorianCalendar是Calendar的一个具体子类,提供了世界上大多数国家/地区使用的标准日历系统 2.注意 (1)月份:1月到12月[0-11] (2)星期:周日到周六[1-7] ...

  3. jquery跨域方法

    $.ajax({ type: 'get', dataType: "jsonp",//支持跨域 jsonp: "callback", jsonpCallback: ...

  4. MFC之几类消息的区别

    1.ON_COMMAND与ON_UPDATE_COMMAND_UI 开发MFC程序,给菜单子项添加消息处理函数时,会碰到ON_COMMAND和ON_UPDATE_COMMAND_UI两个消息. ON_ ...

  5. 学习Java的进度

    这周我们通过老师的讲解带着我们回到了第八周的知识点.lambda表达式也是一种简化程序的好方法,通过回调程序的测试可以对比出lambda 表达式少的不是一两行代码,可以少了类中方法的定义,直接使用.内 ...

  6. [ARCH] 1、virtualbox中安装archlinux+i3桌面,并做简单美化

    星期六, 28. 七月 2018 02:42上午 - beautifulzzzz 1.安装ArchLinux系统 安装Arch主要看其wiki,比较详细- 中文的我主要参考:一步步教你如何安装 Arc ...

  7. CSS中的px与物理像素、逻辑像素、1px边框问题

    一直不太清楚CSS中的1px与逻辑像素.物理像素是个什么关系(作为一名前端感觉很惭愧 -_-!),今天终于花时间彻底弄清楚了,其实弄清楚之后就觉得事情很简单,但也只有在弄清楚之后,才会觉得简单(语出& ...

  8. js 大厦之JavaScript事件

    1.js事件简介 事件(Event) 是 JavaScript 应用跳动的心脏 ,进行交互,使网页动起来.也是把所有东西粘在一起的胶水.当我们与浏览器中 Web 页面进行某些类型的交互时,事件就发生了 ...

  9. GIT学习笔记——常用命令

    最近使用使用GIT较多,但命令很容易就忘记了,于是整理下,大多整理与一些文档和他人博客 在当前目录新建建一个纯git代码库 $ git --bare init 在当前目录新建一个Git代码库 $ gi ...

  10. PHP新特性Trait

    Trait是PHP 5.4引入的新概念,看上去既像类又像接口,其实都不是,Trait可以看做类的部分实现,可以混入一个或多个现有的PHP类中,其作用有两个:表明类可以做什么:提供模块化实现.Trait ...