Asp.net 为什么Hangfire在轮询sql server查找作业时每隔几秒钟等待15秒?
我继承了一个使用Hangfire和sql server作业存储的系统。通常,当计划立即运行作业时,我们会注意到它需要几秒钟才能触发 在我的开发环境中运行SQL Profiler时,针对Hangfire db运行的SQL如下所示-Asp.net 为什么Hangfire在轮询sql server查找作业时每隔几秒钟等待15秒?,asp.net,.net,hangfire,Asp.net,.net,Hangfire,我继承了一个使用Hangfire和sql server作业存储的系统。通常,当计划立即运行作业时,我们会注意到它需要几秒钟才能触发 在我的开发环境中运行SQL Profiler时,针对Hangfire db运行的SQL如下所示- exec sp_executesql N'delete top (1) JQ output DELETED.Id, DELETED.JobId, DELETED.Queue from [HangFire].JobQueue JQ with (readpast, updl
exec sp_executesql N'delete top (1) JQ
output DELETED.Id, DELETED.JobId, DELETED.Queue
from [HangFire].JobQueue JQ with (readpast, updlock, rowlock, forceseek)
where Queue in (@queues1) and (FetchedAt is null or FetchedAt < DATEADD(second, @timeout, GETUTCDATE()))',N'@queues1 nvarchar(4000),@timeout float',@queues1=N'MYQUEUENAME_master',@timeout=-1800
-- Exactly the same SQL as above is executed about 6 times/second for about 3-4 seconds,
-- then nothing for about 2 seconds, then:
exec sp_getapplock @Resource=N'HangFire:recurring-jobs:lock',@DbPrincipal=N'public',@LockMode=N'Exclusive',@LockOwner=N'Session',@LockTimeout=5000
exec sp_getapplock @Resource=N'HangFire:locks:schedulepoller',@DbPrincipal=N'public',@LockMode=N'Exclusive',@LockOwner=N'Session',@LockTimeout=5000
exec sp_executesql N'select top (@count) Value from [HangFire].[Set] with (readcommittedlock, forceseek) where [Key] = @key and Score between @from and @to order by Score',N'@count int,@key nvarchar(4000),@from float,@to float',@count=1000,@key=N'recurring-jobs',@from=0,@to=1596053348
exec sp_executesql N'select top (@count) Value from [HangFire].[Set] with (readcommittedlock, forceseek) where [Key] = @key and Score between @from and @to order by Score',N'@count int,@key nvarchar(4000),@from float,@to float',@count=1000,@key=N'schedule',@from=0,@to=1596053348
exec sp_releaseapplock @Resource=N'HangFire:recurring-jobs:lock',@LockOwner=N'Session'
exec sp_releaseapplock @Resource=N'HangFire:locks:schedulepoller',@LockOwner=N'Session'
-- Then nothing is executed for about 8-10 seconds, then:
exec sp_executesql N'update [HangFire].Server set LastHeartbeat = @now where Id = @id',N'@now datetime,@id nvarchar(4000)',@now='2020-07-29 20:09:19.097',@id=N'ps12345:19764:fe362d1a-5ee4-4d97-b70d-134fdfab2b87'
-- Then about 500ms-2s later I get
exec sp_executesql N'delete top (1) JQ ... -- i.e. Same as first query
The update LastHeartbeat query is only there every second time (from just a brief inspection, maybe that’s not exactly right).
但如果说有什么问题的话,现在的问题更糟了。或者相同但更快:20次调用delete top(1)JQ…
现在大约在1s内发生,然后是其他查询,然后是15秒等待,然后又重新开始
需要明确的是,主要的问题是,如果在15秒延迟期间添加了任何作业,那么在我的作业执行之前,需要15秒的剩余时间。我认为第二个问题是,它对SQL Server的影响超出了需要:一秒钟20次有点大,至少对我的需要来说是这样
(交叉发布到)我建议查看Hangfire
BackgroundJobServerOptions
查看您在那里设置的轮询间隔。这将定义hangfire服务器检查队列中是否有要执行的作业之前的时间
来自文档
Hangfire Server会定期检查计划,将计划的作业排入其队列,从而允许工作人员
执行它们。默认情况下,检查间隔等于15秒,但您可以通过在传递给BackgroundJobServer构造函数的选项上设置SchedulePollingInterval属性来更改它:
如果未设置
QueuePollInterval
,则使用sql server存储的Hangfire默认为每15秒轮询一次。因此,如果出现此问题,首先要将QueuePollInterval
设置为较小的值,例如1s
但在我的情况下,即使我设定它没有任何效果。原因是在调用app.UseHangfireServer()
之前,我使用SqlServerStorageOptions
调用了GlobalConfiguration.Configuration.UseSqlServerStorage()
调用app.UseHangfireServer()
时,它使用JobStorage.current的当前值。我的代码设置为:
var storage = new SqlServerStorage(connstring);
JobStorage.Current = storage;
后来打电话来
app.UseHangfireServer()
GlobalConfiguration.Configuration
.UseSqlServerStorage(connstring, new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
UseRecommendedIsolationLevel = true,
PrepareSchemaIfNecessary = true,
EnableHeavyMigrations = true
})
后来打电话来
app.UseHangfireServer()
GlobalConfiguration.Configuration
.UseSqlServerStorage(connstring, new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
UseRecommendedIsolationLevel = true,
PrepareSchemaIfNecessary = true,
EnableHeavyMigrations = true
})
将其重新排序以在app之前使用SqlServerStorageOptions
。UseHangfireServer()
意味着SqlServerStorageOptions
生效。感谢您的指针,但myBackgroundJobServerOptions
仅设置了队列
属性。我不太明白投票间隔是用来做什么的,所以我会去仔细阅读。好了,现在我明白了。不,在这种情况下,我们通常会触发立即运行作业,因此您所说的15秒轮询不应该相关,因为这是为了将作业添加到应该很快运行的队列中,例如,如果它们是像BackgroundJob.Schedule(()=>Console.WriteLine(“Hello,world”)、TimeSpan.FromDays(1)这样创建的代码>您可以运行BackgroundJob.Schedule或BackgroundJob.Enqueue。。。两者使用相同的轮询间隔。一定要测试一下,看看这对你的处境是否有帮助。BackgroundJobServeroptions中的大多数值都设置为默认值,但您可以将这些值覆盖为默认值以外的值(new TimeSpan(0,0,5)
5秒间隔,然后Hangfire服务器拾取下一个作业)。谢谢,我将详细介绍。但总的来说,您不希望Hangfire不断地轮询sqlserver以获得新的工作吗?我是说,为什么它会停止投票?这是在没有作业可执行的情况下。。。因此,当我们添加一份工作时,在开始之前会有一个很大的停顿,因为Hangfire没有持续进行投票。我之所以授予奖金,是因为你的回答为我指明了正确的方向,尽管没有直接解决问题。我将发布一个答案来解释更多。根据来源中的这一行,将QueuePollInterval设置为0会使系统切换到长轮询。我建议选择一个严格高于1s的值,比如2s,来改变等待策略,看看它是否有区别(除了删除转换为更新)。谢谢,我会尝试一下。仍然无法解释为什么它会经常等待10秒,很明显,无论它使用什么策略,这都是不好的。但也许战略的改变意味着它无论如何都会停止这样做。