由於signalr作為一個單獨的推送系統,跟業務系統是分離開的,所以此處模擬一個業務系統,新建一個.net core app項目
模擬實現一個登錄功能
我們的登錄很簡單,當進入系統,如果檢測到用戶未登錄則跳轉到登錄頁面,用戶只需要輸入用戶名點擊登錄即算登錄成功
- 配置ConfigServices方法 查看代碼
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, cookieOption => { cookieOption.LoginPath = "/Account/Login"; cookieOption.AccessDeniedPath = "/Account/Login"; });
- 配置Config方法,配置認證、授權的請求管道 查看代碼
app.UseRouting(); app.UseAuthentication(); app.UseAuthorization();
- 接收登錄的post請求,寫cookie,跳轉 查看代碼
前端頁面實現
首先在Layout頁面引入需要的js文件(vue、signalr、msgpack5、signalr-protocol-msgpack) 查看代碼
封裝signalr連接相關js
signalr客戶端js的操作就是,創建連接、監聽推送,封裝后端js如下 查看代碼
/**
* 初始化連接
* @param {object} option 參數
* @param {string} option.url 連接的url地址
* @param {string} option.loggingLevel 日志級別,默認為 Error
* @param {number} option.delay 延遲連接 默認為3000毫秒
* @param {function} option.onStarted 啟動時觸發
* @param {function} option.onLine 啟動時觸發
* @param {function} option.offLine 啟動時觸發
* @returns {object} 連接的實例
*/
function initSignalr(option) {
var config = Object.assign(true, {
loggingLevel: signalR.LogLevel.Error,
delay: 3000,
url: ''
}, option);
var connection = new signalR.HubConnectionBuilder()
.configureLogging(config.loggingLevel)
.withUrl(config.url, {
accessTokenFactory: option.accessTokenFactory
})
.withHubProtocol(new signalR.protocols.msgpack.MessagePackHubProtocol())
.withAutomaticReconnect([0, 2000, 5000, 10000, 20000])
.build();
connection.onreconnecting(function (info) {
console.info('----------------------------------signalr-- onreconnecting', info);
});
connection.onclose(function (err) {
console.info('--------------------------------signalr-- onclose', err);
});
connection.on('OnNotify', config.onNotify);
connection.on('OnLine', config.onLine);
connection.on('OffLine', config.offLine);
setTimeout(function () {
connection.start().then(function (data) {
option.onStarted && option.onStarted(data);
}).catch(function (error) {
console.error(error.toString());
});
}, option.delay);
return connection;
}
調用封裝的js初始化連接 查看代碼
- 然后在Home/Index.cshtml中引入上面的js
- 在頁面加載完后,調用初始化(案例中使用了vue)
在進入頁面后會彈窗讓用戶輸入加入的組,可以不輸入也可以多個function initConnect() { $("#collectionUserInfo").modal({ keyboard: false, show: true, backdrop: 'static' }) $('#collectionUserInfo').on('hidden.bs.modal', function () { var groups = $("#groups").val()||''; connect=initSignalr({ delay: 0, url:`${notifyUrl}notify-hub?userId=${vm.userInfo.userName}&group=${groups}`, loggingLevel: signalR.LogLevel.Error, onNotify: dealNotify, onLine: function (data) { if (data.IsFirst) { getOnlineUsers(); } getOnlineGroups(); vm.logs.push(`新連接上線:${JSON.stringify(data)}`); }, offLine: function (data) { if (data.IsLast) { getOnlineUsers(); } getOnlineGroups(); vm.logs.push(`連接下線:${JSON.stringify(data)}`); }, onStarted: function () { getOnlineUsers(); getOnlineGroups(); vm.$set(vm.userInfo, 'connectionId', connect.connectionId); vm.$set(vm.userInfo, 'groups', groups); vm.logs.push('連接成功'); } }); }) }
onNotify方法,如果仔細的話會看到里面的onNotify方法,所有的推送最終都會調用到該方法來進行分發。查看代碼
offLine,當有客戶端下線的時候會觸發,data里面包含有用戶Id、連接Id、是否該用戶的最后一個連接,可根據需要使用查看代碼
onLine,當用戶連接的時候會觸發,data里面包含有用戶Id、連接Id、是否該用戶的第一個連接(用於用戶上線后的邏輯處理),可根據需要使用 查看代碼
onStarted,當成功連接后觸發,可用於做一些連接后的業務邏輯處理,可根據需要使用 查看代碼
獲取當前用戶信息、在線列表
在用戶連接成功后,獲取當前在線用戶、用戶組、當前用戶信息,並設置到vue的data中 查看代碼
模擬一個任務分配
在項目中心中,點擊"模擬推送待辦"按鈕,將會向當前用戶所在組中推送一條代碼消息,可以登錄不同賬號、開多個tab頁體驗
點擊事件代碼位置 查看代碼
assignTaskToUser: function () {
var that = this;
$.ajax({
type: 'POST',
url: '/api/ServerProxy/AssignTaskToUser',
data: {
groups:that.userInfo.groups
}
})
},
對應的推送解析代碼
首先當有推送過來的時候,會首先進到onNotify方法,然后根據不同類型在分配到不同的js方法中
查看代碼
效果圖
模擬發送消息
登錄互斥
登錄互斥是指,當一個賬號在A電腦登錄,然后再在B電腦登錄,最后的登錄會排斥掉開始的登錄,即,將A上的擠下線
首先用谷歌瀏覽器登錄,輸入用戶名:xiexingen,然后連接
接着使用360急速瀏覽器登錄,輸入用戶名:xiexingen 這個時候會發現谷歌瀏覽器中的登錄已經退出,如圖
必要條件: 不同瀏覽器、同一用戶,比如:同一個瀏覽器,不同tab就不算(能共用cookie)
文件下載(指定連接推送)
文件下載的場景,用戶在操作頁面上選擇了上千個文件,然后點擊打包下載,這個時候可能需要很久時間才反應回來,那么這段時間如果讓用戶一直等待顯然不妥,所以,當用戶點擊打包下載的時候,后端啟用一個后台線程去打包、壓縮,然后立即返回;用戶可以繼續操作,當服務器端打包好后推送給用戶端,用戶點擊下載即可。
此處分兩種情況
- 單連接推送
用戶開了多個tab頁,在其中一個上下載文件,如果后端推送的時候,直接給該用戶推,顯然不妥;正確的做法一個是只給操作的那個tab頁推,這就需要,調用服務器端業務api的時候,需要把當前tab頁對應的連接id發送到服務器端,服務器端處理完業務后,調用推送服務器,告訴推送服務器只推我給你的這個連接的客戶端,這樣就能指定連接推送。 - 單用戶排除某個連接的其他連接推送
這種情況比較少見,告訴推送服務器,給這個用戶,除了某個連接外的其他所有連接推送
模擬操作
點擊第一個圖中的"打包下載文件" 按鈕,當前頁面會受到文件下載的推送
在點擊圖二中的"推送當前用戶其他頁面更新操作"按鈕,會發現出了當前tab頁外,其他tab也都收到了推送消息,如下圖
至此,signalr相關文章算是到此結束了,下一篇談談個人的一點心得以及里面存着的一些問題。
快速導航
標題 | 內容 |
---|---|
索引 | .net core 3.0 Signalr - 實現一個業務推送系統 |
上一篇 | .net core 3.0 Signalr - 07 業務實現-服務端 自定義管理組、用戶、連接 |
下一篇 | .net core 3.0 Signalr - 09 待改進&交流 |
源碼地址 | 源碼 |
官方文檔 | 官方文檔 |