ScheduleMasterCore
ScheduleMasterCore copied to clipboard
程序意外退出锁没有释放的问题
某个worker调度任务时通过DabaseLock.TryGetLock()将schedulelocks字段status设置为1表示获取到锁,
如果在没有执行DabaseLock.Dispose()方法之前worker崩溃意外退出; 这样会导致schedulelocks中的status没有设置为0,也就是锁没有被释放,当再次启动worker会发现任务永远不会被调度,因为DabaseLock.TryGetLock获取不到锁。 目前我临时的解决方法是在管理后台点击【停止】按钮时将该任务的锁释放掉。博主有好的解决方案没?
目前这种基于数据库的分布式锁实现确实有这个不能释放的问题,简单的处理办法可以加一个后台扫描线程,把持有锁超过X时间的强制释放掉。 因为后期规划了使用redis来实现这一块,所以暂时没有优化。 有兴趣可以参与进来。
mysql的话可以加事务避免出现僵尸锁,测试过,拔网线和杀死worker都能避免,talk is cheap ,show my code: RootJob.cs的Execute改造:
public async Task Execute(IJobExecutionContext context)
{
IJobDetail job = context.JobDetail;
if (job.JobDataMap["instance"] is IHosSchedule instance)
{
_sid = Guid.Parse(context.JobDetail.Key.Name);
using (var scope = new ScopeDbContext())
{
_tracer = scope.GetService<RunTracer>();
var locker = scope.GetService<HosLock.IHosLock>();
// 预防出现僵死锁,开始事务(使用DbContext的异步事务处理)
using (var transaction = await scope.GetDbContext().Database.BeginTransactionAsync())
{
try
{
//设置锁超时1秒
scope.GetDbContext().Database.ExecuteSqlRaw(@"SET SESSION innodb_lock_wait_timeout = 1");
if (locker.TryGetLock(context.JobDetail.Key.Name))
{
await InnerRun(context);
// 如果InnerRun成功,则提交事务
await transaction.CommitAsync();
}
else
{
//await transaction.RollbackAsync();
throw new JobExecutionException("lock_failed");
}
}
catch (Exception e)
{
LogHelper.Error($"任务\"{instance.Main.Title}\"-{job.Key}拿锁失败!", e);
// 出现异常时,事务应自动回滚,但显式调用RollbackAsync以确保事务状态明确
if (e is JobExecutionException)
{
//这个错误不会滚
}
else
{
try
{
await transaction.RollbackAsync();
}
catch (Exception rollbackEx)
{
LogHelper.Error("事务回滚时发生错误!", rollbackEx);
}
}
throw e;
}
finally
{
// 恢复 lock wait timeout 为默认50(mysql 8.0默认为50秒)
scope.GetDbContext().Database.ExecuteSqlRaw(@"SET SESSION innodb_lock_wait_timeout = 50");
}
}
}
}
}
你所发的邮件已收到,查阅后再回复!