Azure Webjob 的 TimerTrigger 跨多个区域的单例锁

问题描述 投票:0回答:1

我在多个区域部署了一个计时器触发的 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" />
c# .net azure-blob-storage azure-webjobs azure-webjobssdk
1个回答
0
投票

所以听起来您希望每个区域都运行,但只是不同时运行。

一个简单的解决方案是错开它们,这样它们就不会同时运行,但你给每个人一个稍微不同的 cron 时间,例如彼此相距 10m。

但是,假设您无法预测它们将运行多长时间,或者您本质上希望它们运行得比最小间隔 10m 快得多,并且假设它们都可以访问数据库(例如 Azure Cosmos Mongo),那么您可以添加一个表并一些简单的逻辑,本质上是对“锁定”模式进行建模。

在 mongodb 中,您可以使用

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)
  }
  
}
© www.soinside.com 2019 - 2024. All rights reserved.