揭秘:一行代码搞定.Net API高并发的烦恼
高并发下的接口请求重复提交问题 在.Net开发中,我们经常遇到用户疯狂点击同一按钮,或者服务响应慢时重复发送请求,导致数据重复添加或混乱。这不仅浪费资源,更会得到错误的业务结果。如何高效解决这一普遍问题呢?
常规方案使用分布式锁 面对这问题,分布式锁是一种有效的传统解决方案,可以确保同一时间只有一个请求被处理。但面对众多需要锁定的接口,配置分布式锁无疑是一项繁重的工作。如何优化这一流程?
今天,我带来了一种简洁高效的方案。透过.Net中间件的强大功能,我们可以用一行代码轻松实现防并发。首先,我们定义一个特性ApiLock
,并在中间件中实现基于用户或Token的Redis锁定。如此设计,简单实用又易于扩展。
首先,我们需要创建一个ApiLock得特性,用于判断哪些接口需要执行分布式锁
public class ApiLockAttribute : ValidationAttribute
{
public ApiLockAttribute(int maxLockTime = 10, string msg = "正在处理,请稍等,请勿重复提交")
{
MaxLockTime = maxLockTime;
Msg = msg;
}
public int MaxLockTime { get; set; }
public string Msg { get; set; }
}
然后我们需要写一个中间件,如果不了解中间件的小伙伴可以查看下面文章进行学习:
https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-8.0
我们需要创建一个中间件:
public class ApiLockMiddleware : MiddlewareBase
{
public override async Task Invoke(HttpContext context)
{
}
}
然后我们需要再这个中间件里写一写逻辑,我需要通过HttpContext 获取到Token(用户或者客户端),来进行唯一标识的判定。
public class ApiLockMiddleware : MiddlewareBase
{
public override async Task Invoke(HttpContext context)
{
//获取请求路由
string url= context.Request.Path.Value.ToLower();
}
}
然后我们需要编写一个获取Endpoint的方法:
private static Endpoint GetEndpoint(HttpContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
return context.Features.Get<IEndpointFeature>()?.Endpoint;
}
这个方法用于获取请求的EndPoint来判断是否包含ApiLock的特性
public class ApiLockMiddleware : MiddlewareBase
{
public override async Task Invoke(HttpContext context)
{
//获取请求路由
string url= context.Request.Path.Value.ToLower();
var endpoint = GetEndpoint(context);
if (endpoint != null)
{
var apiLock = endpoint.Metadata.GetMetadata<ApiLockAttribute>();
if (apiLock == null)
{
//没有特性直接走
await base.Invoke(context);
return;
}
else
{
//这里才是我们要写 核心逻辑。我们需要获取token,//然后拼接token和url进行锁定
using (var scope = _scopeFactory.CreateScope()){
var redisLock = scope.ServiceProvider.GetRequiredService<IRedisLock>();
var expiry = TimeSpan.FromSeconds(apiLock .MaxLockTime);//超时时间,如果内部执行超过expity则会释放锁
var wait = TimeSpan.FromSeconds(3);//获取锁的时候等待的时间
var retry = TimeSpan.FromSeconds(1);//每隔多少时间请求一次
string key = $"ApiLock:{用户/客户端Token}:{url}";//锁的key 用户唯一ID+API路由作为锁条件,同一个接口没执行完前不允许执行下一次
using (var redLock = await redisLock.CreateLockAsync(key, expiry, wait, retry))
{
if (!redLock.IsAcquired)
{
//如果被锁定,则返回特性传入的失败消息
await HandleExceptionAsync(context, new Exception(apiLock.Msg), (int)HttpStatusCode.OK);
return;
}
else
{
//没有锁定才继续往后走Controller等业务逻辑
await base.Invoke(context);
return;
}
}
}
}
}
}
}
这里我们的中间件就写完了。我们需要写一个注册的方法:
public static class ApiLockExtensions
{
/// <summary>
/// 防止重复提交中间件
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static IApplicationBuilder UseApiLock(this IApplicationBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
return builder.UseMiddleware<ApiLockMiddleware>();
}
}
然后,我们需要再Configure里进行注册:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
//……
app.UseApiLock();
}
到这里我们的封装就已经完成了,那么我们改如何使用它呢
[ApiController]
[Route("api/[controller]/[action]")]
public class TestController : ControllerBase
{
[HttpPost]
[ApiLock(10,"接口被锁定,请稍后再试")]
public async Task<IActionResult> TestApiLock()
{
await Task.Delay(20000);
return Ok();
}
}
这里也非常简单,我们直接再需要使用锁定的接口上添加ApiLock的特性就可以啦,我再这里对中间件提供了2个参数,分别是锁定的最大时间和锁定后的错误提示。这个大家也可以按照自身业务需求来进行扩展。
然后我们测试一下这个接口,这个接口里面做了20秒的延迟
我们可以看到,当我们连续点击2次测试接口时,我们发现第二次调用就会返回被锁定了。
简洁之美,效率之王 这不仅是一种技术优化,更是一种产品哲学的体现。在追求高效的同时,我们更希望能让开发者从重复的工作中解放出来,将更多的精力投入到创新和业务的核心中去。
即刻行动起来,用最简洁的代码,解决.Net API的高并发头疼问题吧!
相关文章:

