热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何解决api接口的并发问题?

上周公司里发生了一件怪事,就是我们自己系统的注册接口被人恶意频繁访问最后导致该服务不可用,该注册接口是输入电话号码然后获取验证码注册,有人

上周公司里发生了一件怪事,就是我们自己系统的注册接口被人恶意频繁访问最后导致该服务不可用,该注册接口是输入电话号码然后获取验证码注册,有人用遍历的方法无限重试验证码,最终服务没抗住挂掉了。更怪的是查到这个人的ip竟然是自己内部的公网ip,大概是有人闲的无聊了在搞怪,没办法,又不能封了ip,那样大家都访问不了了。

 

So,今天有空研究了一下关于如何解决api接口高并发的问题,在此记录一下。

 

1、通过控制并发数量来实现

信号量:这应该是大学操作系统课本里的概念,它是用在多进程和多任务之间的同步的,它就像十字路口的红绿灯,保证这个路口四条路的畅通行驶。

java中也有这个概念,Java并发库的Semaphore可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

写一个测试接口如下:

private final Semaphore permit = new Semaphore(10, true);
@PostMapping("/test")public String test(){try {permit.acquire();log.info("处理请求===============>");Thread.sleep(2000);}catch (Exception e){log.error("error");}finally {permit.release();}return "success";}

以上是我写了一个测试接口,这个接口最多允许同时10个并发量,超过10个则等待。

接下来我们用压力测试工具测一下这个接口,这个是我用的测试工具:

http://coolaf.com//tool/post 这是在线的,不能修改并发数,里面有本地版本,下载解压就能用。

下载解压后,在工具上输入url和并发量

这里设置并发量为50,也就是同时50个客户端访问该接口,点击开始测试,查看日志

我们可以看到,每次只有10个请求进入接口,然后过2秒后再来10个请求,效果很明显,我们想增大访问量只需要修改初始的信号量的数量即可。

 

2、通过控制访问速率来实现

这种方式采用令牌桶算法来实现,我们以一个恒定的速率向一个桶内放令牌,每次请求来的时候去桶里拿令牌,如果拿到了就继续后面的操作,如果没有拿到则等待。

在我们的工程实践中,通常使用Google开源工具包Guava提供的限流工具类RateLimiter来实现控制速率,该类基于令牌桶算法来完成限流,非常易于使用,而且非常高效。如我们不希望每秒的任务提交超过1个。

