我在多个区域部署了一个计时器触发的 WebJob,并且它在给定的计划时间从所有区域同时触发,如何确保一次仅运行一个作业实例? 我尝试应用
Singleton
属性和 "is_singleton": true
但仍然从所有区域触发。
还有其他方法可以实现这一目标吗?这个 link 表示 Singleton 属性不再适用于此目的,而且我没有看到在 azure blob 存储中创建任何锁定文件。如果这是真的,我们如何实现这一点以确保多个区域仅触发一个区域?或者如果有任何其他内置方法可以使用 WebJob SDK 实现此目的,这对我来说真的很有帮助。
Program.cs
:
var builder = new HostBuilder();
builder
.ConfigureWebJobs((context, b) =>
{
b.AddAzureStorageCoreServices();
});
var host = builder.Build();
using (host)
{
var jobHost = host.Services.GetService(typeof(IJobHost)) as JobHost;
await host.StartAsync().ConfigureAwait(false);
await jobHost.CallAsync("Run").ConfigureAwait(false);
await host.StopAsync().ConfigureAwait(false);
}
Function.cs
:
[Singleton]
[NoAutomaticTrigger]
public async Task Run()
{
}
settings.job
:
{
"schedule": "0 */5 * * * *",
"is_singleton": true
}
NuGet 包:
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions" Version="4.0.1" />
所以听起来您希望每个区域都运行,但只是不同时运行。
一个简单的解决方案是错开它们,这样它们就不会同时运行,但你给每个人一个稍微不同的 cron 时间,例如彼此相距 10m。
但是,假设您无法预测它们将运行多长时间,或者您本质上希望它们运行得比最小间隔 10m 快得多,并且假设它们都可以访问数据库(例如 Azure Cosmos Mongo),那么您可以添加一个表并一些简单的逻辑,本质上是对“锁定”模式进行建模。
findOneAndUpdate
函数对众所周知的文档进行原子更新,这将允许一次只有一个进程“锁定”文档。
本例中的数据库包含一个集合(又名表)和一个文档(又名行),如下所示:
interface ILock {
state: 'UNLOCKED' | 'LOCKED';
lockedBy: null | string;
lockedAt: null | Date;
}
while (true)
{
// todo: add some kind of timeout here so it doesn't run forever.
// `findOneAndUpdate` is atomic, if multiple processes
// attempt to do this modification simultaneously
// only one will succeed, the others will get an `undefined`
// result to indicate the document was not found.
var lock = await this.db.locks.findOneAndUpdate(
{ state: 'UNLOCKED' },
{
$set: {
state: 'LOCKED',
lockedBy: this.jobId,
lockedAt: new Date()
}
}
)
if (lock) {
try {
// you have the lock, do your thing...
await DoWork();
// you are done, exit the loop.
return;
} finally {
// don't forget to unlock!
await this.db.locks.findOneAndUpdate(
{ state: 'LOCKED' },
{
$set: {
state: 'UNLOCKED',
}
}
)
}
} else {
// you are not the one neo, take the blue pill...
await sleep(3000)
}
}