繁体   English   中英

命名信号量在 Azure App Service 上托管的 ASP.Net Core 5 Web API 上不起作用

[英]Named semaphore not working on ASP .Net Core 5 Web API hosted on Azure App Service

我有一个运行在 Azure App Service 上的 ASP.Net Core 5 Web API。 它每天运行一次 Hosted BackgroundService。 因为我只希望此服务每天运行一次(凌晨 2 点),并且因为我的 App Service 已打开自动扩展(当 CPU 或 Memory > 70% 时将实例增加 1,最多 3 个实例) ,我实现了一个命名信号量,以防止多个实例同时运行。

这是托管后台服务的代码。 我对其进行了一些简化以显示要点。

public class MyBackgroundService : BackgroundService
{
    private Timer _timer;
    private readonly ILogger<MyBackgroundService> _logger;

    public MyBackgroundService(ILogger<MyBackgroundService> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        TimeSpan timeTillNextRun = CalculateTimeTillNextRun();
        _timer = new Timer(DoWork, null, timeTillNextRun, new TimeSpan(1, 0, 0, 0));
    }

    private async void DoWork(object state)
    {
        try
        {
            _timer.Change(Timeout.Infinite, Timeout.Infinite);
            await RunAsync();
        }

        finally
        {
            TimeSpan timeTillNextRun = CalculateTimeTillNextRun();
            _timer.Change(timeTillNextRun, new TimeSpan(1, 0, 0, 0));
        }
    }

    private async Task RunAsync()
    {
        using (Semaphore semaphore = new Semaphore(1, 1, "MyBackgroundService"))
        {
            if (!semaphore.WaitOne(1))
            {
                return;
            }

            try
            {
                _logger.LogInformation(null, "Started.");                    
                // Do stuff
            }

            finally
            {
                semaphore.Release();
            }
        }
    }
}

所以我有一个定时器,每天凌晨 2 点触发。 当计时器触发时,我暂停它,然后调用 RunAsync()。 在 RunAsunc() 中,我设置了一个命名的(系统)信号量。 我只等待 1 毫秒,因为如果信号量正在使用中,我什至不想等待它完成,因为这个后台服务应该每天只运行一次。

但是,当我检查日志时,我看到它最多同时运行 6 次(好吧,在大约 3 秒内),每个服务运行两次。

id, machine_name, status, time_stamp
'9577', 'RD2818786D33F4', 'Started.', '2021-08-16 02:00:06'
'9578', 'RD2818786D33F4', 'Started.', '2021-08-16 02:00:08'
'9579', 'RD2818786D0367', 'Started.', '2021-08-16 02:00:08'
'9580', 'RD2818786D1D19', 'Started.', '2021-08-16 02:00:09'
'9581', 'RD2818786D0367', 'Started.', '2021-08-16 02:00:09'
'9582', 'RD2818786D1D19', 'Started.', '2021-08-16 02:00:10'

正如您在上面看到的,它首先在 01:00:06 运行,然后 2 秒后在同一台机器上再次运行,然后立即在另一台机器上再次运行,然后一秒后又运行两次,最后一秒又运行一次。

现在,查看“machine_name”列,应用服务的每个实例似乎都在不同的 VM 上运行。 我想,因为所有实例都在同一个服务计划中,所以它应该是同一个虚拟机,但我想我错了。 所以,我想这就是信号量不工作的原因——它不能在不同的机器上工作。 奇怪的是它似乎也不在同一台机器上工作。 正如您在上面的日志中看到的,RunAsync() 在每个实例上运行了两次。 现在它并不总是那样。 例如,这是前一天的日志:

id, machine_name, status, time_stamp
'9569', 'RD2818786D2D5E', 'Started.', '2021-08-15 02:00:05'
'9570', 'RD2818786D2D5E', 'Started.', '2021-08-15 02:00:06'

在这里它只运行了两次,一次。 同样,它应该只运行一次——尤其是在同一台机器上。

我使用信号量错了吗? 我知道它可能无法跨 VM 使用,但即使在同一台 VM 上似乎也无法使用。

任何建议将不胜感激。 谢谢。

Ps 我知道我最好从 API 中删除此托管服务并将其粘贴在 Azure Function 或 Azure WebJob 中。 事实上,我会尽快这样做。 但我仍然很想知道为什么我的信号量不工作。

信号量似乎每次都在创建一个新的信号量。 我不知道为什么。 这发生在不同的进程上,甚至发生在单个进程上。 按照 Steeeve 的建议,我在信号量名称中添加了“Global//”前缀,但没有任何区别。 我什至在信号量构造函数中记录了第四个参数(布尔参数)的值,再次按照 Steeeve 的建议,看看它是否真的每次都在创建一个新的信号量,它真的是。 我尝试了多种方法,甚至从 Using() 块中删除了信号量(因为我认为这可能以不应该的方式破坏了信号量),但没有任何区别。

最终我像最初一样回到了 SemaphoreSlim。 这并没有给我创建命名/系统信号量的选项,就像 Semaphore 所做的那样,但至少它适用于单个进程。

看起来 Semaphore 根本无法在 Azure App Service 上托管的应用程序上运行,至少有一个像我的一样设置为 Auto Scale Out。 我可以理解这一点,因为看起来 Azure 在扩展时为应用程序的每个实例创建了一个新的 VM(至少有时 - 我不能保证总是这样)但奇怪的是它似乎没有甚至在同一个虚拟机上工作。 根据我的理解,命名/系统信号量在操作系统级别工作——即它应该在机器内跨进程工作。

谢谢大家的帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM