在后台主机中托管SignalR服务并广播心跳包

什么是后台主机

在以前的 Asp.NETCore 轻松学系列中,曾经介绍过一个轻量级服务主机 IHostedService ,利用 IHostedService 能够轻松的实现一个系统级别的后台服务,该服务跟随系统启动和中止;同时,其使用异步加载和兼容注入的特性,能够很好的实现业务的扩展和隔离。javascript

IHostedService 有一个默认的实现基类 Microsoft.Extensions.Hosting.BackgroundService,咱们仅须要继承 BackgroundService 便可实现后台主机。html

本文主要目的在于实现一个后台心跳广播包,全部链接到 SignalR 的客户端,经过订阅心跳包广播频道,可以自动收到服务器发送的心跳广播java

广播协议定义

public interface IHeartbeat
    {
        Task HeartbeatAsync(int data);
    }

上面定义 了一个接口 IHeartbeat,该接口有一个异步的方法 HeartbeatAsync,主要就是心跳的定义,先无论它怎么使用,咱们继续往下git

定义一个泛型的 Hub

public class WeChatHub : Hub<IHeartbeat>
    {
        public void Send(ChatMessage body)
        {
            Clients.All.RecvAsync(body);
        }

        public override Task OnConnectedAsync()
        {
            Console.WriteLine("游客[{0}]进入了聊天室", this.Context.ConnectionId);
            return base.OnConnectedAsync();
        }

        public override Task OnDisconnectedAsync(Exception exception)
        {
            Console.WriteLine("游客[{0}]离开了聊天室", this.Context.ConnectionId);
            return base.OnDisconnectedAsync(exception);
        }
    }

上面定义了一个SignalR通讯管理对象 WeChatHub ,其继承字泛型的 Hub ,其中,泛型类型指定为 IHeartbeat 接口,这里的泛型即表示 SignalR 的客户端,其表明一种通讯协议包,SignalR 的客户端(各类类型的)若是但愿收到 IHeartbeat 定义的消息(方法实现),必须侦听 IHeartbeat 的定义(方法名称和参数) github

上面的这段话比较绕口,其实我也以为不太好理解,简单来讲就是客户端要侦听一个和 SignalR 服务端定义的相同的频道,才能够收到广播。服务器

定义后台服务主机

在定义好 SignalR 的通讯协议后,接下来要作的就是实现一个后台服务主机,也就是 IHostedService 的实现。根据 Asp.Net Core 轻松学-基于微服务的后台任务调度管理器 中的提示,咱们不须要实现这个接口,只须要继承 Microsoft.Extensions.Hosting.BackgroundService 便可app

实现代码
public class WeChatHubWorker : BackgroundService
    {
        private readonly IHubContext<WeChatHub, IHeartbeat> heartbeat;
        public WeChatHubWorker(IHubContext<WeChatHub, IHeartbeat> heartbeat)
        {
            this.heartbeat = heartbeat;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                await this.heartbeat.Clients.All.HeartbeatAsync(0);
                await Task.Delay(3000);
                Console.WriteLine("heartbeat");
            }
        }
    }

上面的代码比较简单,首先定义了 IHubContext<WeChatHub, IHeartbeat> 对象 heartbeat,而后在构造函数中经过注入的方式将其实例化,紧接着在 ExecuteAsync(CancellationToken stoppingToken) 方法中,执行了一个无限循环的操做,每 3000 毫秒给全部 SignalR 客户端发送一个内容为 0 的心跳包。异步

服务注入

实现了后台主机后,须要将其注入到 Asp.NETCore 的管道中async

// 添加服务主机随主机启动
public void ConfigureServices(IServiceCollection services)
    {
        services.AddSignalR();
        services.AddHostedService<WeChatHubWorker>();
         ...
    }

// 配置 SignalR 侦听的 Uri 地址
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseSignalR(routes =>
        {
            routes.MapHub<WeChatHub>("/wechatHub");
        });
        ...
    }

经过服务注入,咱们就完成了服务端的实现,下面看看 JavaScript 的实现。ide

JavaScript 客户端的实现

var connection = new signalR.HubConnectionBuilder()
    .withUrl("/wechatHub")
    .build();

connection.on("RecvAsync", function (data) {
    var li = document.createElement("li");
    li = $(li).text(data.userName + ":" + data.content);
    $("#msgList").append(li);
});

connection.on("HeartbeatAsync", (data) => {
    console.log(data);
});

connection.start()
    .then(function () {
        console.log("客户端已链接");
    }).catch(function (err) {
        console.log(err);
    });

上面的代码,若是有看过前两章的同窗,应该是很是熟悉的,这里就很少解释了;可是,因为本次 SignalR 服务端的 Hub 实现不太同样,因此,这里仍是要解释一下。

上面的代码分别侦听了两个通道 connection.on("RecvAsync",...) 和 connection.on("HeartbeatAsync",...) ,这两个通道对应服务端的 IHeartbeat 的定义的成员名称,而后在 Callback 中的参数,也须要和 IHeartbeat 定义的同样,保证能够完整接收服务端推送的消息。

  • RecvAsync 通道用来接收和发送客户端的聊天消息(主动/被动)
  • HeartbeatAsync 通道用来接收来自服务端推送的心跳广播(被动)

运行演示

运行服务端,分别打开两个客户端,同时观察服务端和客户端的心跳输出

红圈处就是心跳广播的内容:0。

使用两个客户端分别发送聊天消息

红圈处就是聊天消息。

扩展

若是须要开通多个通道的话怎么办呢,聪明的你必定想到了,就是增长 IHeartbeat 的定义,而后在客户端订阅该频道便可。

示例代码下载

https://github.com/lianggx/Examples/tree/master/SignalR/Ron.SignalRLesson3

相关文章
相关标签/搜索