升訊威線上客服系統的並行高效能資料處理技術:超強的 SignalR

2023-12-22 12:00:34

我在業餘時間開發維護了一款免費開源的升訊威線上客服系統,也收穫了許多使用者。對我來說,只要能獲得使用者的認可,就是我最大的動力。

最近客服系統成功經受住了客戶現場組織的壓力測試,獲得了客戶的認可。
客戶組織多名客服上線後,所有員工同一時間開啟訪客頁面瘋狂不停的給線上客服發訊息,系統穩定無異常無掉線,客服回覆訊息正常。訊息實時到達無任何延遲。

https://kf.shengxunwei.com/


我會通過一系列的文章詳細分析升訊威線上客服系統的並行高效能技術是如何實現的,使用了哪些方案以及具體的做法。

本篇介紹超強的 SignalR 技術。

什麼是 SignalR?

ASP.NET Core SignalR 是一個開放原始碼庫,可用於簡化嚮應用新增實時 Web 功能。 實時 Web 功能使伺服器端程式碼能夠將內容推播到使用者端。

適合 SignalR 的候選項:

  • 需要從伺服器進行高頻率更新的應用。 範例包括遊戲、社群網路、投票、拍賣、地圖和 GPS 應用。
  • 儀表板和監視應用。 範例包括公司儀表板、即時銷售更新或旅行警報。
  • 共同作業應用。 共同作業應用的範例包括白板應用和團隊會議軟體。
  • 需要通知的應用。 社群網路、電子郵件、聊天、遊戲、旅行警報和很多其他應用都需使用通知。
  • SignalR 提供用於建立伺服器到使用者端遠端過程呼叫 (RPC) 的 API。 RPC 從伺服器端 .NET Core 程式碼呼叫使用者端上的函數。 提供多個受支援的平臺,其中每個平臺都有各自的使用者端 SDK。 因此,RPC 呼叫所呼叫的程式語言有所不同。

以下是 ASP.NET Core SignalR 的一些功能:

  • 自動處理連線管理。
  • 同時向所有連線的使用者端傳送訊息。 例如聊天室。
  • 向特定使用者端或使用者端組傳送訊息。
  • 對其進行縮放,以處理不斷增加的流量。

傳輸

SignalR 支援以下用於處理實時通訊的技術(按正常回退的順序):

  • WebSockets
  • Server-Sent Events
  • 長輪詢

SignalR 自動選擇伺服器和使用者端能力範圍內的最佳傳輸方法。

中心

SignalR 使用中心在使用者端和伺服器之間進行通訊。

Hub 是一種高階管道,允許使用者端和伺服器相互呼叫方法。 SignalR 自動處理跨計算機邊界的排程,並允許使用者端呼叫伺服器上的方法,反之亦然。 可以將強型別引數傳遞給方法,從而支援模型繫結。 SignalR 提供兩種內建中心協定:基於 JSON 的文字協定和基於 MessagePack 的二進位制協定。 與 JSON 相比,MessagePack 通常會建立更小的訊息。 舊版瀏覽器必須支援 XHR 級別 2 才能提供 MessagePack 協定支援。

中心通過傳送包含使用者端方法的名稱和引數的訊息來呼叫使用者端程式碼。 作為方法引數傳送的物件使用設定的協定進行反序列化。 使用者端嘗試將名稱與使用者端程式碼中的方法匹配。 當用戶端找到匹配項時,它會呼叫該方法並將反序列化的引數資料傳遞給它。

在升訊威客服系統中使用 SignalR

建立 SignalR 中心

中心是一個類,用作處理使用者端 - 伺服器通訊的高階管道。

在 SignalRChat 專案資料夾中,建立 Hubs 資料夾。

在 Hubs 資料夾中,使用以下程式碼建立 ChatHub 類:

using Microsoft.AspNetCore.SignalR;

namespace SignalRChat.Hubs
{
    public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }
}

ChatHub 類繼承自 SignalRHub。 Hub 類管理連線、組和訊息。

可通過已連線使用者端呼叫 SendMessage,以向所有使用者端傳送訊息。 本教學後面部分將顯示呼叫該方法的 JavaScript 使用者端程式碼。 SignalR 程式碼是非同步模式,可提供最大的可伸縮性。

設定 SignalR

必須將 SignalR 伺服器設定為將 SignalR 請求傳遞給 SignalR。 將以下突出顯示的程式碼新增到 Program.cs 檔案。

using SignalRChat.Hubs;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddSignalR();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();
app.MapHub<ChatHub>("/chatHub");

