.NET SignalR Redis实时Web应用

2024-04-13 07:52
文章标签 应用 redis web 实时 net signalr

本文主要是介绍.NET SignalR Redis实时Web应用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

环境 Win10 VS2022 .NET8 Docker  Redis

前言

什么是 SignalR?

ASP.NET Core SignalR 是一个开放源代码库,可用于简化向应用添加实时 Web 功能。 实时 Web 功能使服务器端代码能够将内容推送到客户端。

适合 SignalR 的候选项:

  • 需要从服务器进行高频率更新的应用。 (游戏、社交网络、投票、拍卖、地图和 GPS 应用)
  • 仪表板和监视应用。 (公司仪表板、即时销售更新或出行警报)
  • 协作应用。 (包括白板应用和团队会议软件)
  • 需要通知的应用。( 社交网络、电子邮件、聊天、游戏等)

SignalR 提供用于创建服务器到客户端的远程过程调用 (RPC) API。 RPC 从服务器端 .NET Core 代码调用客户端上的函数。支持JavaScript ,.NET ,JAVA,Swift (官方没有明确支持,这是第三方库)其中每个平台都有各自的客户端 SDK。 因此,RPC 调用所调用的编程语言有所不同。

ASP.NET Core SignalR 的一些功能:

  • 自动处理连接管理。
  • 同时向所有连接的客户端发送消息。 例如聊天室。
  • 向特定客户端或客户端组发送消息。
  • 对其进行缩放,以处理不断增加的流量。
  • SignalR 中心协议

1.👋nuget引入SignalR

2.👀创建SignalR Hub

using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;namespace WebSignalR
{public class ChatHub : Hub{public async Task SendMessage(string user, string message){await Clients.All.SendAsync("ReceiveMessage", user, message);}}}

3.🌱 Program.cs添加SignalR服务

 (Startup.cs)

//添加SignalR服务 
builder.Services.AddSignalR();
builder.Services.AddControllersWithViews();
app.UseEndpoints(endpoints =>
{endpoints.MapHub<ChatHub>("/chathub");endpoints.MapControllerRoute(name: "default",pattern: "{controller=Home}/{action=Index}/{id?}");
});

4.📫 添加前端代码


<div class="text-center"><div id="chat-container"><input type="text" id="userInput" placeholder="Your name" /><input type="text" id="messageInput" placeholder="Type a message..." /><button id="sendButton">Send</button><ul id="messagesList"></ul></div><script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/5.0.12/signalr.min.js"></script><script>const connection = new signalR.HubConnectionBuilder().withUrl("/chathub").build();connection.on("ReceiveMessage", function (user, message) {const encodedUser = user.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");const encodedMessage = message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");const li = document.createElement("li");li.textContent = `${encodedUser}: ${encodedMessage}`;document.getElementById("messagesList").appendChild(li);});connection.start().catch(function (err) {return console.error(err.toString());});document.getElementById("sendButton").addEventListener("click", function (event) {const user = document.getElementById("userInput").value;const message = document.getElementById("messageInput").value;connection.invoke("SendMessage", user, message).catch(function (err) {return console.error(err.toString());});event.preventDefault();});</script></div>

5.⚡F5运行

升级优化

封装Msg

    public class Msg{public string? user { get; set; }public string? message { get; set; }}

