ASP.NET Core SignalR 配置与集成测试究极指南
这篇文章也可以在我的博客中查看
前言
哥们最近都在埋头苦干,沉默是金,有一段时间没更新博客了。然而今儿SignalR集成测试实属是给我整破防了。虽说SignalR是.NET官方维护的实时通信库,已经开发了有十几年,甚至已经编入至了core dll,然而更新迭代异常迅速,导致文档不全,出了事不知所措。这不最近在集成测试SignalR这点上就踩了大坑。
今天就给大伙分享一下如何配置SignalR,并重点讲解如何在 .NET 8 中使用xUnit与Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory对最新版(ASP.NET Core)SignalR进行集成测试,希望后来者可以少走弯路。
痛点
SignalR测试为何困难,原因有下:
WebApplicationFactory,或者说其背后的TestServer,并不提供真的服务器环境,所有默认配置下的网络客户端(当然包括HttpClient)都无法连接至该模拟的服务器。- 然而SignalR客户端所有连接都是在默认网络环境下的,需要替换成
TestServer环境下的客户端
- 然而SignalR客户端所有连接都是在默认网络环境下的,需要替换成
HttpClient并不提供WebSocket连接支持。- 然而SignalR实时通讯首选的是
WebSocket,所以我们还要配个TestServer环境下的WebSocket客户端
- 然而SignalR实时通讯首选的是
- Hub受身份验证保护。
- 替换成
TestServer客户端的时候还需要考虑身份验证
- 替换成
汗流浃背了家人们
关于本文
本文按这三个问题为思路逐步进行,最合理的解决方案会在文末给出。
如果你觉得TL;DR、不想关注过程、或者认为看代码比看文章舒服,可以跳转到文章最后获取项目源码👇
本文只介绍SignalR配置与集成测试,阅读本文前建议做以下准备工作(本文可能不会介绍以下内容):
- SignalR的使用(只提及部分)
- 配置
[Authorize]身份认证(只一笔带过) - 配置.NET集成测试框架,如 xUnit
- 配置
WebApplicationFactory
本文操作环境:
- .NET 8
- xUnit 测试框架
无身份验证SignalR
在引入复杂性之前,应先处理最核心的配置,因此先不配置身份验证。
基本配置
配置Hub
在 .NET 8 中,SignalR已经集成至ASP.NET Core中,因此不需要下载任何Nuget包就能够使用。
配置也十分简洁,首先需要创建一个Hub。Hub相当于是SignalR中的控制器。
创建Hub非常简单,只需要继承Hub即可。以下例子展示了一个最基本的收发消息ChatHub,SendMessage向所有连接广播一条消息:
using Microsoft.AspNetCore.SignalR;namespace SignalR.IntegrationTests;public class ChatHub : Hub
{public async Task SendMessage(string message){await Clients.All.SendAsync("ReceiveMessage", message);}
}
SendMessage是客户端向服务端发送消息的入口- 该方法可以有返回值,返回值会传回调用者
ReceiveMessage是服务端向客户端发送消息的入口message是参数,参数不一定只有一个,也不一定为string
- A向B发送一条聊天信息其实需要经历两次交互
- A向服务器发送消息
- 服务器向B发送消息
配置Program.cs
在Program.cs中注册SignalR组件,最简单的配置如下:
const string HubsPrefix = "/hubs"; // <-- Grouped by prefix /hubsvar builder = WebApplication.CreateBuilder(args);builder.Services.AddSignalR(); // <-- Add SignalRvar app = builder.Build();app.MapGroup(HubsPrefix).MapHub<ChatHub>("/chat"); // <-- Map your ChatHub to /hubs/chatapp.Run();
强类型Hub
上面的例子中,服务端消息方法ReceiveMessage是字符串,众所周知字符串意味着弱类型,无编译时提示,稍不留神可能就会写错。
.NET提供了一个做法强类型化这些方法。
首先定义一个接口:
public interface IChatClientProxy
{public Task ReceiveMessage(string message);
}
由于客户端还是需要以字符串订阅消息,因此函数应以客户端的角度进行命名:
- 是
Receive而不是Send - 虽然是异步方法,但不加
Async后缀
然后将ChatHub修改如下:
public class ChatHub : Hub<IChatClientProxy>
{public async Task SendMessage(string message){await Clients.All.ReceiveMessage(message);}
}
SignalR会自动实现IChatClientProxy接口,当调用这个接口的方法时,对应名称的消息就会被发出。
在Hub外向客户端发送消息
更多时候我们会在Hub之外发送消息,就需要借助IHubContext获取Hub上下文。这个接口也支持强类型化。
以下实现了一个简单的服务,先做一系列检测和记录,再使用IHubContext实现实时发送消息:
using Microsoft.AspNetCore.SignalR;namespace SignalR.IntegrationTests;public class ChatService(IHubContext<ChatHub, IChatClientProxy> _hubContext)
{public async Task SendMessageToAllAsync(string message){// Chek for permissions...// Record to database...// ...await _hubContext.Clients.All.ReceiveMessage(message);}
}
为了保持程序中的一致性,通常情况下也会希望在Hub中引用自己的服务,而不是直接发送消息:
public class ChatHub(ChatService _chatService) : Hub<IChatClientProxy>
{public async Task SendMessage(string message){await _chatService.SendMessageToAllAsync(message);}
}
别忘了在Program.cs中为自己的服务注册依赖注入:
builder.Services.AddScoped<ChatService>();
在客户端中接收SignalR消息
呃,严格意义上你无法在服务端中接收SignalR消息,你需要一个客户端接收服务端发出的信息。
以下代码是客户端代码,它可能位于另一个项目,可以是另一种语言实现,甚至可以处于另一个平台(e.g. Android)
但是它也可以碰巧是同一个平台,又碰巧是C#实现,甚至碰巧在同一个项目 😉
总之如果要在C#中接收SignalR消息,你需要安装客户端Nuget包Microsoft.AspNetCore.SignalR.Client
下面的例子展示了如何向服务端的ChatHub收发消息:
using Microsoft.AspNetCore.SignalR.Client;
using System.Diagnostics;var connection = new HubConnectionBuilder().WithUrl("http://localhost/hubs/chat").Build();
// Add receive message handler.
connection.On<string>("ReceiveMessage", (message) => Debug.WriteLine(message));await connection.StartAsync();// Send message.
await connection.InvokeAsync("SendMessage", "Hello World");await connection.StopAsync();
On方法用于接收消息。注意泛型参数一定要与服务端的类型兼容,否则可能收不到对应消息InvokeAsync方法用于发送消息。第一个参数是远程方法名,第二个起是远程方法对应的参数- 该方法可以有泛型参数
TResult,以接受对应类型的返回值
- 该方法可以有泛型参数
HubConnectionBuilder还可以配置断线重连、身份验证等功能,具体请查阅官方文档
集成测试
准备工作
进行下一步之前,需要先:
- 新建一个 xUnit 项目
- 添加主项目为依赖项
- 在测试项目中安装并配置
WebApplicationFactory - 在测试项目中安装
Microsoft.AspNetCore.SignalR.ClientNuget包
测试用例
根据含义,我们会尝试使用SignalR客户端发送一条消息,然后断言能够收到消息:
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.SignalR.Client;public class WebAppFactory : WebApplicationFactory<Program> { }public class HubIntegrationTests(WebAppFactory _factory) : IClassFixture<WebAppFactory>
{private HubConnection SetupHubConnection(string path){var uri = new Uri(_factory.Server.BaseAddress, path);return new HubConnectionBuilder().WithUrl(uri).Build();}[Fact]public async Task MessageTest(){// --> Arrangevar connection = SetupHubConnection("/hubs/chat");string? received = null;connection.On<string>("ReceiveMessage", (m) => received = m);await connection.StartAsync();string message = "Hello World";// --> Actawait connection.InvokeAsync("SendMessage", message);// Wait for messages to be received. You may need to increase the delay if you're running in a slow environment.await Task.Delay(1);// --> AssertAssert.Equal(message, received);}
}
SetupHubConnection函数用作连接SignalR服务器。- 其中等待了1毫秒以确保有足够的时间接收消息
- 如果你的测试环境是老爷机,可能需要增加等待时间
然而这个用例会失败,错误如下:
System.Net.Http.HttpRequestException : No connection could be made because the target machine actively refused it. (localhost:80)
原因是TestServer并不是真的服务器,它只模拟ASP.NET应用服务器的行为,而不会在宿主机环境中启动真的服务器。因此我们使用常规的方式进行连接是无法访问的。但没有关系……
非WebSocket传输模式的测试
TestServer提供了一个用于连接至测试服务器的HttpMessageHandler对象,也就是任何支持HttpMessageHandler进行Http数据交换的库都可以通过使用该对象访问TestServer。
经常接触.NET测试的伙伴此时已经要素察觉了:HttpClient对HttpMessageHandler就是原生支持的!
然后还有两个好消息:
- 非
WebSocket模式下的SignalR发起的连接使用的就是HttpClient- 没错,只是非
WebSocket,但总比连接失败要好!
- 没错,只是非
SignalR提供了一个配置项,可以替换内部HttpClient使用的HttpMessageHandler
所以解决方案很简单,只需要将上述SetupHubConnection函数修改成以下形式:
private HubConnection SetupHubConnection(string path)
{var server = _factory.Server;var uri = new Uri(server.BaseAddress, path);return new HubConnectionBuilder().WithUrl(uri, o =>{o.HttpMessageHandlerFactory = _ => server.CreateHandler();}).Build();
}
TestServer.CreateHandler()生成了一个HttpMessageHandler,将它赋值给HttpMessageHandlerFactory,可以改变其内部HttpClient的连接行为,使其得以与TestServer进行交互。
问题
虽然测试是能通过了,但是注意到测试时间长达4秒,这对于本地服务器来讲显然是不正常的:
========== Starting test run ==========
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.5.3.1+6b60a9e56a (64-bit .NET 8.0.4)
[xUnit.net 00:00:00.04] Starting: SignalR.IntegrationTests.Tests
[xUnit.net 00:00:04.37] Finished: SignalR.IntegrationTests.Tests
========== Test run finished: 1 Tests (1 Passed, 0 Failed, 0 Skipped) run in 4.4 sec ==========
原因是因为产生了等待。事实上,这个用例并没有建立WebSocket连接,而是在等待WebSocket连接超时后,转为了使用LongPolling模式连接。
如果我们强制限制SignalR客户端使用WebSocket连接:
private HubConnection SetupHubConnection(string path)
{var server = _factory.Server;var uri = new Uri(server.BaseAddress, path);return new HubConnectionBuilder().WithUrl(uri, o =>{o.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets; // WebSockets only.o.HttpMessageHandlerFactory = _ => server.CreateHandler();}).Build();
}
这个用例会在4秒后超时失败:
System.AggregateException : Unable to connect to the server with any of the available transports. (WebSockets failed: Unable to connect to the remote server) (ServerSentEvents failed: The transport is disabled by the client.) (LongPolling failed: The transport is disabled by the client.)
轮询并不是一般情况下的连接方式,而且我们也不希望每个连接都等待4秒,所以,有没有办法能够进行Socket连接?
WebSocket传输模式的测试
WebSocket连接失败的原因是WebSocketClient独立于HttpClient,虽然我们构建了SignalR内部HttpClient与TestServer之间的连接,但是并没有改变WebSocketClient,它仍然是向真正的宿主机环境建立连接,所以必然会失败。
但是没有关系,这个问题早在几年前就被SignalR团队注意到,并提供了替换WebSocketClient的配置项:
private HubConnection SetupHubConnection(string path)
{var server = _factory.Server;var uri = new Uri(server.BaseAddress, path);return new HubConnectionBuilder().WithUrl(uri, o =>{o.Transports = HttpTransportType.WebSockets;o.HttpMessageHandlerFactory = _ => server.CreateHandler();// Support WebSocket transports.o.WebSocketFactory = async (context, cancellationToken) =>{var wsClient = server.CreateWebSocketClient();return await wsClient.ConnectAsync(context.Uri, cancellationToken);};o.SkipNegotiation = true;}).Build();
}
通过配置WebSocketFactory,可以将默认的WebSocketClient换成TestServer提供的客户端。从而能够对其进行WebSocket访问。
在WebSocket模式下,顺便设置了SkipNegotiation,可以减少协商时间,而不会影响结果。
这里其实可以省略
HttpMessageHandlerFactory的配置,因为使用WebSocket时不会用到HttpClient。但如果使用LongPolling则很重要,因此还是保留以供选择。
修改了WebSoketClient配置后,重新运行测试用例,这次可以快速以WebSocket模式通过测试:
========== Starting test run ==========
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.5.3.1+6b60a9e56a (64-bit .NET 8.0.4)
[xUnit.net 00:00:00.03] Starting: SignalR.IntegrationTests.Tests
[xUnit.net 00:00:00.21] Finished: SignalR.IntegrationTests.Tests
========== Test run finished: 1 Tests (1 Passed, 0 Failed, 0 Skipped) run in 216 ms ==========
带身份验证SignalR
身份配置
SignalR的身份验证方式
SignalR可以使用Cookie与Token令牌两种方式进行身份认证。
Cookie是浏览器环境下的首选方式,可以自动传递凭证;而Token则是非浏览器客户端下最简便的做法。
由于Cookie开箱即用,不需要做额外配置,因此本文只重点介绍Token做法。
SignalR Token令牌传递方式
根据SignalR文档,在不同情况下有不同的传达方式:
- 在非浏览器环境中,以
Authorization请求头的方式传递 - 在浏览器环境的
WebSocket,Server Side Event模式下,无法使用自定义请求头,需要以查询字符串的方式传递- 该查询字符串需要在身份验证服务器自行读取接收
服务端配置接收access_token
所有无法自定义连接请求头的情况下,都约定使用一个写死的(😅微软你也干这事啊)查询字符串access_token作为身份认证的参数。
你写死不要紧,要紧的是我们使用SignalR是需要手动处理这个查询字符串的,否则这种情况下永远无法触发身份验证。
虽然官网有说明,但是总有像我一样的愣头青不喜欢看官方文档然后捣鼓了一整天才发现涅麻麻的要手动配置这个查询字符串。
所以为了减少愣头青,请你务必:
按照以下操作配置查询字符串!
按照以下操作配置查询字符串!
按照以下操作配置查询字符串!
接收查询字符串
需要在SignalR服务端中主动接收这个查询字符串。
使用不同的身份验证库,需要以不同的方式进行接收:
- 你使用了内置的
JWT库或者Identity Server,可以参照官方文档进行配置 - 你使用了Identity内置的
BearerToken,可以在Bearer Token中间件进行配置(见下文) - 你使用了其它的身份验证库,基本也是相同的套路:需要在验证请求事件中手动将该查询字符串赋值为用户凭证
Identity 内置Bearer Token身份验证
我这里使用了 .NET 8 Identity的内置BearerToken,所以能够实现目标的最小配置Program.cs是这样的:
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using SignalR.IntegrationTests;const string HubsPrefix = "/hubs";var builder = WebApplication.CreateBuilder(args);builder.Services.AddAuthorization();
builder.Services.AddAuthentication(IdentityConstants.BearerScheme).AddCookie(IdentityConstants.ApplicationScheme).AddBearerToken(IdentityConstants.BearerScheme, o =>{o.Events = new(){OnMessageReceived = context =>{var accessToken = context.Request.Query["access_token"];var path = context.HttpContext.Request.Path;// If the request is for our hub...if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments(HubsPrefix)){// Read the token out of the query stringcontext.Token = accessToken;}return Task.CompletedTask;}};});
builder.Services.AddIdentityCore<IdentityUser>().AddApiEndpoints().AddEntityFrameworkStores<IdentityDbContext>();
builder.Services.AddDbContext<IdentityDbContext>(x => x.UseInMemoryDatabase("db"));builder.Services.AddSignalR();var app = builder.Build();app.MapIdentityApi<IdentityUser>();app.MapGroup(HubsPrefix).MapHub<ChatHub>("/chat");app.Run();
使用身份验证保护Hub
与Controller一样,通过使用Authorize、AllowAnonymous特性控制对Hub的访问
[Authorize]
public class ChatHub(ChatService _chatService) : Hub<IChatClientProxy>
{// ......
}
为Hub连接提供身份验证
Cookie验证
- 浏览器环境中,正常使用Cookie登录,凭证会在请求时自动携带
- 非浏览器环境中,可以通过手动设置Cookie请求头实现Cookie验证
- 但这种做法不如使用Token更加正规
Token令牌验证
Token可以在客户端发起连接前使用AccessTokenProvider提供。
var connection = new HubConnectionBuilder().WithUrl("http://localhost/hubs/chat", options =>{options.AccessTokenProvider = () => Task.FromResult(token);}).Build();
考虑到重连与Token过期问题,
AccessTokenProvider接受的是一个工厂函数,你可以选择动态获取新Token,而不是写死一个值
集成测试
由于我们替换了默认的WebSocketClient,我们需要手动携带Token令牌,以支持WebSocket模式下的身份验证;非WebSocket的身份验证仍然使用AccessTokenProvider配置项,无需修改。因此修改SetupHubConnection方法:
- 配置
AccessTokenProvider参数,使非WebSocket连接方式能够携带令牌 - 将
token添加至WebSocketClient中,使WebSocket连接方式能够携带令牌。由于是非浏览器环境,有两种方案可以选择:- 添加名为
access_token的查询字符串 - 添加
Authorization请求头
- 添加名为
小孩子才做选择,我全都要。
private HubConnection SetupHubConnection(string path, string? token = null)
{var server = _factory.Server;var uri = new Uri(server.BaseAddress, path);return new HubConnectionBuilder().WithUrl(uri, o =>{o.Transports = HttpTransportType.WebSockets;o.HttpMessageHandlerFactory = _ => server.CreateHandler();o.WebSocketFactory = async (context, cancellationToken) =>{var wsClient = server.CreateWebSocketClient();if (token != null){// Authentication for socket transports. (Chooses one of these.)// Option1: Use request headers.wsClient.ConfigureRequest = request => request.Headers.Authorization = new($"Bearer {token}");// Option2: Add access token to query string.uri = new Uri(QueryHelpers.AddQueryString(context.Uri.ToString(), "access_token", token));// I like both ;)}else{uri = context.Uri;}return await wsClient.ConnectAsync(uri, cancellationToken);};o.SkipNegotiation = true;// Authentication for non-socket transports. (Can be omitted here.)o.AccessTokenProvider = () => Task.FromResult(token);}).Build();
}
最后在用例中指定token参数,即可成功通过测试。
Q: 我应该如何生成token令牌?
如何生成令牌取决于你身份验证的实现方式。
在使用WebApplicationFactory的集成测试中,你可以比较容易地使用真实的用户与正常的登录方式获取令牌;如果身份认证本身并不是集成测试的关键,你可以设法使用测试替身替换掉原有的身份验证程序。(但一般情况下这只会更麻烦)
如果你想了解如何以正常登录方式获取令牌,可以查看我的源码👇
至此,所有问题解决!现在我们可以用SignalR WebSocket模式对带身份认证的Hub进行集成测试了!
项目源码
- Github
参考资料
- Authentication and authorization in ASP.NET Core SignalR
- [SignalR] Better integration with TestServer
- SignalR Hub auth?
相关文章:
ASP.NET Core SignalR 配置与集成测试究极指南
这篇文章也可以在我的博客中查看 前言 哥们最近都在埋头苦干,沉默是金,有一段时间没更新博客了。然而今儿SignalR集成测试实属是给我整破防了。虽说SignalR是.NET官方维护的实时通信库,已经开发了有十几年,甚至已经编入至了core…...
JENKINS 安装,学习运维从这里开始
Download and deployJenkins – an open source automation server which enables developers around the world to reliably build, test, and deploy their softwarehttps://www.jenkins.io/download/首先点击上面。下载Jenkins 为了学习,从windows开始&#x…...
大语言模型从Scaling Laws到MoE
1、摩尔定律和伸缩法则 摩尔定律(Moores law)是由英特尔(Intel)创始人之一戈登摩尔提出的。其内容为:集成电路上可容纳的晶体管数目,约每隔两年便会增加一倍;而经常被引用的“18个月”…...
四级英语翻译随堂笔记
降维表达:中译英,英译英 没有强调主语,没有说明主语:用被动 但如果实在不行,再增添主语 不会就不翻译,不要乱翻译 以xxx为背景:against the backdrop of the xxx eg:against the backdrop of…...
Nacos支持的配置格式及其在微服务架构中的应用
今天,我想和大家探讨一下Nacos这一重要的微服务组件,特别是它所支持的配置格式以及这些格式在微服务架构中的应用。 一、Nacos简介 Nacos是阿里巴巴开源的一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它提供了服务发现、配置管理…...
2024年华为OD机试真题-小明找位置-(C++)-OD统一考试(C卷D卷)
题目描述: 小朋友出操,按学号从小到大排成一列;小明来迟了,请你给小明出个主意,让他尽快找到他应该排的位置。 算法复杂度要求不高于nLog(n);学号为整数类型,队列规模<=10000; 输入描述: 1、第一行:输入已排成队列的小朋友的学号(正整数),以”,”隔开; …...
机器人系统ros2内部接口介绍
内部 ROS 接口是公共 C API ,供创建客户端库或添加新的底层中间件的开发人员使用,但不适合典型 ROS 用户使用。 ROS客户端库提供大多数 ROS 用户熟悉的面向用户的API,并且可能采用多种编程语言。 内部API架构概述 内部接口主要有两个&#x…...
跟随Facebook的足迹:社交媒体背后的探索之旅
在当今数字化时代,社交媒体已经成为了人们日常生活中不可或缺的一部分。而在这庞大的社交媒体网络中,Facebook作为其中的巨头,一直在引领着潮流。从创立之初的一个大学社交网络到如今的全球性平台,Facebook的发展历程承载了无数故…...
面试题分享之Java并发篇
注意:文章若有错误的地方,欢迎评论区里面指正 🍭 系列文章目录 面试题分享之Java集合篇(三) 面试题分享之Java集合篇(二) 面试题分享之Java基础篇(三) 前言 今天给小…...
bpmn-js 多实例配置MultiInstanceLoopCharacteristics实现或签会签
使用bpmn-js流程图开发过程中会遇到会签和或签的问题,这个时候我们就需要使用多实例配置来实现BPMN 2.0的配置实现了,多实例任务,是从流程编辑概念之初也就是Activiti时期就存在的一个方式。所谓的多实例任务也就是字面意思,一个任务由多个人完成,常见于我们的审批流程的或…...
【gpedit.msc】组策略编辑器的安装,针对windows家庭版,没有此功能
创建一个记事本文件然后放入以下内容 echo offpushd "%~dp0"dir /b %systemroot%\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientExtensions-Package~3*.mum >gp.txtdir /b %systemroot%\servicing\Packages\Microsoft-Windows-GroupPolicy-…...
带EXCEL附件邮件发送相关代码
1.查看生成的邮件 2.1 非面向对象的方式(demo直接copy即可) REPORT Z12. DATA: IT_DOCUMENT_DATA TYPE SODOCCHGI1,IT_CONTENT_TEXT TYPE STANDARD TABLE OF SOLISTI1 WITH HEADER LINE,IT_PACKING_LIST TYPE TABLE OF SOPCKLSTI1 WITH HEADER LIN…...
【算法作业】均分卡牌,购买股票
问题描述 John 有两个孩子,在 John病逝后,留下了一组价值不一定相同的魔卡, 现在要求你设计一种策略,帮John的经管人将John的这些遗产分给他的两个孩子,使得他们获得的遗产差异最小(每张魔卡不能分拆&#…...
python作业
题目 分析 步骤: 判断先画空格还是数字 当有n层时,第i层有多少个空格第i层的起始数字是几,结尾是几,即数字取值范围当有n层时,第i层有多少个数字 代码 模式A n int(input("请输入行数:")) for i in range(…...
【Linux的文件篇章 - 管道文件】
Linux学习笔记---013 Linux的管道文件1、进程间通信1.1、进程为什么要通信?1.2、进程如何通信?1.3、进程通信的方式? 2、匿名管道2.1、理解一种现象2.2、基本概念和管道原理 3、管道的使用3.1、代码样例3.2、如何使用管道通信呢?3…...
C# 局部静态函数,封闭方法中的最佳选择
C# 局部静态函数,封闭方法中的最佳选择 简介特性 应用场景辅助计算递归与尾递归优化筛选与过滤操作查找与映射操作 生命周期静态局部函数 vs 普通局部函数性能封装性可读性 简介 C# 局部静态函数(Local Static Functions)是一种函数作用域内…...
【MySQL】MySQL 8.4.0 长期支持版(LTS)安装
就在2024年 “5.1” 节前,MySQL官方发布了8.4.0长期支持版(LTS - Long Term Support)。根据官方提供的文档,在本地虚拟机进行安装测试。 安装、配置和启动过程记录如下: 第一步,上传到安装包(my…...
nest中的ORM
在 Nest.js 中执行 SQL 查询通常涉及使用 TypeORM 或 Sequelize 这样的 ORM(对象-关系映射)库。这些库使得在 Nest.js 应用程序中连接和操作 SQL 数据库变得更加简单和直观。 以下是一个使用 TypeORM 在 Nest.js 中执行 SQL 查询的示例代码:…...
TCP(Transmission Control Protocol,传输控制协议)如何保证数据的完整性?
TCP(Transmission Control Protocol,传输控制协议)通过一系列机制来保证数据传输的可靠性和无错性,这些机制主要包括: 校验和:TCP报文段包含一个校验和字段,用于检测数据在传输过程中是否出错。…...
Numpy库介绍
NumPy(Numerical Python的缩写)是Python中用于科学计算的一个强大的库。它提供了高性能的多维数组对象(即ndarray)、用于处理这些数组的工具以及用于数学函数操作的函数。让我为你介绍一下它的一些主要功能: 1. 多维数…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
