SignalR在后台服务中托管 ASP.NET Core

作者: Brady Gaster

本文提供以下内容的指导:

  • SignalR使用宿主 ASP.NET Core 的后台工作进程托管中心。
  • 从 .NET Core BackgroundService中将消息发送到已连接的客户端。

SignalR在启动时启用

SignalR在后台工作进程的上下文中托管 ASP.NET Core 中心与在 ASP.NET Core 的 web 应用中托管集线器完全相同。 在 Startup.ConfigureServices 方法中,调用会 services.AddSignalR 将所需的服务添加到 ASP.NET Core 依赖关系注入 (DI) 层以支持 SignalR 。 在中 Startup.ConfigureMapHub 调用方法 UseEndpoints 以连接 ASP.NET Core 请求管道中的中心终结点。

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSignalR();
        services.AddHostedService<Worker>();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapHub<ClockHub>("/hubs/clock");
        });
    }
}

SignalR在后台工作进程的上下文中托管 ASP.NET Core 中心与在 ASP.NET Core 的 web 应用中托管集线器完全相同。 在 Startup.ConfigureServices 方法中,调用会 services.AddSignalR 将所需的服务添加到 ASP.NET Core 依赖关系注入 (DI) 层以支持 SignalR 。 在中 Startup.ConfigureUseSignalR 调用方法以将中心终结点连接到 ASP.NET Core 请求管道中的) (。

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSignalR();
        services.AddHostedService<Worker>();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseSignalR((routes) =>
        {
            routes.MapHub<ClockHub>("/hubs/clock");
        });
    }
}

在前面的示例中, ClockHub 类实现 Hub<T> 类以创建强类型中心。 已 ClockHub 在类中配置, Startup 以响应终结点上的请求 /hubs/clock

有关强类型化集线器的详细信息,请参阅使用中 SignalR 的中心进行 ASP.NET Core

备注

此功能并不限于集线器 <T> 类。 从 中心继承的任何类(如 DynamicHub)都适用。

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

强类型化所使用的接口 ClockHubIClock 接口。

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

SignalR从后台服务调用中心

在启动过程中, Worker BackgroundService 使用启用类 AddHostedService

services.AddHostedService<Worker>();

由于 SignalR 还会在 Startup 阶段启用,其中每个中心均附加到 ASP.NET Core 的 HTTP 请求管道中的单个终结点,因此,每个中心都由 IHubContext<T> 服务器上的表示。 使用 ASP.NET Core 的 DI 功能,由宿主层实例化的其他类(如 BackgroundService 类、MVC 控制器类或 Razor 页模型)可以 IHubContext<ClockHub, IClock> 在构造过程中接受实例,从而获取对服务器端集线器的引用。

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);
        }
    }
}
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);
        }
    }
}

由于 ExecuteAsync 在后台服务中以迭代方式调用方法,服务器的当前日期和时间将使用发送到已连接的客户端 ClockHub

React SignalR 包含后台服务的事件

与使用适用于的 JavaScript 客户端的单页面应用 SignalR 或使用的 .net 桌面应用一样 ASP.NET Core SignalR.NET 客户端BackgroundServiceIHostedService 实现也可用于连接到 SignalR 集线器和响应事件。

ClockHubClient类实现 IClock 接口和 IHostedService 接口。 这样一来,就可以在过程中将其启用 Startup ,并对来自服务器的中心事件做出响应。

public partial class ClockHubClient : IClock, IHostedService
{
}

在初始化期间,会 ClockHubClient 创建一个实例 HubConnection ,并将该 IClock.ShowTime 方法作为中心事件的处理程序启用 ShowTime

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;
}

IHostedService.StartAsync 实现中,以 HubConnection 异步方式启动。

public async Task StartAsync(CancellationToken cancellationToken)
{
    // Loop is here to wait until the server is running
    while (true)
    {
        try
        {
            await _connection.StartAsync(cancellationToken);

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

IHostedService.StopAsync 方法中,将 HubConnection 异步释放。

public Task StopAsync(CancellationToken cancellationToken)
{
    return _connection.DisposeAsync();
}
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, 
        dateTime => _ = ShowTime(dateTime));
}

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

    return Task.CompletedTask;
}

IHostedService.StartAsync 实现中,以 HubConnection 异步方式启动。

public async Task StartAsync(CancellationToken cancellationToken)
{
    // Loop is here to wait until the server is running
    while (true)
    {
        try
        {
            await _connection.StartAsync(cancellationToken);

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

IHostedService.StopAsync 方法中,将 HubConnection 异步释放。

public Task StopAsync(CancellationToken cancellationToken)
{
    return _connection.DisposeAsync();
}

其他资源