在後臺主機中託管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

相關文章
相關標籤/搜索