本文已录入至我的Github库房DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star
前言
前两天面试的时分,面试官问我:一个ip发恳求过来,是一个ip对应一个线程吗?我突然愣住了,对于SpringBoot怎么处理恳求好像从来没细心思考过,所以面试完毕后就细心研究了一番,现在就来讨论一下这个问题。
正文
咱们都知道,SpringBoot默许的内嵌容器是Tomcat,也便是咱们的程序实际上是运行在Tomcat里的。所以与其说SpringBoot能够处理多少恳求,到不如说Tomcat能够处理多少恳求。
关于Tomcat的默许装备,都在spring-configuration-metadata.json
文件中,对应的装备类则是org.springframework.boot.autoconfigure.web.ServerProperties
。
和处理恳求数量相关的参数有四个:
- server.tomcat.threads.min-spare:最少的作业线程数,默许巨细是10。该参数相当于长时间工,假如并发恳求的数量达不到10,就会依次运用这几个线程去处理恳求。
- server.tomcat.threads.max:最多的作业线程数,默许巨细是200。该参数相当于临时工,假如并发恳求的数量在10到200之间,就会运用这些临时工线程进行处理。
- server.tomcat.max-connections:最大连接数,默许巨细是8192。表明Tomcat能够处理的最大恳求数量,超越8192的恳求就会被放入到等候行列。
- server.tomcat.accept-count:等候行列的长度,默许巨细是100。
举个比如阐明一下这几个参数之间的联系:
假如把Tomcat比作一家饭店的话,那么一个恳求其实就相当于一位客人。min-spare便是厨师(长时间工);max是厨师总数(长时间工+临时工);max-connections便是饭店里的座位数量;accept-count是门口小板凳的数量。来的客人优先坐到饭店里边,然后厨师开始忙活,假如长时间工能够干的完,就让长时间工干,假如长时间工干不完,就再让临时工干。图中画的厨师一共15人,饭店里有30个座位,也便是说,假如现在来了20个客人,那么就会有5个人先在饭店里等着。假如现在来了35个人,饭店里坐不下,就会让5个人先到门口坐一下。假如来了50个人,那么饭店座位+门口小板凳一共40个,所以就会有10人脱离。
也便是说,SpringBoot一起所能处理的最大恳求数量是max-connections+accept-count
,超越该数量的恳求直接就会被丢掉。
纸上得来终觉浅,绝知此事要躬行。
上面仅仅理论成果,现在通过一个实际的小比如来演示一下到底是不是这样:
创立一个SpringBoot的项目,在application.yml里装备一下这几个参数,因为默许的数量太大,欠好测验,所以配小一点:
server:
tomcat:
threads:
# 最少线程数
min-spare: 10
# 最多线程数
max: 15
# 最大连接数
max-connections: 30
# 最大等候数
accept-count: 10
再来写一个简略的接口:
@GetMapping("/test")
public Response test1(HttpServletRequest request) throws Exception {
log.info("ip:{},线程:{}", request.getRemoteAddr(), Thread.currentThread().getName());
Thread.sleep(500);
return Response.buildSuccess();
}
代码很简略,仅仅打印了一下线程名,然后休眠0.5秒,这样肯定会导致部分恳求处理一次性处理不了而进入到等候行列。
然后我用Apifox创立了一个测验用例,去模仿100个恳求:
观察一下测验成果:
从成果中能够看出,因为设置的 max-connections+accept-count 的和是40,所以有60个恳求会被丢掉,这和咱们的预期是相符的。因为最大线程是15,也便是有25个恳求会先等候,等前15个处理完了再处理15个,最终在处理10个,也便是将40个恳求分成了15,15,10这样三批进行处理。
再从控制台的打印日志能够看到,线程的最大编号是15,这也印证了前面的想法。
总结一下:假如并发恳求数量低于server.tomcat.threads.max,则会被当即处理,超越的部分会先进行等候,假如数量超越max-connections与accept-count之和,则剩余的部分则会被直接丢掉。
延伸:并发问题是怎么发生的
到目前为止,就已经搞明白了SpringBoot能够一起处理多少恳求的问题。可是在这里我还想基于上面的比如再延伸一下,便是为什么并发场景下会呈现一些值和咱们预期的不一样?
设想有以下场景:厨师们用一个账本记载一共做了多少道菜,每个厨师做完菜都记载一下,每次记载都是将账本上的数字先抄到草稿纸上,核算x+1等于多少,然后将核算的成果写回到账本上。
Spring容器中的Bean默许是单例的,也便是说,处理恳求的Controller、Service实例就只有一份。在并发场景下,将cookSum界说为全局变量,是所有线程共享的,当一个线程读到了cookSum=20,然后核算,写回前另一个线程也读到是20,两个线程都加1后写回,最终cookSum就变成了21,可是实际上应该是22,因为加了两次。
private int cookSum = 0;
@GetMapping("/test")
public Response test1(HttpServletRequest request) throws Exception {
// 做菜。。。。。。
cookSum += 1;
log.info("做了{}道菜", cookSum);
Thread.sleep(500);
return Response.buildSuccess();
}
假如要防止这样的情况发生,就涉及到加锁的问题了,就不在这里讨论了。
本文已录入至我的Github库房DayDayUP:github.com/RobodLee/Da…,欢迎Star
假如您觉得文章还不错,请给我来个
点赞
,收藏
,重视
学习更多编程常识,欢迎重视微信公众号『 R o b o d 』: