欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-01-04 09:50:10
本文主要目的在于实现一个后台心跳广播包,所有连接到 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 通道用来接收来自服务端推送的心跳广播(被动)

运行演示

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

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

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

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

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

红圈处就是聊天消息。

扩展

如果需要开通多个通道的话怎么办呢,聪明的你一定想到了,就是增加 iheartbeat 的定义,然后在客户端订阅该频道即可。

示例代码下载

https://github.com/lianggx/examples/tree/master/signalr/ron.signalrlesson3

上一篇:

下一篇: