繁体   English   中英

在自托管 ASP.NET Core 微服务中启动多个后台线程

[英]Start multiple background threads inside Self Hosted ASP.NET Core Microservice

我在哪里可以在Self Hosted Self Contained ASP.NET Core Microservice微服务中创建多个长时间运行的后台线程,其生命周期与微服务生命周期相同? 这样从线程中检索到的信息可以作为对请求的响应发送。

尝试了给定的代码,但当后台线程忙时,它会降低 http 请求的性能。 Program.cs 文件的主要方法是:

static void Main(string[] args)
{
    //Start background thread1
    //Start background thread2
    //Around 10 background threads
    //Start host
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseUrls(ServerUrl)
        .UseConfiguration(config)
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .ConfigureServices(s => s.AddRouting())
        .Configure(app => app.UseRouter(r => { (new Router()).Route(r); }))
        .Build();
    host.Run();
}

线程以这种方式工作:

Thread t1 = new Thread(StartWork);
t1.IsBackground = true;
t1.Start();

public void StartWork()
{
    while (ApplicationIsRunning)
    {
        //Get database info >> login into remote devices (SSH) >> get information >> process information >> update application variables and database
        Thread.Sleep(10000);
    }
}

当线程繁忙但 http 请求性能仍然很差时,CPU 利用率仅为 1-5%。 进入睡眠状态后,性能再次提高。

问题在于 SSH 客户端连接的连接方法。 在某些时候,connect 方法没有响应,它也会影响所有其他线程。 这很奇怪!

Renci.SshNet.SshClient sshClient = New Renci.SshNet.SshClient(sshConnectionInfo);
sshClient.Connect();

如果一个线程由于任何原因忙于连接,则不应影响其他线程。

编辑:代码可能来自 Steve Gordon 在https://www.stevejgordon.co.uk/asp-net-core-2-ihostedservice的帖子?

肯定可以 :) 使用 IHostedService(来自 .net 核心的开箱即用),您可以实现以下功能:

public abstract class HostedService : IHostedService
    {

        private Task _executingTask;
        private CancellationTokenSource _cts;

        public Task StartAsync(CancellationToken cancellationToken)
        {
            // Create a linked token so we can trigger cancellation outside of this token's cancellation
            _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

            // Store the task we're executing
            _executingTask = ExecuteAsync(_cts.Token);

            // If the task is completed then return it, otherwise it's running
            return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask;
        }

        public async Task StopAsync(CancellationToken cancellationToken)
        {
            // Stop called without start
            if (_executingTask == null)
            {
                return;
            }

            // Signal cancellation to the executing method
            _cts.Cancel();

            // Wait until the task completes or the stop token triggers
            await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken));

            // Throw if cancellation triggered
            cancellationToken.ThrowIfCancellationRequested();
        }

        // Derived classes should override this and execute a long running method until 
        // cancellation is requested
        protected abstract Task ExecuteAsync(CancellationToken cancellationToken);
    }


then you can implement your abstract class:


public class DataRefreshService : HostedService
{
    private readonly RandomStringProvider _randomStringProvider;

    public DataRefreshService(RandomStringProvider randomStringProvider)
    {
        _randomStringProvider = randomStringProvider;
    }

    protected override async Task ExecuteAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            await _randomStringProvider.UpdateString(cancellationToken);
            await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
        }
    }
}

在您的设置中,您只需要添加依赖项:

services.AddSingleton<IHostedService, DataRefreshService>();

RandomStringProvider只是一个例子。 你得到了图片:)

.net 核心线自动为您,工作就像一个魅力! 保持rabbitmq连接打开的完美选择!

试一试!

尝试了给定的代码,但当后台线程忙时,它会降低 http 请求的性能。

首先要意识到的是,在 Web 应用程序的上下文中没有真正的后台工作。 Web 服务器旨在快速为请求提供服务。 有一个线程池,通常由多达 1000 个线程组成(通常称为服务器的“最大请求”,因为每个请求都需要一个线程)。 该线程池是一种有限资源,当您将其最大化时,任何进一步的请求都会排队,直到线程再次可用。 启动一个新线程需要从这个池中取出一个线程。 因此,您的一个请求现在消耗了两个线程而不是一个线程。 做这种类型的事情就足够了,你可以很容易地用少量的请求耗尽线程池,然后让你的 Web 服务器瘫痪。

至少,如果您正在执行某种异步工作,您可能会允许服务请求的主线程在这个新线程执行其操作时返回到池中,但即便如此,您也只是在用一个线程交换另一个线程。 然而,在这里,你甚至没有这样做。 您的代码在此处处于阻塞状态,因此当您的线程池已经处于饥饿状态时,您现在已经让线程处于空闲状态。

长期和短期,不要这样做。 如果有任何需要完成的工作需要花费相当多的时间,则该工作应该卸载到后台进程,即在您的 Web 应用程序上下文之外运行的可以在不影响您的 Web 性能的情况下完成工作的东西应用程序。 您几乎不应该在 Web 应用程序中创建新线程。 如果您发现自己这样做,则需要重新考虑您的设计。

暂无
暂无

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

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