马鞍山建设集团有限公司网站,百度云手机app下载,网站信息优化的方式,ui设计素材库高并发下的接口请求重复提交问题 在.Net开发中#xff0c;我们经常遇到用户疯狂点击同一按钮#xff0c;或者服务响应慢时重复发送请求#xff0c;导致数据重复添加或混乱。这不仅浪费资源#xff0c;更会得到错误的业务结果。如何高效解决这一普遍问题呢#xff1f; 常规… 高并发下的接口请求重复提交问题 在.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/?viewaspnetcore-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.GetIEndpointFeature()?.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.GetMetadataApiLockAttribute(); if (apiLock null) { //没有特性直接走 await base.Invoke(context); return; } else { //这里才是我们要写 核心逻辑。我们需要获取token//然后拼接token和url进行锁定 using (var scope _scopeFactory.CreateScope()){ var redisLock scope.ServiceProvider.GetRequiredServiceIRedisLock(); var expiry TimeSpan.FromSeconds(apiLock .MaxLockTime);//超时时间如果内部执行超过expity则会释放锁 var wait TimeSpan.FromSeconds(3);//获取锁的时候等待的时间 var retry TimeSpan.FromSeconds(1);//每隔多少时间请求一次 string key $ApiLock:{用户/客户端Token}:{url};//锁的key 用户唯一IDAPI路由作为锁条件同一个接口没执行完前不允许执行下一次 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 namebuilder/param /// returns/returns public static IApplicationBuilder UseApiLock(this IApplicationBuilder builder) { if (builder null) { throw new ArgumentNullException(nameof(builder)); } return builder.UseMiddlewareApiLockMiddleware(); } } 然后我们需要再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 TaskIActionResult TestApiLock() { await Task.Delay(20000); return Ok(); }} 这里也非常简单我们直接再需要使用锁定的接口上添加ApiLock的特性就可以啦我再这里对中间件提供了2个参数分别是锁定的最大时间和锁定后的错误提示。这个大家也可以按照自身业务需求来进行扩展。 然后我们测试一下这个接口这个接口里面做了20秒的延迟 我们可以看到当我们连续点击2次测试接口时我们发现第二次调用就会返回被锁定了。 简洁之美效率之王 这不仅是一种技术优化更是一种产品哲学的体现。在追求高效的同时我们更希望能让开发者从重复的工作中解放出来将更多的精力投入到创新和业务的核心中去。 即刻行动起来用最简洁的代码解决.Net API的高并发头疼问题吧