揭秘:一行代码搞定.Net API高并发的烦恼
高并发下的接口请求重复提交问题 在.Net开发中,我们经常遇到用户疯狂点击同一按钮,或者服务响应慢时重复发送请求,导致数据重复添加或混乱。这不仅浪费资源,更会得到错误的业务结果。如何高效解决这一普遍问题呢? 常规…...
SpringBoot的 8 个优点
目录 1、简化配置 2、快速开发 3、微服务支持 4、内嵌服务器 5、健康监测 6、热部署 7、自动化管理 8、社区支持和生态系统 SpringBoot 是一个基于 Spring 框架的快速开发框架,它通过提供一系列的自动配置、约定优于配置、快速集成等功能,简化了…...
Spark中多分区写文件前可以不排序么
背景 Spark 3.5.0 目前 Spark中的实现中,对于多分区的写入默认会先排序,这是没必要的。可以设置spark.sql.maxConcurrentOutputFileWriters 为大于0来避免排序。 分析 这部分主要分为三个部分: 一个是V1Writes规则的重改; 另一个是FileFormatWriter中…...
突破编程_C++_面试(变量与常量)
面试题 1 : C 中的变量存储类别有哪些,并简要描述它们的特点? 在C中,变量的存储类别决定了变量的生命周期和可见性。以下是C中的几种变量存储类别及其特点: 自动存储期 也称为局部存储类别。这类变量在函数或代码块…...
k8s的一些关键信息(归类摘抄,非提炼)
零:举例说明 当用户提交一个 Deployment 对象到 Kubernetes 集群时,控制平面的 API Server 接收到该请求,并将其转发给 Controller Manager。Controller Manager 中的 Deployment Controller 监听到该请求,并根据用户定义的配置信…...

海外媒体发稿:8个提升影响力的日韩地区媒体发稿推广策略-华媒舍
在今天的数字化时代,媒体发稿推广成为企业和个人增加影响力的重要方式。特别是在日韩地区,这个拥有庞大媒体市场和活跃社交媒体用户的地区,正确的推广策略将对影响力的提升起到关键作用。我们将介绍8个提升影响力的日韩地区媒体发稿推广策略。…...
面试官:能不能给 Promise 增加取消功能和进度通知功能... 我:???
扯皮 这段时间闲着没事就去翻翻红宝书,已经看到 Promise 篇了,今天又让我翻到两个陌生的知识点。 因为 Promise 业务场景太多了自我感觉掌握的也比较透彻,之前也跟着 Promise A 的规范手写过完整的 Promise,所以这部分内容基本上…...
详解MySQL增删查改
众所周知,MySQL是非常重要的数据库语言,下面我们来回顾一下mysql的增删查改吧 MySQL创建数据库: CREATE DATABASE 数据库名;MySQL删除数据库: DROP DATABASE <database_name>; --直接删除,不检查是否存在 DROP…...

