解决方法:在执行的任务方法前加上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. Base64格式上传文件至阿里云(java)

    Controller @PostMapping("/save") public R save(@RequestBody ShareEntity share){ OSSClient ...

  2. java 保证程序安全退出

    以前在开发时只知道依靠数据库事务来保证程序关闭时数据的完整性. 但有些时候一个业务上要求的原子操作,不一定只包括数据库,比如外部接口或者消息队列.此时数据库事务就无能为力了. 这时我们可以依靠java ...

  3. Codeforces831D Office Keys

    D. Office Keys time limit per test 2 seconds memory limit per test 256 megabytes input standard inpu ...

  4. spring boot 入门及示例

    需要环境:eclipse4.7.3 + jdk1.8 +maven3.6.1 + tomcat(web需要) spring boot官网介绍:https://spring.io/guides/gs/s ...

  5. Must Know Tips/Tricks in Deep Neural Networks

    Must Know Tips/Tricks in Deep Neural Networks (by Xiu-Shen Wei)   Deep Neural Networks, especially C ...

  6. Windows 10 IoT Core 17083 for Insider 版本更新

    1月26日,微软发布了Windows 10 IoT Core 17083 for Insider版本更新,概括如下: 新特性:1. General bug fixes2. Enabled Flash ...

  7. MySQL 每秒 570000 的写入,如何实现?

    阅读本文大概需要 2.8 分钟. 来源:http://t.cn/E2TbCg5 一.需求 一个朋友接到一个需求,从大数据平台收到一个数据写入在20亿+,需要快速地加载到MySQL中,供第二天业务展示使 ...

  8. ubuntu18.04安装mongoDB

    STEP 1:  在终端输入GPK码 $  sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334B ...

  9. [Swift]两种线程休眠的方式

    一.线程休眠方式1 [Swift]与[C#]的比较: //C#:需要添加using System.Threading; //线程休眠3000毫秒,即3秒 Thread.Sleep(); //Swift ...

  10. [Postman]定制Postman(4)

    自定义请求方法 您可以在Postman中自定义请求方法以满足特定要求.创建自己的请求方法后,您将能够发送/保存它们. 此功能允许您保存/删除自定义方法,还可以删除默认方法.单击请求方法下拉区域,键入方 ...