首页 > 其他分享 >揭秘:一行代码搞定.Net API高并发的烦恼!

揭秘:一行代码搞定.Net API高并发的烦恼!

时间:2024-09-19 12:48:26浏览次数:14  
标签:搞定 中间件 接口 API context var Net 锁定 public

        高并发下的接口请求重复提交问题 在.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的高并发头疼问题吧!

标签:搞定,中间件,接口,API,context,var,Net,锁定,public
From: https://blog.csdn.net/u012094427/article/details/142357379

相关文章

  • Kubernetes(K8S)实战:构建高可用的微服务架构
    Kubernetes(K8S)实战:构建高可用的微服务架构在云计算与容器化技术日新月异的今天,Kubernetes(简称K8S)凭借其强大的容器编排能力,已成为企业构建云原生应用的首选平台。本文将深入探讨Kubernetes的核心概念、架构设计原则,并通过一个实际案例——构建一个高可用的微服务架构,展示如......
  • Kimi进阶,价值过万的知识图谱,kimi搞定
    大家好,我是Shelly,一个专注于输出AI工具和科技前沿内容的AI应用教练,体验过300+款以上的AI应用工具。关注科技及大模型领域对社会的影响10年+。关注我一起驾驭AI工具,拥抱AI时代的到来。知识图谱是什么知识图谱在过去几年当中,都是学术研究和教学领域非常重要的一个研究方法和表......
  • 获得shopee商品详情API接口实现精细化运营
    Shopee是一个在线购物平台,隶属于东南亚地区的一家电商公司。该平台在东南亚多个国家拥有广泛的用户基础,包括新加坡、马来西亚、泰国、印度尼西亚、菲律宾、越南和台湾等。Shopee致力于打造一个多类别的产品市场,涵盖时尚、家居、美妆、电子产品、家电、玩具等多个领域,满足消费者的多......
  • ES 常用API:创建索引,创建别名,重建索引
    华为云不同版本间索引同步数据(目前华为后台工具不能跨版本间同步数据):ES常用API:创建索引,创建别名,重建索引华为云不同版本间索引同步数据(目前华为后台工具不能跨版本间同步数据):elasticdump--inputhttp://admin:123456@ip:9200/sensitive_1_v15--outputhttp://admin:123123@......
  • 如何使用自动化测试工具来提高API测试的效率?
    在软件开发生命周期中,API测试是确保应用程序质量和性能的关键环节。随着API的复杂性增加,手动测试已无法满足快速迭代和高质量交付的需求。自动化测试工具的出现,为API测试带来了革命性的改变。本文将探讨如何利用自动化测试工具提高API测试的效率。自动化测试工具的优势提高测试速度......
  • 修复控制台出现Deprecation Warning: The legacy JS API is deprecated and will be r
    背景项目使用到Vite+Sass,然后突然某天启动项目,控制台出现了这一行报错,找了一遍没找到解决方案。最后去StackOverflow找了一下,解决了。修复方式在vite.config.js中添加这一配置即可。import{defineConfig}from"vite";exportdefaultdefineConfig({//...css:......
  • DevExpress中文教程:如何将WinForms数据网格连接到ASP. NET Core WebAPI服务?
    日前DevExpress官方发布了DevExpressWinForms的后续版本——将.NET桌面客户端连接到安全后端WebAPI服务(EFCorewithOData),在本文中我们将进一步演示如何使用一个更简单的服务来设置DevExpressWinForms数据网格。P.S:DevExpressWinForms拥有180+组件和UI库,能为WindowsForms......
  • 如何测试和验证API的性能和稳定性?
    在开发过程中,测试和验证API的性能和稳定性是确保软件质量的关键步骤。以下是一些有效的方法和最佳实践:功能测试:首先,确保API的所有功能按预期工作。这包括对请求参数、方法、路径和预期响应的理解,以及对正常流程和异常流程的测试。性能测试:使用工具如JMeter、Apifox或Postman进......
  • 大数据新视界 --大数据大厂之Kubernetes与大数据:容器化部署的最佳实践
           ......
  • 论文阅读:Generative Adversarial Nets
    Abstract本文贡献:提出GAN:生成模型 G,生成模型用来捕获数据的分布;辨别模型D,辨别模型用来判断样本是来自于训练数据还是生成模型生成的。在任意函数空间里,存在唯一解,G能找出训练数据的真实分布,而D的预测概率为......