public static void main(String[] args) {String start &#61; new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());RateLimiter limiter &#61; RateLimiter.create(1.0); // 这里的1表示每秒允许处理的量为1个for (int i &#61; 1; i <&#61; 10; i&#43;&#43;) {double waitTime &#61; limiter.acquire(i);// 请求RateLimiter, 超过permits会被阻塞System.out.println("cutTime&#61;" &#43; System.currentTimeMillis() &#43; " call execute:" &#43; i &#43; " waitTime:" &#43; waitTime);}String end &#61; new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());System.out.println("start time:" &#43; start);System.out.println("end time:" &#43; end);}

RateLimiter limiter &#61; RateLimiter.create(1.0) 创建一个限流器&#xff0c;每秒生成1个令牌&#xff1b;

limiter.acquire(i) 以阻塞的方式获取令牌&#xff0c;随着i的增加&#xff0c;需要的令牌数增多&#xff0c;则需要等待的时间也增加。

查看输出结果&#xff1a;

可以看出&#xff0c;当 i&#61;6时&#xff0c;等待时间差不多为i-1&#61;5秒&#xff0c;在这里是因为RateLimiter支持预消费&#xff0c;来支持一定程度的突发情况。

开可以用tryAcquire(int permits, long timeout, TimeUnit unit)来设置等待超时时间的方式获取令牌&#xff0c;当等待超过了超时时长则立马返回。

 

令牌的生成策略有两种&#xff0c;一种是稳定模式&#xff08;SmoothBursty 模式&#xff09;&#xff0c;一种为渐进模式&#xff08;SmoothWarmingUp模式&#xff09;。

SmoothBursty 模式&#xff1a;RateLimiter limiter &#61; RateLimiter.create(5);

RateLimiter.create(5)表示桶容量为5且每秒新增5个令牌&#xff0c;即每隔200毫秒新增一个令牌&#xff1b;limiter.acquire()表示消费一个令牌&#xff0c;如果当前桶中有足够令牌则成功&#xff08;返回值为0&#xff09;&#xff0c;如果桶中没有令牌则暂停一段时间&#xff0c;比如发令牌间隔是200毫秒&#xff0c;则等待200毫秒后再去消费令牌&#xff0c;这种实现将突发请求速率平均为了固定请求速率。

SmoothWarmingUp模式&#xff1a;RateLimiter limiter &#61; RateLimiter.create(5,1000, TimeUnit.MILLISECONDS);

创建方式&#xff1a;RateLimiter.create(doublepermitsPerSecond, long warmupPeriod, TimeUnit unit)&#xff0c;permitsPerSecond表示每秒新增的令牌数&#xff0c;warmupPeriod表示在从冷启动速率过渡到平均速率的时间间隔。速率是梯形上升速率的&#xff0c;也就是说冷启动时会以一个比较大的速率慢慢到平均速率&#xff1b;然后趋于平均速率&#xff08;梯形下降到平均速率&#xff09;。可以通过调节warmupPeriod参数实现一开始就是平滑固定速率。

写一个测试接口测试

private final RateLimiter limiter &#61; RateLimiter.create(5.0);&#64;PostMapping("/test2")public String test2(){try {boolean flag &#61; limiter.tryAcquire(1,3, TimeUnit.SECONDS);if (flag){log.info("处理请求&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;>");Thread.sleep(1000);}else {return "系统繁忙&#xff01;";}}catch (Exception e){log.error("error");}return "success";}

每秒产生5个令牌&#xff0c;请求进来每次获取一个&#xff0c;然后超时时长是3秒&#xff0c;还是设置50的并发量&#xff1a;

查看输出结果&#xff1a;

可以看到&#xff0c;一开始预消费进来了8个请求&#xff0c;随后的时间里每次进来5个请求&#xff0c;在这之间有超时3秒没有获取到令牌的都返回超时了。返回结果如下&#xff1a;

 

至此&#xff0c;我所了解的解决接口并发调用的方法就是这些&#xff0c;欢迎大佬指出错误和不足。


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Android实战——jsoup实现网络爬虫,糗事百科项目的起步
    本文介绍了Android实战中使用jsoup实现网络爬虫的方法,以糗事百科项目为例。对于初学者来说,数据源的缺乏是做项目的最大烦恼之一。本文讲述了如何使用网络爬虫获取数据,并以糗事百科作为练手项目。同时,提到了使用jsoup需要结合前端基础知识,以及如果学过JS的话可以更轻松地使用该框架。 ... [详细]
  • 本文是一篇翻译文章,介绍了async/await的用法和特点。async关键字被放置在函数前面,意味着该函数总是返回一个promise。文章还提到了可以显式返回一个promise的方法。该特性使得async/await更易于理解和使用。本文还提到了一些可能的错误,并希望读者能够指正。 ... [详细]
  • 【爬虫】关于企业信用信息公示系统加速乐最新反爬虫机制
    ( ̄▽ ̄)~又得半夜修仙了,作为一个爬虫小白,花了3天时间写好的程序,才跑了一个月目标网站就更新了,是有点悲催,还是要只有一天的时间重构。升级后网站的层次结构并没有太多变化,表面上 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Oracle Database 10g许可授予信息及高级功能详解
    本文介绍了Oracle Database 10g许可授予信息及其中的高级功能,包括数据库优化数据包、SQL访问指导、SQL优化指导、SQL优化集和重组对象。同时提供了详细说明,指导用户在Oracle Database 10g中如何使用这些功能。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
author-avatar
石某俊
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有