sendMessage

      public async Task SendMessage(Msg entity){if (Clients != null)await Clients.All.SendAsync("ReceiveMessage", entity.user, entity.message);// $"{entity.user} 发送消息:{entity.message}");}

前端   connection.invoke("SendMessage" ...   传递msg对象进来即可

6.💪跨域问题

builder.Services.AddCors(options =>
{options.AddPolicy("CorsPolicy",builder => builder.AllowAnyMethod().AllowAnyHeader().WithOrigins("http://localhost:5173") // 替换为你允许的来源.AllowCredentials());
});
//通过添加app.UseCors("CorsPolicy")中间件来启用跨域支持
app.UseCors("CorsPolicy"); 

上面代码中的WithOrigins方法指定了允许访问SignalR端点的来源。将​"http://localhost:5173"替换为你允许的实际来源。如果要允许任何来源访问,可以使用通配符"*"。​

这样就可以跨域访问 👇Vue跨域


 

7.🧙‍♂️聊天池的实现

实际生产可能需要1对1或者多对多,可在后端建立一个字典,将聊天池的标识映射到该聊天池的连接ID列表。

        public Dictionary<string, List<string>> _chatRooms = new Dictionary<string, List<string>>();public async Task JoinChatRoom(string chatRoomId){// 将用户连接添加到特定的聊天池if (!MsgSt._chatRooms2.ContainsKey(chatRoomId)){MsgSt._chatRooms2[chatRoomId] = new List<string>();}MsgSt._chatRooms2[chatRoomId].Add(Context.ConnectionId);// int i = _chatRooms.Count;Console.WriteLine("chatRoomId-Cid" + chatRoomId + " " + Context.ConnectionId);}public async Task SendMessageToChatRoom(string chatRoomId, string user, string message){//         Console.WriteLine(connectionIds);// 向特定的聊天池发送消息if (MsgSt._chatRooms2.TryGetValue(chatRoomId, out var connectionIds)){foreach (var connectionId in connectionIds){await Clients.Client(connectionId).SendAsync("ReceiveMessage",user, message);}}//  await Clients.Client(connectionId).SendAsync("ReceiveMessage", message);}
   public class MsgSt{public static Dictionary<string, List<string>> _chatRooms2= new Dictionary<string, List<string>>();//public static int temp2 = 0;}

在前端发送消息时,除了发送消息内容外,还要发送消息到的聊天池的标识。

JoinChatRoom   SendMessageToChatRoom

    <script>const connection = new signalR.HubConnectionBuilder().withUrl("/chathub").build();const userId = "userid001";const chatRoomId = "room001"; // 聊天池标识connection.on("ReceiveMessage", function (user, message) {const encodedUser = user.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");const encodedMessage = message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");const li = document.createElement("li");li.textContent = `${encodedUser}: ${encodedMessage}`;document.getElementById("messagesList").appendChild(li);});connection.start().then(() => {console.log("Connection started" +chatRoomId);connection.invoke("JoinChatRoom", chatRoomId); // 加入特定的聊天池}).catch(err => console.error(err));document.getElementById("sendButton").addEventListener("click", function (event) {const message = document.getElementById("messageInput").value;const user = document.getElementById("userInput").value;connection.invoke("SendMessageToChatRoom", chatRoomId, user, message).catch(function (err) {return console.error(err.toString());});event.preventDefault();});</script>

chatroom1 


 

8.☔断线重连

确保客户端在与 SignalR Hub 的连接断开后能够重新连接并恢复之前的状态

可以在客户端代码中实现重连逻辑

  let isConnected = false; // 用于标识是否已连接// 连接成功时将 isConnected 设置为 trueconnection.onclose(() => {isConnected = false;});async function startConnection() {try {await connection.start();console.log("Connection started");isConnected = true;} catch (err) {console.error(err);isConnected = false;// 连接失败时尝试重新连接setTimeout(startConnection, 5000); // 5秒后重试}}startConnection(); // 初始连接

9.🌠配置Redis分布式缓存

Docker Redis 👈 Redis部署

用 Microsoft.Extensions.Caching.StackExchangeRedis 包连接到 Redis 并使用分布式缓存。这样可以确保即使服务重启,也能够保留聊天室的状态。

安装 Microsoft.Extensions.Caching.StackExchangeRedis   

or StackExchange.Redis 

Program.cs

// 添加Redis缓存
builder.Services.AddStackExchangeRedisCache(options =>
{options.Configuration = "127.0.0.1:6379"; // Redis服务器地址options.InstanceName = "ChatRooms"; // 实例名称
});

options.Configuration 设置为 Redis 服务器的地址,如果 Redis 运行在本地,则可以设置为 "localhost"。options.InstanceName 是 Redis 实例名称。

启动Redis服务  

在 ChatHub 中注入 IDistributedCache,连接到 Redis

_cache相当于 _chatRooms2存放连接ID的列表

private readonly IDistributedCache _cache;public ChatHub(IDistributedCache cache){_cache = cache;}public async Task JoinChatRoom(string chatRoomId){// 使用Redis的SET操作来添加连接ID到聊天室  var connectionId = Context.ConnectionId;var key = $"chatrooms:{chatRoomId}";var connectionIds = await _cache.GetStringAsync(key);var connectionsList = string.IsNullOrEmpty(connectionIds)? new List<string>(): connectionIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();connectionsList.Add(connectionId);await _cache.SetStringAsync(key, string.Join(",", connectionsList));Console.WriteLine($"chatRoomId-Cid {chatRoomId} {connectionId}");}public async Task SendMessageToChatRoom(string chatRoomId, string user, string message){var key = $"chatrooms:{chatRoomId}";var connectionIds = await _cache.GetStringAsync(key);if (!string.IsNullOrEmpty(connectionIds)){var connectionsList = connectionIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);foreach (var connectionId in connectionsList){await Clients.Client(connectionId).SendAsync("ReceiveMessage", user, message);}}}

这样前端传过来的 room001 room002 便会存入到Redis里面

运行调试的时候可以看到有用户JionChatRoom的chatRoomId connectionId

也可通过Redis命令 KEY * 查看

PS:这里用简单的字符串来存储连接ID的列表,连接ID之间用逗号分隔,实际生产可使用Redis的集合(Set)数据类型来存储连接ID,还需处理Redis连接失败、缓存过期等异常情况。

📜参考资料:

ASP.NET Core SignalR 入门 | Microsoft Learn

RPC-wiki

这篇关于.NET SignalR Redis实时Web应用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/899561

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

zoj3820(树的直径的应用)

题意:在一颗树上找两个点,使得所有点到选择与其更近的一个点的距离的最大值最小。 思路:如果是选择一个点的话,那么点就是直径的中点。现在考虑两个点的情况,先求树的直径,再把直径最中间的边去掉,再求剩下的两个子树中直径的中点。 代码如下: #include <stdio.h>#include <string.h>#include <algorithm>#include <map>#

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

【区块链 + 人才服务】可信教育区块链治理系统 | FISCO BCOS应用案例

伴随着区块链技术的不断完善,其在教育信息化中的应用也在持续发展。利用区块链数据共识、不可篡改的特性, 将与教育相关的数据要素在区块链上进行存证确权,在确保数据可信的前提下,促进教育的公平、透明、开放,为教育教学质量提升赋能,实现教育数据的安全共享、高等教育体系的智慧治理。 可信教育区块链治理系统的顶层治理架构由教育部、高校、企业、学生等多方角色共同参与建设、维护,支撑教育资源共享、教学质量评估、

Java Web指的是什么

Java Web指的是使用Java技术进行Web开发的一种方式。Java在Web开发领域有着广泛的应用,主要通过Java EE(Enterprise Edition)平台来实现。  主要特点和技术包括: 1. Servlets和JSP:     Servlets 是Java编写的服务器端程序,用于处理客户端请求和生成动态网页内容。     JSP(JavaServer Pages)