app.Run();

以上突出顯示的程式碼將 SignalR 新增到 ASP.NET Core 依賴關係注入和路由系統。

新增 SignalR 使用者端程式碼

  • 建立並啟動連線。
  • 向「提交」按鈕新增一個用於向中心傳送訊息的處理程式。
  • 向連線物件新增一個用於從中心接收訊息並將其新增到列表的處理程式。
"use strict";

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

//Disable the send button until connection is established.
document.getElementById("sendButton").disabled = true;

connection.on("ReceiveMessage", function (user, message) {
    var li = document.createElement("li");
    document.getElementById("messagesList").appendChild(li);
    // We can assign user-supplied strings to an element's textContent because it
    // is not interpreted as markup. If you're assigning in any other way, you 
    // should be aware of possible script injection concerns.
    li.textContent = `${user} says ${message}`;
});

connection.start().then(function () {
    document.getElementById("sendButton").disabled = false;
}).catch(function (err) {
    return console.error(err.toString());
});

document.getElementById("sendButton").addEventListener("click", function (event) {
    var user = document.getElementById("userInput").value;
    var message = document.getElementById("messageInput").value;
    connection.invoke("SendMessage", user, message).catch(function (err) {
        return console.error(err.toString());
    });
    event.preventDefault();
});

當從 Hub 類外部呼叫使用者端方法時,沒有與該呼叫關聯的呼叫方。 因此,無法存取 ConnectionId、Caller 和 Others 屬性。

需要將使用者對映到連線 ID 並保留該對映的應用可以執行以下操作之一:

  • 將單個或多個連線的對映保留為組。 有關詳細資訊,請參閱SignalR 中的組。
  • 通過單一範例服務保留連線和使用者資訊。 有關詳細資訊,請參閱將服務注入中心。 單一範例服務可以使用任何儲存方法,例如:
  • 字典中的記憶體中儲存。
  • 永久性外部儲存。 例如,使用 Azure.Data.Tables NuGet 包的資料庫或 Azure 表儲存。
  • 在使用者端之間傳遞連線 ID。

從 IHost 獲取 IHubContext 範例

從 Web 主機存取 IHubContext 對於與 ASP.NET Core 之外的區域整合很有用,例如,使用第三方依賴項注入框架:

public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();
            var hubContext = host.Services.GetService(typeof(IHubContext<ChatHub>));
            host.Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder => {
                    webBuilder.UseStartup<Startup>();
                });
    }

SignalR 中的單個使用者可以與一個應用建立多個連線。 例如,使用者可以在桌面和手機上進行連線。 每臺裝置都有一個單獨的 SignalR 連線,但它們都與同一個使用者關聯。 如果向用戶傳送訊息,則與該使用者關聯的所有連線都會收到訊息。 可以通過中心內的 Context.UserIdentifier 屬性存取連線的使用者識別符號。

預設情況下,SignalR 使用與連線關聯的 ClaimsPrincipal 中的 ClaimTypes.NameIdentifier 作為使用者識別符號。 若要自定義此行為,請參閱使用宣告自定義標識處理。

重新連線時不會保留組成員身份。 重新建立連線後,需要重新加入組。 無法計算組的成員數,因為如果將應用程式擴充套件到多臺伺服器,則無法獲取此資訊。

若要在使用組時保護對資源的存取,請使用 ASP.NET Core 中的身份驗證和授權功能。 如果僅當憑據對組有效時才將使用者新增到該組,則傳送到該組的訊息將僅傳送給授權使用者。 但是,組不是一項安全功能。 身份驗證宣告具有組不具備的功能,例如到期和復原。 如果復原使用者對組的存取許可權,應用必須從組中顯式刪除該使用者。


簡介

升訊威線上客服與行銷系統是一款客服軟體,但更重要的是一款行銷利器。

https://kf.shengxunwei.com/

  • 可以追蹤正在存取網站或使用 APP 的所有訪客,收集他們的瀏覽情況,使客服能夠主動出擊,施展話術,促進成單。
    訪* 客端在 PC 支援所有新老瀏覽器。包括不支援 WebSocket 的 IE8 也能正常使用。
  • 行動端支援所有手機瀏覽器、APP、各大平臺的公眾號對接。
  • 支援訪客資訊互通,可傳輸訪客標識、名稱和其它任意資訊到客服系統。
  • 具備一線專業技術水平,網路中斷,拔掉網線,手機飛航模式,不丟訊息。同類軟體可以按視訊方式對比測試。