13.【.NET 8 实战--孢子记账--从单体到微服务--转向微服务】--微服务基础工具与技术--Refit
在微服务架构中,不同服务之间经常需要相互调用以完成复杂业务流程,而 Refit 能让这种“跨服务调用”变得简洁又可靠。开发者只需将对外暴露的 REST 接口抽象成 C# 接口,并通过共享库或内部 NuGet 包在各服务中引用,这种契约优先的做法能够确保接口签名和数据模型在编译期就被校验,避免了运行时因 URL 拼写或参数不一致引发的问题。借助 .NET Core 的依赖注入与 HttpClientFactory,将 Refit 客户端注册到 DI 容器后,所有跨服务调用都使用由框架管理的 HttpClient 实例,不仅解决了连接复用和 DNS 刷新问题,还能集中配置超时、重试和熔断策略(如 Polly)。由于 Refit 的所有方法均返回 Task 或 Task,它与 async/await 模型无缝契合,在高并发场景下既能保持响应性,又能充分利用线程资源。并且基于接口的设计也极大地提升了可测试性:测试时只需 Mock 接口即可模拟各种返回结果和异常场景,无需启动真实服务,从而加快测试反馈和覆盖率。通过共享契约、类型安全、依赖注入和异步支持,Refit 将繁琐的 HTTP 调用封装为直观的接口方法,让微服务之间的调用像本地方法调用一样简单、可靠且易于维护。
一、 安装与配置
安装与配置 Refit 非常简单,主要包括以下几步:
-
安装 NuGet 包
- 核心库:
Refit
- ASP.NET Core HttpClientFactory 集成:
Refit.HttpClientFactory
- 可选Newtonsoft.Json 支持:
Refit.Newtonsoft.Json
- 核心库:
-
注册到依赖注入容器
在Program.cs
中,通过AddRefitClient<T>()
将接口客户端注册到 DI 容器,并配置基础地址及其他策略:builder.Services.AddRefitClient<IMyApi>() // 注入接口.ConfigureHttpClient(c => // 配置 HttpClientc.BaseAddress = new Uri("https://api.abc.com")).AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(10))); // Polly 重试/熔断
-
自定义序列化
默认使用System.Text.Json
,性能优异。如果需要Newtonsoft.Json
特性(如复杂契约、忽略循环引用),安装Refit.Newtonsoft.Json
并在注册时传入RefitSettings
:var settings = new RefitSettings {ContentSerializer = new NewtonsoftJsonContentSerializer(new JsonSerializerSettings{ContractResolver = new CamelCasePropertyNamesContractResolver(),NullValueHandling = NullValueHandling.Ignore}) }; builder.Services.AddRefitClient<IBlogApi>(settings).ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.abc.com"));
-
使用客户端
在需要调用的服务或控制器中直接构造依赖:public class MyService {private readonly IMyApi _api;public MyService(IMyApi api) => _api = api;public async Task DoWorkAsync() {var result = await _api.GetDataAsync();// more code} }
完成上述步骤后,Refit 会在运行时自动生成 HTTP 客户端实现,处理 URL 拼接、序列化/反序列化及异常封装。通过与 HttpClientFactory
、Polly、DI 容器的无缝集成,既能获得高性能和可扩展性,又能保持代码的简洁与可维护。
二、定义接口
2.1 定义接口
-
创建接口定义
在 Refit 中,接口定义是使用接口和特性来描述的。首先,我们需要创建一个接口,并在其中定义我们要调用的 HTTP 方法。以下是一个简单的示例:using Refit; using System.Threading.Tasks;public interface IMyApi {[Get("/users/{id}")]Task<User> GetUserAsync(int id);[Post("/users")]Task<User> CreateUserAsync([Body] User user); }
在这个示例中,我们定义了一个名为
IMyApi
的接口,其中包含两个方法:GetUserAsync
和CreateUserAsync
。每个方法都使用了不同的 HTTP 请求类型特性([Get]
和[Post]
)。 -
特性注解详解
Refit 提供了一组特性用于描述 HTTP 请求的不同注解,常用注解如下:-
[Get]
:用于发送 HTTP GET 请求。可以包含 URL 路径参数和查询参数。[Get("/users/{id}")] Task<User> GetUserAsync(int id);
-
[Post]
:用于发送 HTTP POST 请求。通常用于创建资源。[Post("/users")] Task<User> CreateUserAsync([Body] User user);
-
[Put]
:用于发送 HTTP PUT 请求。通常用于更新资源。[Put("/users/{id}")] Task<User> UpdateUserAsync(int id, [Body] User user);
-
[Delete]
:用于发送 HTTP DELETE 请求。用于删除资源。[Delete("/users/{id}")] Task DeleteUserAsync(int id);
-
[Body]
:用于指定请求正文中的参数。通常与 POST 和 PUT 请求一起使用。[Post("/users")] Task<User> CreateUserAsync([Body] User user);
-
[Query]
:用于指定查询参数。可以将方法参数映射到查询字符串中。[Get("/search")] Task<List<User>> SearchUsersAsync([Query] string name);
-
[Header]
:用于指定请求头。可以在方法级别或参数级别使用。[Get("/users")] [Header("Authorization", "Bearer")] Task<List<User>> GetUsersAsync();
-
[AliasAs]
:用于指定参数的别名。可以用于更改 URL 路径参数或查询参数的名称。[Get("/users/{userId}")] Task<User> GetUserAsync([AliasAs("userId")] int id);
-
通过这些特性,我们可以灵活地定义接口,并描述我们希望 Refit 如何生成和发送 HTTP 请求。
三、使用 Refit 进行 HTTP 调用
3.1 同步调用
虽然 Refit 更适合异步调用,但在某些情况下,我们可能需要进行同步调用。以下是一个使用 Refit 进行同步调用的示例:
using Refit;
using System;public interface IMyApi
{[Get("/users/{id}")]Task<User> GetUserAsync(int id);
}public class ApiService
{private readonly IMyApi _api;public ApiService(){_api = RestService.For<IMyApi>("https://api.abc.com");}public User GetUser(int id){// 使用 Task.Result 进行同步调用return _api.GetUserAsync(id).Result;}
}
在这个示例中,我们定义了一个 IMyApi
接口,并创建了一个 ApiService
类。在 GetUser
方法中,我们使用 Task.Result
来进行同步调用。这种方式虽然简单,但可能会导致线程阻塞,因此应谨慎使用。
3.2 异步调用
Refit 最常用的方式是进行异步调用。以下是一个使用 Refit 进行异步调用的示例:
using Refit;
using System.Threading.Tasks;public interface IMyApi
{[Get("/users/{id}")]Task<User> GetUserAsync(int id);[Post("/users")]Task<User> CreateUserAsync([Body] User user);
}public class ApiService
{private readonly IMyApi _api;public ApiService(){_api = RestService.For<IMyApi>("https://api.abc.com");}public async Task<User> GetUserAsync(int id){return await _api.GetUserAsync(id);}public async Task<User> CreateUserAsync(User user){return await _api.CreateUserAsync(user);}
}
在这个示例中,我们同样定义了一个 IMyApi
接口,并创建了一个 ApiService
类。在 GetUserAsync
和 CreateUserAsync
方法中,我们使用 await
关键字来进行异步调用。这种方式不会阻塞线程,更适合现代应用程序的开发。
通过以上示例,我们可以看到使用 Refit 进行 HTTP 调用的基本方法。根据具体需求,我们可以选择同步调用或异步调用,但在大多数情况下,异步调用是更好的选择。
四、错误处理
4.1 异常处理机制
在使用 Refit 进行 HTTP 调用时,可能会遇到各种异常情况。Refit 提供了一种简单的方式来处理这些异常。以下是一个基本的异常处理示例:
using Refit;
using System;
using System.Net.Http;
using System.Threading.Tasks;public class ApiService
{private readonly IMyApi _api;public ApiService(){_api = RestService.For<IMyApi>("https://api.abc.com");}public async Task<User> GetUserAsync(int id){try{return await _api.GetUserAsync(id);}catch (ApiException ex){// 处理 API 异常Console.WriteLine($"API Error: {ex.StatusCode}");// 可以根据具体的 StatusCode 进行进一步处理throw;}catch (HttpRequestException ex){// 处理 HTTP 请求异常Console.WriteLine($"Request Error: {ex.Message}");throw;}catch (Exception ex){// 处理其他类型的异常Console.WriteLine($"Unexpected Error: {ex.Message}");throw;}}
}
在这个示例中,我们在 GetUserAsync
方法中使用了 try-catch
结构来捕获并处理不同类型的异常。ApiException
用于处理 Refit 特定的 API 异常,而 HttpRequestException
用于处理一般的 HTTP 请求异常。其他类型的异常则通过通用的 Exception
来处理。
4.2 常见问题与解决方案
-
404 Not Found 错误
问题: 当请求的资源不存在时,会返回 404 错误。
解决方案: 确保请求的 URL 和参数正确。同时,可以在捕获ApiException
时检查StatusCode
并进行相应处理。catch (ApiException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) {Console.WriteLine("Resource not found."); }
-
401 Unauthorized 错误
问题: 当缺少或使用了无效的身份验证令牌时,会返回 401 错误。
解决方案: 确保请求中包含正确的身份验证令牌。可以在接口定义中使用[Header]
特性添加身份验证头。[Get("/protected-resource")] [Header("Authorization", "Bearer")] Task<ProtectedResource> GetProtectedResourceAsync();
-
超时错误
问题: 当请求超时时,会抛出TaskCanceledException
。
解决方案: 可以设置HttpClient
的超时时间,并在捕获TaskCanceledException
时进行相应处理。var httpClient = new HttpClient {Timeout = TimeSpan.FromSeconds(30) }; var api = RestService.For<IMyApi>(httpClient);try {var result = await api.GetUserAsync(id); } catch (TaskCanceledException ex) {Console.WriteLine("Request timed out."); }
-
网络连接错误
问题: 当网络连接失败时,会抛出HttpRequestException
。
解决方案: 可以在捕获HttpRequestException
时进行重试或其他处理。catch (HttpRequestException ex) {Console.WriteLine("Network error: " + ex.Message);// 可以在这里实现重试逻辑 }
通过上述示例和解决方案,我们可以有效地处理在使用 Refit 进行 HTTP 调用时遇到的各种错误和异常情况。
五、Refit 的高级功能
5.1 自定义处理程序
Refit 允许我们自定义 HTTP 处理程序,以便在发送请求之前或接收响应之后执行额外的逻辑。通过创建自定义的 HttpMessageHandler
,我们可以实现请求重试、日志记录、身份验证等功能。
- 创建自定义处理程序
以下是创建自定义处理程序的示例:using System.Net.Http; using System.Threading; using System.Threading.Tasks;public class CustomHttpMessageHandler : DelegatingHandler {protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){// 在发送请求之前执行逻辑Console.WriteLine("Sending request to " + request.RequestUri);// 发送请求并获取响应var response = await base.SendAsync(request, cancellationToken);// 在接收响应之后执行逻辑Console.WriteLine("Received response with status code " + response.StatusCode);return response;} }
- 使用自定义处理程序
在创建 Refit 客户端时,可以将自定义处理程序传递给HttpClient
:using Refit; using System.Net.Http;public class ApiService {private readonly IMyApi _api;public ApiService(){var handler = new CustomHttpMessageHandler{InnerHandler = new HttpClientHandler()};var httpClient = new HttpClient(handler){BaseAddress = new Uri("https://api.abc.com")};_api = RestService.For<IMyApi>(httpClient);} }
通过这种方式,我们可以在整个请求生命周期中插入自定义逻辑。
5.2 拦截器与中间件
拦截器与中间件提供了一种更高级的方式来处理 HTTP 请求和响应。我们可以使用拦截器来修改请求或响应,或者添加额外的处理逻辑。
-
创建拦截器
以下是创建请求和响应拦截器的示例:using System.Net.Http; using System.Threading; using System.Threading.Tasks;public class LoggingHandler : DelegatingHandler {protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){// 日志记录请求信息Console.WriteLine("Request:");Console.WriteLine(request.ToString());if (request.Content != null){Console.WriteLine(await request.Content.ReadAsStringAsync());}var response = await base.SendAsync(request, cancellationToken);// 日志记录响应信息Console.WriteLine("Response:");Console.WriteLine(response.ToString());if (response.Content != null){Console.WriteLine(await response.Content.ReadAsStringAsync());}return response;} }
-
使用拦截器
同样,我们可以在创建 Refit 客户端时使用拦截器:using Refit; using System.Net.Http;public class ApiService {private readonly IMyApi _api;public ApiService(){var handler = new LoggingHandler{InnerHandler = new HttpClientHandler()};var httpClient = new HttpClient(handler){BaseAddress = new Uri("https://api.abc.com")};_api = RestService.For<IMyApi>(httpClient);} }
-
中间件的使用
Refit 本身并不直接支持中间件,但我们可以通过自定义处理程序和拦截器来实现类似中间件的功能。通过将多个处理程序链接在一起,我们可以创建一个请求处理管道,每个处理程序都可以在请求和响应的不同阶段插入逻辑。例如,我们可以将日志记录、身份验证和重试逻辑分别实现为不同的处理程序,并将它们组合在一起:
var loggingHandler = new LoggingHandler {InnerHandler = new AuthenticationHandler{InnerHandler = new RetryHandler{InnerHandler = new HttpClientHandler()}} };var httpClient = new HttpClient(loggingHandler) {BaseAddress = new Uri("https://api.abc.com") };_api = RestService.For<IMyApi>(httpClient);
通过这种方式,我们可以实现灵活且可扩展的请求处理管道,满足各种复杂的需求。
六、实战案例
6.1 实战案例
-
案例背景
在本节中,我们将通过一个简单的示例项目来演示如何使用 Refit 进行 HTTP 调用。这个项目将模拟一个用户管理系统,我们将实现以下功能:- 获取用户信息
- 创建新用户
我们将首先定义接口,然后配置依赖注入,最后调用接口并处理响应。
-
实现步骤
-
创建接口定义
首先,我们需要创建一个接口来定义我们的 HTTP 调用。这些接口将使用 Refit 特性来描述每个请求。using Refit; using System.Threading.Tasks;public interface IMyApi {[Get("/users/{id}")]Task<User> GetUserAsync(int id);[Post("/users")]Task<User> CreateUserAsync([Body] User user); }
在这个接口定义中,我们定义了两个方法:
GetUserAsync
和CreateUserAsync
。GetUserAsync
方法使用[Get]
特性来标识这是一个 GET 请求,并且 URL 中包含一个路径参数{id}
。CreateUserAsync
方法使用[Post]
特性来标识这是一个 POST 请求,并且请求正文中包含一个User
对象。 -
配置依赖注入
接下来,我们需要在项目中配置依赖注入,以便在需要的地方可以使用我们的 API 接口。
在Program.cs
文件中,我们需要添加以下代码:using Microsoft.Extensions.DependencyInjection; using Refit;public class Program {// more codebuilder.Services.AddRefitClient<IMyApi>() // 注入接口.ConfigureHttpClient(c => // 配置 HttpClientc.BaseAddress = new Uri("https://api.abc.com"));// more code }
在这个配置中,我们使用
AddRefitClient
方法将IMyApi
接口添加到依赖注入容器中,并设置基础地址为https://api.abc.com
。 -
调用接口并处理响应
最后,我们可以在需要的地方调用接口并处理响应。在一个控制器或服务中,我们可以注入IMyApi
接口并使用它来进行 HTTP 调用。using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks;[ApiController] [Route("api/[controller]")] public class UsersController : ControllerBase {private readonly IMyApi _api;public UsersController(IMyApi api){_api = api;}[HttpGet("{id}")]public async Task<ActionResult<User>> GetUser(int id){try{var user = await _api.GetUserAsync(id);return Ok(user);}catch (ApiException ex){return StatusCode((int)ex.StatusCode, ex.Content);}}[HttpPost]public async Task<ActionResult<User>> CreateUser(User user){try{var createdUser = await _api.CreateUserAsync(user);return CreatedAtAction(nameof(GetUser), new { id = createdUser.Id }, createdUser);}catch (ApiException ex){return StatusCode((int)ex.StatusCode, ex.Content);}} }
在这个控制器中,我们定义了两个方法:
GetUser
和CreateUser
。我们使用依赖注入的IMyApi
接口来调用 API,并处理可能的异常。在GetUser
方法中,我们使用await
关键字来异步调用GetUserAsync
方法,并返回用户信息。在CreateUser
方法中,我们使用await
关键字来异步调用CreateUserAsync
方法,并返回创建的用户信息。
-
通过以上步骤,我们完成了一个简单的 Refit 实战案例。这个案例演示了如何定义接口、配置依赖注入,以及调用接口并处理响应。在实际项目中,我们可以根据需要扩展和修改这个示例。
七、总结
在微服务架构中,不同服务之间经常需要相互调用以完成复杂业务流程,而 Refit 能让这种“跨服务调用”变得简洁又可靠。通过将对外暴露的 REST 接口抽象成 C# 接口,并通过共享库或内部 NuGet 包在各服务中引用,开发者可以确保接口签名和数据模型在编译期被校验,避免了运行时因 URL 拼写或参数不一致引发的问题。
借助 .NET Core 的依赖注入与 HttpClientFactory,将 Refit 客户端注册到 DI 容器后,所有跨服务调用都使用由框架管理的 HttpClient 实例,不仅解决了连接复用和 DNS 刷新问题,还能集中配置超时、重试和熔断策略(如 Polly)。由于 Refit 的所有方法均返回 Task 或 Task,它与 async/await 模型无缝契合,在高并发场景下既能保持响应性,又能充分利用线程资源。
基于接口的设计也极大地提升了可测试性,测试时只需 Mock 接口即可模拟各种返回结果和异常场景,无需启动真实服务,从而加快测试反馈和覆盖率。通过共享契约、类型安全、依赖注入和异步支持,Refit 将繁琐的 HTTP 调用封装为直观的接口方法,让微服务之间的调用像本地方法调用一样简单、可靠且易于维护。
在本文中,我们详细介绍了 Refit 的安装与配置、接口定义、HTTP 调用、错误处理、高级功能以及实战案例。通过这些内容,读者可以掌握使用 Refit 进行跨服务调用的基本方法和技巧,并在实际项目中灵活应用。
相关文章:
13.【.NET 8 实战--孢子记账--从单体到微服务--转向微服务】--微服务基础工具与技术--Refit
在微服务架构中,不同服务之间经常需要相互调用以完成复杂业务流程,而 Refit 能让这种“跨服务调用”变得简洁又可靠。开发者只需将对外暴露的 REST 接口抽象成 C# 接口,并通过共享库或内部 NuGet 包在各服务中引用,这种契约优先的…...
C++ 并发性能优化实战:提升多线程应用的效率与稳定性
🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,获得2024年博客之星荣誉证书,高级开发工程师,数学专业,拥有高级工程师证书;擅长C/C、C#等开发语言,熟悉Java常用开发技术,…...
前端性能优化的全方位方案【待进一步结合项目】
以下是前端性能优化的全方位方案,结合代码配置和最佳实践,涵盖从代码编写到部署的全流程优化: 一、代码层面优化 1. HTML结构优化 <!-- 语义化标签减少嵌套 --> <header><nav>...</nav> </header> <main&…...
(undone) 并行计算 CS149 Lecture3 (现代多核处理器2 + ISPC编程抽象)
url: https://www.bilibili.com/video/BV1du17YfE5G?spm_id_from333.788.videopod.sections&vd_source7a1a0bc74158c6993c7355c5490fc600&p3 如上堂课,超线程技术通过储存不同线程的 execution context,能够在一个线程等待 IO 的时候低成本切换…...
DiffAD:自动驾驶的统一扩散建模方法
25年3月来自新加坡公司 Carion 和北航的论文“DiffAD: A Unified Diffusion Modeling Approach for Autonomous Driving”。 端到端自动驾驶 (E2E-AD) 已迅速成为实现完全自动驾驶的一种有前途的方法。然而,现有的 E2E-AD 系统通常采用传统的多任务框架,…...
QScrollArea 内部滚动条 QSS 样式失效问题及解决方案
在使用 Qt 进行 UI 开发时,我们经常希望通过 QSS(Qt Style Sheets)自定义控件的外观,比如为 QScrollArea 的内部滚动条设置特定的样式。然而,有开发者遇到了这样的问题:在 UI 设计器中预览 QSS 显示效果正常,但程序运行时却显示为系统默认样式。经过反复测试和调试,最终…...
换脸视频FaceFusion3.1.0-附整合包
2025版最强换脸软件FaceFusion来了(附整合包)超变态的AI换脸教程 2025版最强换脸软件FaceFusion来了(附整合包)超变态的AI换脸教程 整合包地址: 「Facefusion_V3.1.0」 链接:https://pan.quark.cn/s/f71601…...
Qt 入门 1 之第一个程序 Hello World
Qt 入门1之第一个程序 Hello World 直接上操作步骤从头开始认识,打开Qt Creator,创建一个新项目,并依次执行以下操作 在Qt Creator中,一个Kits 表示一个完整的构建环境,包括编译器、Qt版本、调试器等。在上图中可以直…...
无锁队列简介与实现示例
1. 简介 无锁队列是一种数据结构,旨在在多线程环境中实现高效的并发访问,而无需使用传统的锁机制(如互斥锁)。无锁队列通过使用原子操作(如CAS,Compare-And-Swap)来确保线程安全,从…...
SpringMVC与SpringCloud的区别
SpringMVC与SpringCloud的核心区别 功能定位 • SpringMVC: 基于Spring框架的Web层开发模块,采用MVC(Model-View-Controller)模式,专注于处理HTTP请求、路由分发(如DispatcherServlet)和视图…...
STM32F103C8T6单片机开发:简单说说单片机的外部GPIO中断(标准库)
目录 前言 如何使用STM32F1系列的标准库完成外部中断的抽象 初始化我们的GPIO为输入的一个模式 初识GPIO复用,开启GPIO的复用功能时钟 GPIO_EXTILineConfig和EXTI_Init配置外部中断参数 插入一个小知识——如何正确的配置结构体? 初始化中断&#…...
Python urllib3 全面指南:从基础到实战应用
欢迎来到涛涛的频道,今天用到了urllib3,和大家分享下。 1、介绍 urllib3 urllib3 是 Python 中一个功能强大且用户友好的 HTTP 客户端库,它提供了许多标准库 urllib 所不具备的高级特性。作为 Python 生态中最受欢迎的 HTTP 库之一…...
25.5 GLM-4优化RAG实战:0.1%参数实现准确率飙升30%,成本直降90%!
使用 GLM-4 优化 RAG 程序:基于标注数据的 Adapter 训练实战 关键词:GLM-4 优化, RAG 增强, 数据标注, Adapter 训练, 检索增强生成 1. RAG 系统的核心挑战与优化方向 传统 RAG(Retrieval-Augmented Generation)系统常面临以下瓶颈: graph LR A[用户提问] --> B[检…...
OrangePi入门教程(待更新)
快速上手指南 https://www.hiascend.com/developer/techArticles/20240301-1?envFlag1 教学课程(含开发板配置和推理应用开发) https://www.hiascend.com/developer/devboard 开发推理应用 https://www.hiascend.com/developer/techArticles/20240326-1?envFlag1...
基于SpringBoot+Vue实现的二手交易市场平台功能一
一、前言介绍: 1.1 项目摘要 随着社会的发展和人们生活水平的提高,消费者购买能力的提升导致产生了大量的闲置物品,这些闲置物品具有一定的经济价值。特别是在高校环境中,学生群体作为一个具有一定消费水平的群体,每…...
TC3xx芯片的UCB介绍
文章目录 前言一、UCB的定义及其功能简介二、UCB_BMHDx_ORIG and UCB_BMHDx_COPY (x 0 - 3)2.1 BMHD(Boot Mode Head) 三、UCB_SSW四、UCB_PFLASH_ORIG and UCB_PFLASH_COPY4.1 Password4.2 UCB Confirmation 前言 缩写全称UCBUser Configuration BlockBMHDBoot Mode Headers…...
Airflow量化入门系列:第四章 A股数据处理与存储优化
Airflow量化入门系列:第四章 A股数据处理与存储优化 本教程系统性地讲解了 Apache Airflow 在 A 股量化交易中的应用,覆盖从基础安装到高级功能的完整知识体系。通过八章内容,读者将掌握 Airflow 的核心概念、任务调度、数据处理、技术指标计…...
《海空重力测量理论方法及应用》之一重力仪系统组成及工作原理(下)
2、三轴稳定平台型 稳定平台的作用是隔离测量载体角运动对重力观测量的影响,确保重力传感器的敏感轴方向始终与重向保持一致。 当前主流的海空重力仪使用的稳定平台方案主要有4种: ①双轴阻尼陀螺平台: ②)双轴惯导加捷联方位平台: ③三轴惯导平台; ④捷联惯导…...
C++模板递归结构详解和使用
示例代码 template<typename _SourceIterator, typename _DestT> struct convert_pointer {typedef typename convert_pointer<typename _SourceIterator::pointer, _DestT>::type type; };1. 模板参数 _SourceIterator 是输入的类型,通常表示迭代器类…...
(八)PMSM驱动控制学习---无感控制之滑膜观测器
在FOC矢量控制中,我们需要实时得到转子的转速和位置 ,但在考虑到成本和使用场合的情况下,往往使用无感控制,因为无位置传感器克服了传统机械式传感器的很多缺点和不足。比如,机械式传感器对环境要求比较严格࿰…...
蓝桥杯真题-分糖果-题解
链接:https://www.lanqiao.cn/problems/4124/learning/ 题目 复述:两种糖果,分别有9和16,分给7人,每个人得到的最少2,最多5,必需全部分完,几种分法? 复习-深度优先搜索 …...
推荐系统(二十二):基于MaskNet和WideDeep的商品推荐CTR模型实现
在上一篇文章《推荐系统(二十一):基于MaskNet的商品推荐CTR模型实现》中,笔者基于 MaskNet 构建了一个简单的模型。笔者所经历的工业级实践证明,将 MaskNet 和 Wide&Deep 结合应用,可以取得不错的效果&…...
辅助查询是根据查询到的文档片段再去生成新的查询问题
💡 辅助查询是怎么来的? 它是基于你当前查询(query)检索到的某个文档片段(chunk_result),再去“反推”出新的相关问题(utility queries),这些问题的作用是&a…...
Spring Cloud 框架为什么能处理高并发
Spring Cloud框架能够有效处理高并发场景,核心在于其微服务架构设计及多组件的协同作用,具体机制如下: 一、分布式架构设计支撑高扩展性 服务拆分与集群部署 Spring Cloud通过微服务拆分将单体系统解耦为独立子服务,每个服务可独…...
Pseduo LiDAR(CVPR2019)
文章目录 AbstractIntroductionRelated WorkLiDAR-based 3D object detectionStereo- and monocular-based depth estimationImage-based 3D object detection MethodDepth estimationPseudo-LiDAR generationLiDAR vs. pseudo-LiDAR3D object detectionData representation ma…...
强化学习课程:stanford_cs234 学习笔记(3)introduction to RL
文章目录 前言7 markov 实践7.1 markov 过程再叙7.2 markov 奖励过程 MRP(markov reward process)7.3 markov 价值函数与贝尔曼方程7.4 markov 决策过程MDP(markov decision process)的 状态价值函数7.4.1 状态价值函数7.4.2 状态…...
前端精度计算:Decimal.js 基本用法与详解
一、Decimal.js 简介 decimal.js 是一个用于任意精度算术运算的 JavaScript 库,它可以完美解决浮点数计算中的精度丢失问题。 官方API文档:Decimal.js 特性: 任意精度计算:支持大数、小数的高精度运算。 链式调用:…...
来聊聊C++中的vector
一.vector简介 vector是什么 C 中的 vector 是一种序列容器,它允许你在运行时动态地插入和删除元素。 vector 是基于数组的数据结构,但它可以自动管理内存,这意味着你不需要手动分配和释放内存。 与 C 数组相比,vector 具有更多的…...
对比学习中的NCE(Noise-Contrastive Estimation)和InfoNCE(SimCLR)损失函数+案例(附SimSiam分析)
在对比学习(Contrastive Learning)中,NCE(Noise-Contrastive Estimation)和InfoNCE是两种常见的目标函数,它们都用于通过区分正样本和负样本来学习高质量的表示。 1. NCE(Noise-Contrastive Est…...
基于FAN网络的图像识别系统设计与实现
基于FAN网络的图像识别系统设计与实现 一、系统概述 本系统旨在利用FAN(Fourier Analysis Networks)网络架构实现高效的图像识别功能,并通过Python语言设计一个直观的用户界面,方便用户操作与使用。FAN网络在处理周期性特征方面具有独特优势,有望提升图像识别在复杂场景…...
【瑞萨 RA-Eco-RA2E1-48PIN-V1.0 开发板测评】PWM
【瑞萨 RA-Eco-RA2E1-48PIN-V1.0 开发板测评】PWM 本文介绍了瑞萨 RA2E1 开发板使用内置时钟和定时器实现 PWM 输出以及呼吸灯的项目设计。 项目介绍 介绍了 PWM 和 RA2E1 的 PWM 资源。 PWM 脉冲宽度调制(Pulse Width Modulation, PWM)是一种对模拟…...
NDK开发:开发环境
NDK开发环境 一、NDK简介 1.1 什么是NDK NDK(Native Development Kit)是Android提供的一套工具集,允许开发者在Android应用中使用C/C++代码。它包含了: 交叉编译器构建工具调试器系统头文件和库示例代码和文档1.2 NDK的优势 性能优化:直接使用底层代码,提高性能代码保…...
设计模式简述(三)工厂模式
工厂模式 描述简单工厂(静态工厂)工厂方法模式 抽象工厂增加工厂管理类使用 描述 工厂模式用以封装复杂的实例初始化过程,供外部统一调用 简单工厂(静态工厂) 如果对象创建逻辑简单且一致,可以使用简单工…...
通过Postman和OAuth 2.0连接Dynamics 365 Online的详细步骤
🌟 引言 在企业应用开发中,Dynamics 365 Online作为微软的核心CRM平台,提供了强大的Web API接口。本文将教你如何通过Postman和OAuth 2.0认证实现与Dynamics 365的安全连接,轻松调用数据接口。 📝 准备工作 工具安装…...
LlamaIndex实现RAG增强:上下文增强检索/重排序
面向文档检索的上下文增强技术 文章目录 面向文档检索的上下文增强技术概述技术背景核心组件方法详解文档预处理向量存储创建上下文增强检索检索对比技术优势结论导入库和环境变量读取文档创建向量存储和检索器数据摄取管道使用句子分割器的摄取管道使用句子窗口的摄取管道查询…...
AI比人脑更强,因为被植入思维模型【43】蝴蝶效应思维模型
giszz的理解:蝴蝶效应我们都熟知,就是说一个微小的变化,能带动整个系统甚至系统的空间和时间的远端,产生巨大的链式反应。我学习后的启迪,简单的说,就是不要忽视任何微小的问题,更多时候&#x…...
程序化广告行业(62/89):DSP系统的媒体与PDB投放设置探秘
程序化广告行业(62/89):DSP系统的媒体与PDB投放设置探秘 大家好!在之前的学习中,我们对程序化广告的DSP系统有了一定了解。今天还是带着和大家共同进步的想法,深入探索DSP系统中媒体设置以及PDB投放设置的…...
Java项目之基于ssm的怀旧唱片售卖系统(源码+文档)
项目简介 怀旧唱片售卖系统实现了以下功能: 用户信息管理: 用户信息新增:添加新用户的信息。 用户信息修改:对现有用户信息进行修改。 商品信息管理: 商品信息添加:增加新的商品(唱片&#x…...
程序化广告行业(61/89):DSP系统活动设置深度剖析
程序化广告行业(61/89):DSP系统活动设置深度剖析 大家好!在程序化广告的学习道路上,我们已经探索了不少重要内容。今天依旧本着和大家一起学习进步的想法,深入解析DSP系统中活动设置的相关知识。这部分内容…...
Altshuller矛盾矩阵查询:基于python和streamlit
基于python和streamlit实现的Altshuller矛盾矩阵查询 import streamlit as st import json# 加载数据 st.cache_resource def load_data():with open(parameter.json, encodingutf-8) as f:parameters json.load(f)with open(way.json, encodingutf-8) as f:contradictions …...
FreeRTOS的空闲任务
在 FreeRTOS 中,空闲任务(Idle Task) 是操作系统自动创建的一个特殊任务,其作用和管理方式如下: 1. 空闲任务创建 FreeRTOS 内核自动创建:当调用 vTaskStartScheduler() 启动调度器时,内核会自…...
【代码模板】如何用FILE操作符打开文件?fopen、fclose
#include "stdio.h" #include "unistd.h"int main(int argc, char *argv[]) {FILE *fp fopen("1.log", "wb");if (!fp) {perror("Failed open 1.log");return -1;}fclose(fp); }关于权限部分参考兄弟篇【代码模板】C语言中…...
[特殊字符] Pandas 常用操作对比:Python 运算符 vs Pandas 函数
在 Pandas 中,许多操作可以直接使用 Python 的比较运算符(如 、!、>、< 等),而不需要调用 Pandas 的专门函数(如 eq()、ne()、gt() 等)。这些运算符在 Pandas 中已经被重载,代码更简洁。以…...
I.MX6ULL开发板与linux互传文件的方法--NFS,SCP,mount
1、内存卡或者U盘 方法比较简单,首先在linux系统中找到u盘对应的文件夹,随后使用cp指令将文件拷贝进u盘。 随后将u盘插入开发板中,找到u盘对应的设备文件。一般u盘对应的设备文件在/dev下,以sda开头,可以使用命令列出所…...
图解AUTOSAR_SWS_FlashEEPROMEmulation
AUTOSAR Flash EEPROM Emulation (FEE) 详解 基于AUTOSAR规范的Flash EEPROM Emulation模块分析 目录 1. 概述2. 架构设计 2.1 模块位置与接口2.2 内部状态管理2.3 配置结构3. API接口 3.1 接口功能分类3.2 错误管理4. 操作流程 4.1 写入操作序列5. 总结1. 概述 Flash EEPROM …...
Unity:Simple Follow Camera(简单相机跟随)
为什么需要Simple Follow Camera? 在游戏开发中,相机(Camera)是玩家的“眼睛”。它的作用是决定玩家看到游戏世界的哪一部分。很多游戏需要相机自动跟随玩家角色,让玩家始终可以看到角色及其周围的环境,而…...
[项目总结] 在线OJ刷题系统项目总结与分析(二): 技术应用(上)
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…...
针对Ansible执行脚本时报错“可执行文件格式错误”,以下是详细的解决步骤和示例
针对Ansible执行脚本时报错“可执行文件格式错误”,以下是详细的解决步骤和示例: 目录 一、错误原因分析二、解决方案1. 检查并添加可执行权限2. 修复Shebang行3. 转换文件格式(Windows → Unix)4. 检查脚本内容兼容性5. 显式指定…...
从 Dense LLM 到 MoE LLM:以 DeepSeek MoE 为例讲解 MoE 的基本原理
写在前面 大多数 LLM 均采用 Dense(密集) 架构。这意味着,在处理每一个输入 Token 时,模型所有的参数都会被激活和计算。想象一下,为了回答一个简单的问题,你需要阅读整部大英百科全书的每一个字——这显然效率低下。 为了突破 Dense 模型的瓶颈,一种名为 Mixture of …...
未来已来:探索AI驱动的HMI设计新方向
在科技浪潮的持续冲击下,人工智能(AI)正以势不可挡的姿态重塑各个领域的格局,其中人机交互(HMI,Human - Machine Interaction)设计领域深受其影响,正经历着深刻的变革。AI 技术的融入…...