Aracılığıyla paylaş


Kuyruk Hizmeti Oluşturma

Kuyruk hizmeti, önceki iş öğeleri tamamlandıkça iş öğelerinin sıralı olarak kuyruğa alınabildiği ve üzerinde çalışılabildiği, uzun süre çalışan bir hizmet için harika bir örnektir. Çalışan Hizmeti şablonuna bağlı olarak, üzerinde BackgroundServiceyeni işlevler oluşturursunuz.

Bu öğreticide aşağıdakilerin nasıl yapılacağını öğreneceksiniz:

  • Bir kuyruk hizmeti oluşturun.
  • Görevi görev kuyruğuna devretme.
  • Olaylardan bir konsol anahtar dinleyicisi IHostApplicationLifetime kaydedin.

Bahşiş

".NET'te Çalışanlar" örnek kaynak kodunun tümü, indirilebilmeniz için Samples Browser'da bulunur. Daha fazla bilgi için bkz . Kod örneklerine göz atma: .NET'te çalışanlar.

Önkoşullar

Yeni proje oluşturma

Visual Studio ile yeni bir Çalışan Hizmeti projesi oluşturmak için Dosya>Yeni>Proje... seçeneğini belirlemeniz gerekir. Yeni proje oluştur iletişim kutusunda "Çalışan Hizmeti" araması yapın ve Çalışan Hizmeti şablonu'na tıklayın. .NET CLI kullanmayı tercih ediyorsanız, sık kullandığınız terminali çalışma dizininde açın. dotnet new komutunu çalıştırın ve öğesini istediğiniz proje adıyla değiştirin<Project.Name>.

dotnet new worker --name <Project.Name>

.NET CLI yeni çalışan hizmeti projesi komutu hakkında daha fazla bilgi için bkz . dotnet new worker.

Bahşiş

Visual Studio Code kullanıyorsanız tümleşik terminalden .NET CLI komutlarını çalıştırabilirsiniz. Daha fazla bilgi için bkz . Visual Studio Code: Tümleşik Terminal.

Kuyruğa alma hizmetleri oluşturma

Ad alanından işlevselliği System.Web.Hosting hakkında QueueBackgroundWorkItem(Func<CancellationToken,Task>) bilgi sahibi olabilirsiniz.

Bahşiş

Ad alanının işlevselliği System.Web kasıtlı olarak .NET'e geçirilmemiştir ve .NET Framework'e özel olarak kalır. Daha fazla bilgi için bkz . ASP.NET Core geçişi için artımlı ASP.NET kullanmaya başlama.

.NET'te, işlevden esinlenen QueueBackgroundWorkItem bir hizmeti modellemek için projeye bir IBackgroundTaskQueue arabirim ekleyerek başlayın:

namespace App.QueueService;

public interface IBackgroundTaskQueue
{
    ValueTask QueueBackgroundWorkItemAsync(
        Func<CancellationToken, ValueTask> workItem);

    ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(
        CancellationToken cancellationToken);
}

Biri kuyruğa alma işlevselliğini kullanıma sunan iki yöntem ve daha önce kuyruğa alınmış iş öğelerinin sırasını kaldıran bir yöntem vardır. İş öğesi bir Func<CancellationToken, ValueTask>öğesidir. Ardından, varsayılan uygulamayı projeye ekleyin.

using System.Threading.Channels;

namespace App.QueueService;

public sealed class DefaultBackgroundTaskQueue : IBackgroundTaskQueue
{
    private readonly Channel<Func<CancellationToken, ValueTask>> _queue;

    public DefaultBackgroundTaskQueue(int capacity)
    {
        BoundedChannelOptions options = new(capacity)
        {
            FullMode = BoundedChannelFullMode.Wait
        };
        _queue = Channel.CreateBounded<Func<CancellationToken, ValueTask>>(options);
    }

    public async ValueTask QueueBackgroundWorkItemAsync(
        Func<CancellationToken, ValueTask> workItem)
    {
        ArgumentNullException.ThrowIfNull(workItem);

        await _queue.Writer.WriteAsync(workItem);
    }

