zoukankan      html  css  js  c++  java
  • 在后台主机中托管SignalR服务并广播心跳包

    什么是后台主机

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

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

    本文主要目的在于实现一个后台心跳广播包,所有连接到 SignalR 的客户端,通过订阅心跳包广播频道,能够自动收到服务器发送的心跳广播

    广播协议定义

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

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

    定义一个泛型的 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 的定义(方法名称和参数)

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

    定义后台服务主机

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

    实现代码
        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 的管道中

    // 添加服务主机随主机启动
    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 的实现。

    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

  • 相关阅读:
    SpringMVC + spring3.1.1 + hibernate4.1.0 集成及常见问题总结
    开涛spring3(9.4)
    开涛spring3(9.3)
    开涛spring3(9.2)
    开涛spring3(9.1)
    开涛spring3(8.4)
    分水岭算法——学习笔记
    【代码备份】pocs.m
    【代码备份】NLM插值
    【代码备份】原图降采样后进行NLM滤波
  • 原文地址:https://www.cnblogs.com/viter/p/10771974.html
Copyright © 2011-2022 走看看