【问题标题】:Worker Service mysteriously stops doing work工人服务神秘地停止工作
【发布时间】:2021-02-05 12:54:57
【问题描述】:

尊敬的女士们,先生们,我最近在 .Net Core 3.1 中第一次使用 Worker 服务,而在一般的 Windows 服务中只进行了第二次(第一次是在 .Net Framework 中制作的,直到今天都可以正常工作) .如果有人可以阐明我将提供的示例中缺少的内容,那就太好了。

所以,为了简单起见,我的问题是: 我假设长时间(永远)运行的 Worker 服务在一天中的任意时间意外停止工作,但在服务管理器中仍显示为“正在运行”(这可能是 Windows 处理服务的方式)。不一定要每天都这样,但它会时不时停止工作,直到我手动停止它然后在服务管理器中重新启动它。

我也有stumbled upon this question,它似乎可以解决我的问题,但即使在将我的所有服务代码块完全包装在 try-catch 中之后,即使在顶层,我仍然没有在我的 Log 表中注册任何内容,或者如果我的数据库连接失败,即使在我设置写入的文件中。服务似乎只是停止调用 ExecuteAsync() 方法。

好的,这是我的代码的逻辑结构,我已经排除了实现,我只是展示在调用 DoWork 之前会发生什么:

public class Worker : BackgroundService
{
    private readonly IConfiguration _configuration;
    public Worker(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public override Task StartAsync(CancellationToken cancellationToken)
    {                      
        return base.StartAsync(cancellationToken);
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            while (true)
            {
                try //paranoid try-catch
                {
                    await DoWork();
                    await Task.Delay(TimeSpan.FromSeconds(45), stoppingToken);
                }
                catch (Exception e)
                {
                    await Log(e, customMessage: "Proccess failed at top level.");
                }
            }
        }
        catch (Exception e)
        {
            await Log(e, customMessage: "Proccess failed at topmost level.");
        }

    }

    private async Task DoWork()
    {
        try
        {
            
        }
        catch (Exception e)
        {
            await Log(e);
        }
    }

    public async Task Log(Exception e, string user = null, string emailID = null, string customMessage = null)
    {
        
    }
}

如您所见,我没有处理取消,就像我上面链接的问题一样。现在我考虑一下,也许我应该这样做,而某些事情是无意中发送了取消?我没有这样做的原因是因为我不确定哪些事件确切地表明了取消。只有手动停止服务,或者其他什么可能?如果是发送的取消导致我的服务停止工作,它不应该也停止我的服务运行吗?

顺便说一句,我刚刚在虚拟服务上测试了取消,它使用while(true) 实现了我的逻辑,它捕获了停止异常,即使它有点尴尬,因为它捕获它并在停止之前多次记录它,所以我认为它可能不是导致我的 DoWork 不触发的取消令牌。

【问题讨论】:

  • 您检查过Stephen C.'s answer 中的改进了吗?考虑到 stopToken 是其中之一,但我专门针对他指的是停止应用程序主机的部分。并在处理异常时记下令牌的状态。
  • 可能是例如死锁。然后对于 Windows,您的服务确实仍在运行,而实际上它被卡在某个地方并且没有做任何有用的事情(并且没有例外)。
  • 如果您的日志记录功能失败怎么办?是写SQL还是什么?如果出现短暂的网络错误,或者连续出现两个 SQL 死锁,那么它将停止
  • @Fildor 尽管在我看来我确实犯了一个错误并将托管服务视为应用程序,但我认为这不会让我担心,因为我希望我的 BackgroundService 永远不会退出。但是,我仍然认为添加实施该附件在逻辑上是正确的。我认为 Evk 更符合可能发生的情况,因为我的服务严重依赖异步。
  • 我会首先确保您在每次此类呼叫时都有超时。因为如果您通过 TCP 连接(即 http,通常是数据库和许多其他调用)发送一些请求并且没有超时 (0),并且连接意外中断 - 您将永远等待回复。因此,对于任何此类操作,都应该有一个合理的超时设置(通常与数据库一起使用的 api、http 调用等都提供了一种设置方法)。如果您使用任何类型的锁(lock 等,或异步类似物),那么当然也要检查它们。

标签: c# .net-core async-await worker


【解决方案1】:

好的,伙计们,我已经修好了。见下面的评论。

猜想导致死锁的原因可能是通过相同连接从不同线程到数据库的并发调用过多。
并不是说我知道会是原因(我仍然不知道并且只能猜测为什么会发生这种情况,所以如果有人能澄清为什么会发生这种情况以及为什么不打电话请排队),但是当我尝试修复它时,这似乎是一个很好的起点。

我所做的只是将可能的并发调用限制为 1:

  1. 类级别上实例化 SemaphoreSlim:
    private static SemaphoreSlim Semaphore = new SemaphoreSlim(1);
  2. 在我的每个数据库调用之前插入SemaphoreSlim.WaitAsync,并在调用后的 finally 块中插入其各自的SemaphoreSlim.Release
try
{
    await Semaphore.WaitAsync();
    var id = await sqlCommand.ExecuteScalarAsync().ToString();
}
finally
{
    Semaphore.Release();
}

我认为这会降低性能,但令我惊喜的是,我并没有感觉到明显的差异。

此外,我很想将 Semaphore 的初始计数设置为超过 1 个线程,但我认为如果死锁发生在许多线程上,那么它可能发生在 2-10 个线程上。有人可能对这个数字有更多了解吗?是处理器相关、SQL 相关还是 C# 相关?

【讨论】:

  • 这不是主要的。经过 5 天的连续工作(迄今为止最长的一次),该服务昨天再次挂起。我想等待 SQL 调用会有所帮助,但我想我也必须等待对 EWS(Exchange API)的调用,因为我读到 EWS 在面临重度并行时表现得可疑。最终会更新。
  • 您应该将await Semaphore.WaitAsync(); 移到try/finally 块之外。否则,获取信号量失败可能导致释放未获取的信号量,从而无意中增加了最大并行度。
  • @TheodorZoulias 我想我明白你在说什么。我以为我在 Microsoft 文档的 try 块中看到了它,但现在再看一眼,MS 文档也将它放在 try 块之上。感谢您的提示,这是有道理的,尽管到目前为止我的日志没有显示它发生的迹象。
【解决方案2】:

你是否实现了一个 dispose 方法在完成 DoWork 方法后关闭数据库连接?我在使用工作服务时遇到了死锁问题,并意识到数据库连接没有被释放。实现 dispose 方法后,它对我来说可以解决问题。

【讨论】:

  • 嘿,我没有在我的 SqlConnection 实例上显式调用 dispose 方法,但它包含在 Using() 语句中。这还不够吗?在这些 Using 语句中,我调用了一个 connection.Open(),没有在 Using() 结束时关闭它,但我认为这不应该是连接池的问题?
猜你喜欢
  • 1970-01-01
  • 2014-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多