    public async ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(
        CancellationToken cancellationToken)
    {
        Func<CancellationToken, ValueTask>? workItem =
            await _queue.Reader.ReadAsync(cancellationToken);

        return workItem;
    }
}

Önceki uygulama bir kuyruk olarak dayanır Channel<T> . BoundedChannelOptions(Int32), açık bir kapasiteyle çağrılır. Kapasite beklenen uygulama yüküne ve kuyruğa erişen eşzamanlı iş parçacıklarının sayısına göre ayarlanmalıdır. BoundedChannelFullMode.Wait çağrısının bir görevi döndürmesine ChannelWriter<T>.WriteAsync neden olur ve bu işlem yalnızca kullanılabilir alan olduğunda tamamlanır. Bu da çok fazla yayımcı/çağrının birikmeye başlaması durumunda geri baskıya yol açar.

Worker sınıfını yeniden yazma

Aşağıdaki QueueHostedService örnekte:

  • ProcessTaskQueueAsync yöntemi içinde ExecuteAsyncbir Task döndürür.
  • Kuyruktaki arka plan görevleri içinde sıralanır ve yürütülür ProcessTaskQueueAsync.
  • hizmet uygulamasında durdurulmadan StopAsyncönce iş öğeleri beklenir.

Mevcut Worker sınıfı aşağıdaki C# koduyla değiştirin ve dosyayı QueueHostedService.cs olarak yeniden adlandırın.

namespace App.QueueService;

public sealed class QueuedHostedService(
        IBackgroundTaskQueue taskQueue,
        ILogger<QueuedHostedService> logger) : BackgroundService
{
    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation("""
            {Name} is running.
            Tap W to add a work item to the 
            background queue.
            """,
            nameof(QueuedHostedService));

        return ProcessTaskQueueAsync(stoppingToken);
    }

    private async Task ProcessTaskQueueAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                Func<CancellationToken, ValueTask>? workItem =
                    await taskQueue.DequeueAsync(stoppingToken);

                await workItem(stoppingToken);
            }
            catch (OperationCanceledException)
            {
                // Prevent throwing if stoppingToken was signaled
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Error occurred executing task work item.");
            }
        }
    }

    public override async Task StopAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation(
            $"{nameof(QueuedHostedService)} is stopping.");

        await base.StopAsync(stoppingToken);
    }
}

Bir MonitorLoop hizmet, giriş cihazında anahtar seçildiğinde barındırılan w hizmet için sıralama görevlerini işler:

  • IBackgroundTaskQueue hizmetine eklenirMonitorLoop.
  • IBackgroundTaskQueue.QueueBackgroundWorkItemAsync bir iş öğesini sıralamak için çağrılır.
  • İş öğesi, uzun süre çalışan bir arka plan görevinin benzetimini gerçekleştirir:
namespace App.QueueService;

public sealed class MonitorLoop(
    IBackgroundTaskQueue taskQueue,
    ILogger<MonitorLoop> logger,
    IHostApplicationLifetime applicationLifetime)
{
    private readonly CancellationToken _cancellationToken = applicationLifetime.ApplicationStopping;

    public void StartMonitorLoop()
    {
        logger.LogInformation($"{nameof(MonitorAsync)} loop is starting.");

        // Run a console user input loop in a background thread
        Task.Run(async () => await MonitorAsync());
    }

    private async ValueTask MonitorAsync()
    {
        while (!_cancellationToken.IsCancellationRequested)
        {
            var keyStroke = Console.ReadKey();
            if (keyStroke.Key == ConsoleKey.W)
            {
                // Enqueue a background work item
                await taskQueue.QueueBackgroundWorkItemAsync(BuildWorkItemAsync);
            }
        }
    }

    private async ValueTask BuildWorkItemAsync(CancellationToken token)
    {
        // Simulate three 5-second tasks to complete
        // for each enqueued work item

        int delayLoop = 0;
        var guid = Guid.NewGuid();

        logger.LogInformation("Queued work item {Guid} is starting.", guid);

        while (!token.IsCancellationRequested && delayLoop < 3)
        {
            try
            {
                await Task.Delay(TimeSpan.FromSeconds(5), token);
            }
            catch (OperationCanceledException)
            {
                // Prevent throwing if the Delay is cancelled
            }

            ++ delayLoop;

            logger.LogInformation("Queued work item {Guid} is running. {DelayLoop}/3", guid, delayLoop);
        }

        if (delayLoop is 3)
        {
            logger.LogInformation("Queued Background Task {Guid} is complete.", guid);
        }
        else
        {
            logger.LogInformation("Queued Background Task {Guid} was cancelled.", guid);
        }
    }
}

