Nguon: Microsoft Learn · .NET 8.0

Host ASP.NET Core SignalR trong background services (dịch vụ nền)

Nguồn: Host ASP.NET Core SignalR in background services

Bởi Dave PringleBrady Gaster

Bài viết này cung cấp hướng dẫn cho:

Bật SignalR khi khởi động ứng dụng

Hosting ASP.NET Core SignalR Hubs trong ngữ cảnh của tiến trình background worker giống hệt như hosting một Hub trong ứng dụng web ASP.NET Core. Trong Program.cs, việc gọi builder.Services.AddSignalR thêm các service cần thiết vào lớp Dependency Injection (DI - tiêm phụ thuộc) của ASP.NET Core để hỗ trợ SignalR. Phương thức MapHub được gọi trên WebApplication app để kết nối các endpoint của Hub vào pipeline (đường ống xử lý) request của ASP.NET Core.

csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSignalR();
builder.Services.AddHostedService<Worker>();

var app = builder.Build();

app.MapHub<ClockHub>("/hubs/clock");

app.Run();

Trong ví dụ trên, lớp ClockHub triển khai lớp Hub<T> để tạo một strongly typed Hub (Hub được định kiểu mạnh). ClockHub đã được cấu hình trong Program.cs để phản hồi các request tại endpoint /hubs/clock.

Để biết thêm thông tin về strongly typed Hubs, xem Use hubs in SignalR for ASP.NET Core.

Lưu ý: Chức năng này không chỉ giới hạn ở lớp Hub<T>. Bất kỳ lớp nào kế thừa từ Hub, chẳng hạn như DynamicHub, đều hoạt động được.

csharp
public class ClockHub : Hub<IClock>
{
    public async Task SendMyLocalTimeToOtherClients(DateTime dateTime)
    {
        await Clients.All.ShowTime(dateTime);
    }
}

Interface (giao diện) được sử dụng bởi ClockHub strongly typed là interface IClock.

csharp
public interface IClock
{
    Task ShowTime(DateTime currentTime);
}

Gọi một SignalR Hub từ background service

Trong quá trình khởi động, lớp Worker — một BackgroundService — được kích hoạt bằng AddHostedService.

csharp
builder.Services.AddHostedService<Worker>();

Vì SignalR cũng được bật trong giai đoạn khởi động, trong đó mỗi Hub được gắn vào một endpoint riêng lẻ trong pipeline HTTP request của ASP.NET Core, mỗi Hub được biểu diễn bởi một IHubContext<T> trên server. Sử dụng các tính năng DI của ASP.NET Core, các lớp khác được khởi tạo bởi hosting layer (lớp lưu trữ), như các lớp BackgroundService, lớp MVC Controller, hay các page model Razor, có thể lấy tham chiếu đến các Hub phía server bằng cách nhận các instance của IHubContext<ClockHub, IClock> trong quá trình khởi tạo.

csharp
public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly IHubContext<ClockHub, IClock> _clockHub;

    public Worker(ILogger<Worker> logger, IHubContext<ClockHub, IClock> clockHub)
    {
        _logger = logger;
        _clockHub = clockHub;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {Time}", DateTime.Now);
            await _clockHub.Clients.All.ShowTime(DateTime.Now);
            await Task.Delay(1000, stoppingToken);
        }
    }
}

Khi phương thức ExecuteAsync được gọi lặp đi lặp lại trong background service, ngày và giờ hiện tại của server được gửi đến các client đã kết nối bằng ClockHub.

Phản hồi các sự kiện SignalR với background services

Giống như một Single Page App (ứng dụng một trang) sử dụng JavaScript client cho SignalR, hoặc một ứng dụng .NET desktop sử dụng ASP.NET Core SignalR .NET client, một triển khai BackgroundService hoặc IHostedService cũng có thể được dùng để kết nối đến SignalR Hubs và phản hồi các sự kiện.

Lớp ClockHubClient triển khai cả interface IClock và interface IHostedService. Nhờ đó, nó có thể được kích hoạt trong quá trình khởi động để chạy liên tục và phản hồi các sự kiện Hub từ server.

csharp
public partial class ClockHubClient : IClock, IHostedService
{
}

Trong quá trình khởi tạo, ClockHubClient tạo một instance của HubConnection và kích hoạt phương thức IClock.ShowTime làm handler (trình xử lý) cho sự kiện ShowTime của Hub.

csharp
private readonly ILogger<ClockHubClient> _logger;
private HubConnection _connection;

public ClockHubClient(ILogger<ClockHubClient> logger)
{
    _logger = logger;
    
    _connection = new HubConnectionBuilder()
        .WithUrl(Strings.HubUrl)
        .Build();

    _connection.On<DateTime>(Strings.Events.TimeSent, ShowTime);
}

public Task ShowTime(DateTime currentTime)
{
    _logger.LogInformation("{CurrentTime}", currentTime.ToShortTimeString());

    return Task.CompletedTask;
}

Trong triển khai IHostedService.StartAsync, HubConnection được khởi động bất đồng bộ.

csharp
public async Task StartAsync(CancellationToken cancellationToken)
{
    // Vòng lặp này chờ cho đến khi server chạy
    while (true)
    {
        try
        {
            await _connection.StartAsync(cancellationToken);

            break;
        }
        catch
        {
            await Task.Delay(1000, cancellationToken);
        }
    }
}

Trong phương thức IHostedService.StopAsync, HubConnection được giải phóng bất đồng bộ.

csharp
public async Task StopAsync(CancellationToken cancellationToken)
{
    await _connection.DisposeAsync();
}