一、微服务架构的高并发问题
背景:由于服务器的最大处理线程数都是有上线的,比如tomcat等。当系统某时刻出现高并发请求时,如秒杀活动等,在瞬间服务器可处理线程数瞬间使用完,线程资源耗尽。当后面的其他请求在过来时,请求将一直处于一段等待空闲释放线程的时间,在此时间内对用户的体验就是一直未响应状态。
如微服务中的雪崩效应,微服务A在对外提供服务时,需要调用微服务B,微服务B对A提供服务时,需要调用微服务C。如果服务C出现问题,对于服务器B和A都将无法释放请求占用的线程资源。如果请求较多,服务B和服务A服务器节点无法给其他服务DEF等其他服务在开启线程资源,间接的将影响到DEF等等。。如此下去整个微服务架构系统将会因为一个服务的故障导致整个架构瘫痪。
二、微服务架构的高并发问题复现
- 服务提供者工程提供一个耗时2秒的订单查询接口。
- 服务消费者工程提供一个订单查询接口,远程http调用提供者工程中的订单查询接口。
- 服务消费者工程在提供一个本地的快速查询订单接口,不远程调用,也不做延迟。
- 服务消费者工程对最大线程数做指定限制为最大10个线程。
- 使用压测工具同时启动20个线程调用消费者工程快速查询接口,并循环50次,在运行过程中查看快速查询接口观察期相应速度
我们对消费者工程进行配置设置tomcat的最大线程数为10,即工程同时处理的最多线程数量为10个。然后使用Apache JMeter 压测工具进行模拟高并发请求订单查询接口。在请求过程中我们页面访问快速查询接口,查看其相应时间等效果。
1,提供者工程Controller 核心代码:
@GetMapping("/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPaymentById(id);
log.info(payment.toString());
try{TimeUnit.SECONDS.sleep(2);}catch (Exception e){e.printStackTrace();}
if(payment != null){
return new CommonResult(0,"查询成功 server node:"+ip+":"+port,payment);
}else{
return new CommonResult(-9999,"查询失败,不存在信息 server node:"+ip+":"+port,null);
}
}
2,消费者工程Controller 核心代码
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
return restTemplate.getForObject("http://127.0.0.1:8001/payment/get/"+id,CommonResult.class);
}
@GetMapping("/consumer/fastget/{id}")
public CommonResult<Payment> fastget(@PathVariable("id") Long id){
System.out.println(Thread.currentThread().getName()+"------fastget------");
return new CommonResult(200,"fastget:"+id,null);
}
消费者工程tomcat 最大线程数配置 application.yml
server:
port: 80 #服务端口
tomcat:
max-threads: 10 #tomcat最大线程数
3,高并发压测环境安装
下载安装Apache Jmeter 下载地址:https://jmeter.apache.org/download_jmeter.cgi
运行启动:apache-jmeter-5.3\bin\jmeter.bat
4,启动压测程序并查看快速查询接口,可以看到在没有高并发时,秒返回的,在高并发下相应时间长达3秒。
三、高并发问题的解决策略
为了解决微服务中的资源耗尽或服务间强依赖导致的间接服务不可用处理的方式
-
以服务的维度可以做
1,线程池隔离:对不同的服务分配单独的线程池,超过线程池数量的改服务请求进行线程池外等待。(下图1)
2,信号量隔离:对不同的服务设置固定的同时请求数量,超过了正在处理的数量时,直接进行错误返回(下图2)
-
以线程维度可以做
1,服务隔离
顾名思义,系统将按照一定的原则划分,建立若干个相互独立、无强依赖当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其他模块,不影响整体的服务系统。
2,熔断降级
熔断即当下游服务因为请求压力大时,相应便慢或者出错时,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部保证整体的措施叫做熔断。
降级即在上游服务熔断了调用下游接口后,做的一个本地fallback的一个缺省回调返回,即容错处理。
3,服务限流
限流即限制服务的调用流量,一般来说服务的并发流量可以被测算。为了保证系统的稳固运行,服务一单到达需要限制的并发数阈值时,就需要限制调用以及采取措施完成限流目的。比如:推迟解决、拒绝解决、或者部分处理部分拒绝等。