Mevcut Program içeriği aşağıdaki C# koduyla değiştirin:

using App.QueueService;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<MonitorLoop>();
builder.Services.AddHostedService<QueuedHostedService>();
builder.Services.AddSingleton<IBackgroundTaskQueue>(_ => 
{
    if (!int.TryParse(builder.Configuration["QueueCapacity"], out var queueCapacity))
    {
        queueCapacity = 100;
    }

    return new DefaultBackgroundTaskQueue(queueCapacity);
});

IHost host = builder.Build();

MonitorLoop monitorLoop = host.Services.GetRequiredService<MonitorLoop>()!;
monitorLoop.StartMonitorLoop();

host.Run();

Hizmetler (Program.cs) içinde kayıtlıdır. Barındırılan hizmet uzantı yöntemiyle AddHostedService kaydedilir. MonitorLoopprogram.cs üst düzey deyiminde başlatılır:

MonitorLoop monitorLoop = host.Services.GetRequiredService<MonitorLoop>()!;
monitorLoop.StartMonitorLoop();

Hizmetleri kaydetme hakkında daha fazla bilgi için bkz . .NET'te bağımlılık ekleme.

Hizmet işlevselliğini doğrulama

Uygulamayı Visual Studio'dan çalıştırmak için F5'i seçin veya Hata AyıklamaYı>Başlat Hata Ayıklama menü seçeneğini belirleyin. .NET CLI kullanıyorsanız, çalışma dizininden komutunu çalıştırın dotnet run :

dotnet run

.NET CLI çalıştırma komutu hakkında daha fazla bilgi için bkz . dotnet run.

Örnek çıktıda gösterildiği gibi öykünmüş w bir iş öğesini kuyruğa almak için en az bir kez (veya W) girin:

info: App.QueueService.MonitorLoop[0]
      MonitorAsync loop is starting.
info: App.QueueService.QueuedHostedService[0]
      QueuedHostedService is running.

      Tap W to add a work item to the background queue.

info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: .\queue-service
winfo: App.QueueService.MonitorLoop[0]
      Queued work item 8453f845-ea4a-4bcb-b26e-c76c0d89303e is starting.
info: App.QueueService.MonitorLoop[0]
      Queued work item 8453f845-ea4a-4bcb-b26e-c76c0d89303e is running. 1/3
info: App.QueueService.MonitorLoop[0]
      Queued work item 8453f845-ea4a-4bcb-b26e-c76c0d89303e is running. 2/3
info: App.QueueService.MonitorLoop[0]
      Queued work item 8453f845-ea4a-4bcb-b26e-c76c0d89303e is running. 3/3
info: App.QueueService.MonitorLoop[0]
      Queued Background Task 8453f845-ea4a-4bcb-b26e-c76c0d89303e is complete.
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
info: App.QueueService.QueuedHostedService[0]
      QueuedHostedService is stopping.

Uygulamayı Visual Studio'dan çalıştırıyorsanız Hata Ayıklama Hata Ayıklamayı>Durdur... öğesini seçin. Alternatif olarak, iptal sinyali almak için konsol penceresinden Ctrl + C'yi seçin.

Ayrıca bkz.