背景
HangFire有个很奇怪的现象,就是即使你设置的循环作业是一天一次,但是每次作业执行很长时间,我们假设是1小时,那么差不多在开始执行之后的30分钟之后,如果还在执行job,系统就会自动帮你重新开启了新的job执行。也就是撇开循环周期的影响,我一个长时间的作业,超过三十分钟的,那么系统又会在你还在执行的时候,三十分钟之后又起一个来帮你执行。
问题窥探
我之前做项目的时候,就遇到这个问题,莫名其妙。开始的时候,我还以为可以像Quartz加个[DisallowConcurrentExecution]了事。
后面找到这个
但是测试了下,不起作用啊。而且一旦启用分布式部署,这个就算有用,到时候不也没用了,后面想想直接来个干脆的,分布式锁。
解决办法
1、进入job的时候,定义一个key,判断是否正在执行。
string uniqueKey = string.Empty;string key = "xxxxx";if (!CacheExtension.getInstance().AddUnique($"{key}_unique", 1, DateTimeOffset.Now.AddDays(365))){LogExtention.getInstance().WriteCustomLogAsync("", "", true, "上批次还未执行结束--xxxxxxjob");return;}uniqueKey = $"{key}_unique";
2、redis-StringSet保证key是唯一的。
/// <summary>/// 新增,若缓存中已存在相同KEY,则返回false,判断高并发时使用/// </summary>public bool AddUnique(string key, object o, DateTimeOffset expiration, string dependsOnKey = null){key = AddSysCustomKey(key);var ts = expiration - DateTimeOffset.Now;return _client.StringSet(key, ConvertJson(o), ts, When.NotExists);}
3、执行完成的时候,去掉锁。
try{//todo }finally{
CacheExtension.getInstance().Remove(uniqueKey);}
总结
1、这样的话,就能在旧的还未完成之前,新的进来,直接return,保证唯一性,也支持了分布式处理。
2、分布式锁在很多场景,还是很实用的。