当前位置: 首页 > news >正文

揭秘:一行代码搞定.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开发中&#xff0c;我们经常遇到用户疯狂点击同一按钮&#xff0c;或者服务响应慢时重复发送请求&#xff0c;导致数据重复添加或混乱。这不仅浪费资源&#xff0c;更会得到错误的业务结果。如何高效解决这一普遍问题呢&#xff1f; 常规…...

SpringBoot的 8 个优点

目录 1、简化配置 2、快速开发 3、微服务支持 4、内嵌服务器 5、健康监测 6、热部署 7、自动化管理 8、社区支持和生态系统 SpringBoot 是一个基于 Spring 框架的快速开发框架&#xff0c;它通过提供一系列的自动配置、约定优于配置、快速集成等功能&#xff0c;简化了…...

Spark中多分区写文件前可以不排序么

背景 Spark 3.5.0 目前 Spark中的实现中&#xff0c;对于多分区的写入默认会先排序&#xff0c;这是没必要的。可以设置spark.sql.maxConcurrentOutputFileWriters 为大于0来避免排序。 分析 这部分主要分为三个部分: 一个是V1Writes规则的重改; 另一个是FileFormatWriter中…...

突破编程_C++_面试(变量与常量)

面试题 1 &#xff1a; C 中的变量存储类别有哪些&#xff0c;并简要描述它们的特点&#xff1f; 在C中&#xff0c;变量的存储类别决定了变量的生命周期和可见性。以下是C中的几种变量存储类别及其特点&#xff1a; 自动存储期 也称为局部存储类别。这类变量在函数或代码块…...

k8s的一些关键信息(归类摘抄,非提炼)

零&#xff1a;举例说明 当用户提交一个 Deployment 对象到 Kubernetes 集群时&#xff0c;控制平面的 API Server 接收到该请求&#xff0c;并将其转发给 Controller Manager。Controller Manager 中的 Deployment Controller 监听到该请求&#xff0c;并根据用户定义的配置信…...

海外媒体发稿:8个提升影响力的日韩地区媒体发稿推广策略-华媒舍

在今天的数字化时代&#xff0c;媒体发稿推广成为企业和个人增加影响力的重要方式。特别是在日韩地区&#xff0c;这个拥有庞大媒体市场和活跃社交媒体用户的地区&#xff0c;正确的推广策略将对影响力的提升起到关键作用。我们将介绍8个提升影响力的日韩地区媒体发稿推广策略。…...

面试官:能不能给 Promise 增加取消功能和进度通知功能... 我:???

扯皮 这段时间闲着没事就去翻翻红宝书&#xff0c;已经看到 Promise 篇了&#xff0c;今天又让我翻到两个陌生的知识点。 因为 Promise 业务场景太多了自我感觉掌握的也比较透彻&#xff0c;之前也跟着 Promise A 的规范手写过完整的 Promise&#xff0c;所以这部分内容基本上…...

详解MySQL增删查改

众所周知&#xff0c;MySQL是非常重要的数据库语言&#xff0c;下面我们来回顾一下mysql的增删查改吧 MySQL创建数据库&#xff1a; CREATE DATABASE 数据库名;MySQL删除数据库&#xff1a; DROP DATABASE <database_name>; --直接删除&#xff0c;不检查是否存在 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/产生二进制日志文件&#xff0…...

Java:性能优化细节01-10

Java&#xff1a;性能优化细节01-10 在Java程序开发过程中&#xff0c;性能优化是一个重要的考虑因素。常见的误解是将性能问题归咎于Java语言本身&#xff0c;然而实际上&#xff0c;性能瓶颈更多地源于程序设计和代码实现方式的不当。因此&#xff0c;培养良好的编码习惯不仅…...

CVE-2022-24652 漏洞复现

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

LeetCode、338. 比特位计数【简单,位运算】

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

借助Aspose.BarCode条码控件,C# 中的文本转 QR 码生成器

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

vue打包优化,webpack的8大配置方案

vue-cli 生成的项目通常集成Webpack &#xff0c;在打包的时候&#xff0c;需要webpack来做一些事情。这里我们希望它可以压缩代码体积&#xff0c;提高运行效率。 文章目录 &#xff08;1&#xff09;代码压缩&#xff1a;&#xff08;2&#xff09;图片压缩&#xff1a;&…...

B端系统从0到1:有几步,其中需求分析要做啥?

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

django中查询优化

在Django中&#xff0c;查询优化是一个重要的主题&#xff0c;因为不正确的查询可能会导致性能问题&#xff0c;尤其是在处理大量数据时。以下是一些在Django中进行查询优化的建议&#xff1a; 一&#xff1a;使用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 原始的单词编码方式&#xff1a; one-hot&#xff0c;维度太大&#xff0c;不同单词之间相互独立&#xff0c;没有远近关系区分。 wordclass&#xff0c;将同一类单词编码在一起&#xff0c;此时丢失了类别和类别间的相关信息&#xff0c;比如class1和class3…...

Mysql 日志

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

【开源】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.…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...