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
- .NET 8.0 SDK veya üzeri
- .NET tümleşik geliştirme ortamı (IDE)
- Visual Studio'yu kullanmaktan çekinmeyin
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çindeExecuteAsync
bir 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:
- Üç 5 saniyelik gecikme yürütülür Delay.
try-catch
Görev iptal edilirse bir deyim yakalanıyorOperationCanceledException.
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. MonitorLoop
program.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.
Geri Bildirim
https://aka.ms/ContentUserFeedback.
Çok yakında: 2024 boyunca, içerik için geri bildirim mekanizması olarak GitHub Sorunları’nı kullanımdan kaldıracak ve yeni bir geri bildirim sistemiyle değiştireceğiz. Daha fazla bilgi için bkz.Gönderin ve geri bildirimi görüntüleyin