Üzenetsor-szolgáltatás létrehozása
Az üzenetsor-szolgáltatás kiváló példa egy hosszú ideig futó szolgáltatásra, ahol a munkaelemek sorba állíthatók és egymás után dolgozhatnak a korábbi munkaelemek befejezésekor. A Worker Service sablonra támaszkodva új funkciókat építhet ki a BackgroundService.
Ebben az oktatóanyagban az alábbiakkal fog megismerkedni:
- Hozzon létre egy üzenetsor-szolgáltatást.
- Munka delegálása feladatsorba.
- Konzolkulcs-figyelő regisztrálása eseményekből IHostApplicationLifetime .
Tipp.
A "Feldolgozók a .NET-ben" példaforráskódok mindegyike letölthető a Mintaböngészőben . További információ: Kódminták tallózása: Feldolgozók a .NET-ben.
Előfeltételek
- A .NET 8.0 SDK vagy újabb verziója
- .NET integrált fejlesztési környezet (IDE)
- Nyugodtan használhatja a Visual Studiót
Új projekt létrehozása
Ha új Worker Service-projektet szeretne létrehozni a Visual Studióval, válassza a Fájl>új>projekt... lehetőséget. Az Új projekt létrehozása párbeszédpanelen keressen rá a "Worker Service" kifejezésre, és válassza a Worker Service sablont. Ha inkább a .NET CLI-t szeretné használni, nyissa meg kedvenc terminálját egy munkakönyvtárban. Futtassa a dotnet new
parancsot, és cserélje le a <Project.Name>
kívánt projektnevet.
dotnet new worker --name <Project.Name>
A .NET CLI új feldolgozói szolgáltatás projektparancsával kapcsolatos további információkért lásd: dotnet new worker.
Tipp.
Ha Visual Studio Code-ot használ, .NET CLI-parancsokat futtathat az integrált terminálról. További információ: Visual Studio Code: Integrated Terminal.
Üzenetsor-szolgáltatások létrehozása
Lehet, hogy ismeri a QueueBackgroundWorkItem(Func<CancellationToken,Task>) névtér funkcióit System.Web.Hosting
.
Tipp.
A névtér funkcióját System.Web
szándékosan nem portozták át a .NET-be, és továbbra is kizárólagosan .NET-keretrendszer. További információ: Első lépések a növekményes ASP.NET az alapvető áttelepítés ASP.NET.
A .NET-ben a funkciók által QueueBackgroundWorkItem
inspirált szolgáltatás modellezéséhez először adjon hozzá egy IBackgroundTaskQueue
felületet a projekthez:
namespace App.QueueService;
public interface IBackgroundTaskQueue
{
ValueTask QueueBackgroundWorkItemAsync(
Func<CancellationToken, ValueTask> workItem);
ValueTask<Func<CancellationToken, ValueTask>> DequeueAsync(
CancellationToken cancellationToken);
}
Két módszer létezik, az egyik a sorban állási funkciókat teszi elérhetővé, a másik pedig a korábban várólistára helyezett munkaelemeket. A munkaelem egy Func<CancellationToken, ValueTask>
. Ezután adja hozzá az alapértelmezett implementációt a projekthez.
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;
}
}
Az előző implementáció egy Channel<T> üzenetsorra támaszkodik. A BoundedChannelOptions(Int32) meghívás explicit kapacitással történik. A kapacitást az alkalmazás várt terhelése és az üzenetsorhoz hozzáférő egyidejű szálak száma alapján kell beállítani. BoundedChannelFullMode.Wait a hívások ChannelWriter<T>.WriteAsync egy feladatot adnak vissza, amely csak akkor fejeződik be, ha a terület elérhetővé válik. Ami visszanyomáshoz vezet, ha túl sok közzétevő/hívás kezd halmozódik fel.
A Feldolgozó osztály újraírása
Az alábbi QueueHostedService
példában:
- A
ProcessTaskQueueAsync
metódus egy inExecuteAsync
értéket ad Task vissza. - Az üzenetsor háttérfeladatai le lesznek kérdezve és végrehajtva a következőben
ProcessTaskQueueAsync
: . - A munkaelemeket a szolgáltatás leállása előtt várjuk.
StopAsync
Cserélje le a meglévő Worker
osztályt a következő C#-kódra, és nevezze át a fájlt QueueHostedService.cs-re.
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);
}
}
A MonitorLoop
szolgáltatás kezeli az üzemeltetett szolgáltatás lekérdezési feladatait, amikor a w
kulcs ki van választva egy bemeneti eszközön:
- A
IBackgroundTaskQueue
beszúrt a szolgáltatásbaMonitorLoop
. IBackgroundTaskQueue.QueueBackgroundWorkItemAsync
a rendszer meghívja egy munkaelem leküldéséhez.- A munkaelem egy hosszú ideig futó háttérfeladatot szimulál:
- A rendszer három 5 másodperces késést hajt végre Delay.
- A
try-catch
feladat megszakítása OperationCanceledException esetén az utasítás csapódik le.
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);
}
}
}
Cserélje le a meglévő Program
tartalmat a következő C#-kódra:
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();
A szolgáltatások regisztrálva vannak (Program.cs). A üzemeltetett szolgáltatás regisztrálva van a AddHostedService
bővítménymetódussal. MonitorLoop
a Program.cs felső szintű utasításában indul el:
MonitorLoop monitorLoop = host.Services.GetRequiredService<MonitorLoop>()!;
monitorLoop.StartMonitorLoop();
A szolgáltatások regisztrálásával kapcsolatos további információkért lásd : Függőséginjektálás a .NET-ben.
A szolgáltatás működésének ellenőrzése
Ha az alkalmazást a Visual Studióból szeretné futtatni, válassza az F5 lehetőséget, vagy válassza a Hibakeresés>indítása hibakeresés menüt. Ha a .NET CLI-t használja, futtassa a dotnet run
parancsot a munkakönyvtárból:
dotnet run
A .NET CLI futtatási parancsával kapcsolatos további információkért lásd : dotnet run.
Amikor a rendszer kéri, legalább egyszer adja meg az w
(vagy W
) értéket egy emulált munkaelem várólistára helyezéséhez, ahogyan az a példakimenetben látható:
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.
Ha az alkalmazást a Visual Studióban futtatja, válassza a Hibakeresés>leállítása... lehetőséget. Másik lehetőségként válassza a Ctrl C billentyűkombinációt + a konzolablakban a jel törléséhez.
Kapcsolódó információk
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: