Host ASP.NET Core SignalR trong background services (dịch vụ nền)
Bởi Dave Pringle và Brady Gaster
Bài viết này cung cấp hướng dẫn cho:
- Hosting (lưu trữ) SignalR Hubs sử dụng tiến trình background worker (worker nền) được host bởi ASP.NET Core.
- Gửi tin nhắn đến các client đã kết nối từ bên trong .NET BackgroundService.
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.
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.
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.
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.
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.
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.
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.
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ộ.
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ộ.
public async Task StopAsync(CancellationToken cancellationToken)
{
await _connection.DisposeAsync();
}