Mysql开启bin-log日志
目录 一、安装配置 二、mysqlbinlog命令 一、安装配置 yum -y install mariadb mariadb-server#安装mysql数据库#默认配置文件/etc/my.cnfvim /etc/my.cnflog-binmariadb-bin #开启二进制日志 systemctl restart mariadb#会在/car/lib/mysql/产生二进制日志文件࿰…...
Java:性能优化细节01-10
Java:性能优化细节01-10 在Java程序开发过程中,性能优化是一个重要的考虑因素。常见的误解是将性能问题归咎于Java语言本身,然而实际上,性能瓶颈更多地源于程序设计和代码实现方式的不当。因此,培养良好的编码习惯不仅…...

CVE-2022-24652 漏洞复现
CVE-2022-24652 开题 后台管理是thinkphp的,但是工具没检测出漏洞。 登陆后界面如下,上传头像功能值得引起注意 这其实就是CVE-2022-24652,危险类型文件的不加限制上传,是文件上传漏洞。漏洞路由/user/upload/upload 参考文章&a…...

LeetCode、338. 比特位计数【简单,位运算】
文章目录 前言LeetCode、338. 比特位计数【中等,位运算】题目链接与分类思路位运算移位处理前缀思想实现 资料获取 前言 博主介绍:✌目前全网粉丝2W,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java…...

借助Aspose.BarCode条码控件,C# 中的文本转 QR 码生成器
二维码用于在较小的空间内存储大量数据。它们易于使用,可以通过智能手机或其他设备扫描来打开网站、观看视频或访问其他编码信息。在这篇博文中,我们将学习如何使用 C# 以编程方式生成基于文本的 QR 码。我们将提供分步指南和代码片段,帮助您…...

vue打包优化,webpack的8大配置方案
vue-cli 生成的项目通常集成Webpack ,在打包的时候,需要webpack来做一些事情。这里我们希望它可以压缩代码体积,提高运行效率。 文章目录 (1)代码压缩:(2)图片压缩:&…...

B端系统从0到1:有几步,其中需求分析要做啥?
一款B系统从无到有都经历了啥,而其中的需求分析又要做什么?贝格前端工场给老铁们做一下分析,文章写作不易,如果咱们有界面设计和前端开发需求,别忘了私信我呦,开始了。 一、B端系统从0到1都有哪些要走的步骤…...

django中查询优化
在Django中,查询优化是一个重要的主题,因为不正确的查询可能会导致性能问题,尤其是在处理大量数据时。以下是一些在Django中进行查询优化的建议: 一:使用select_related和prefetch_related: select_related用于优化一…...

【JavaScript】输入输出语法
目录 一、输出语法 二、输入语法 一、输出语法 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>D…...

多模态基础--- word Embedding
1 word Embedding 原始的单词编码方式: one-hot,维度太大,不同单词之间相互独立,没有远近关系区分。 wordclass,将同一类单词编码在一起,此时丢失了类别和类别间的相关信息,比如class1和class3…...

Mysql 日志
0 引言 MySQL日志主要分为4类,使用这些日志文件,可以查看MySQL内部发生的事情。这4类日志分别是: ● 错误日志:记录MySQL服务的启动、运行或停止MySQL服务时出现的问题。 ● 查询日志:记录建立的客户端连接和执行的…...

【开源】SpringBoot框架开发服装店库存管理系统
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 角色管理模块2.3 服装档案模块2.4 服装入库模块2.5 服装出库模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 角色表3.2.2 服装档案表3.2.3 服装入库表3.2.4 服装出库表 四、系统展示五、核心代码5.…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...

R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...