一、服务容错
1、高并发带来的问题
现在我们有了订单微服务、商品微服务、用户微服务,服务与服务之间可以相互调用,但是由于网络或者自身原因,微服务不能保证100%都是可用,如果其中一个服务出现问题,那么调用它的服务就会出现网络延迟,此时如果有大量网络涌入,会形成任务堆积,最终导致服务瘫痪。
如图所示,serviceB出现故障,此时serviceA中的a1()方法去调用b1()方法,得不到响应信息,
一直调用,如果按照tomcat最大线程数400来说,那么service中a1()方法一直拿到400位置,
如果此时a2()方法想要去调用b2()方法,发现没有可用连接,也会等待。导致其他方法也不可用,
最终导致整个serviceA不可用,那么调用serviceA的服务最终也不可用,这就是服务雪崩,
发生雪崩时没有一片雪花是无辜的!!!
2、模拟并发场景
原有下单过程,线程睡上5s
@RestController
@Slf4j
@RequestMapping("/test")
public class OrderController2 {
@Autowired
private IOrderService orderService;
@Autowired
private IProductService productService;
//下单 使用feign调用远程服务
@RequestMapping("/createOrder/{pid}")
public Order createOrder(@PathVariable Integer pid) {
//使用微服务的名字,从注册中心Nacos中获取
Product product = productService.findProdutById(pid);
try {
Thread.sleep(5000);
log.info("模拟并发场景,网络延时");
} catch (InterruptedException e) {
e.printStackTrace();
}
Order order = new Order();
order.setNumber(2);
order.setUid(1);
order.setUsername("chenchen");
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
return order;
}
@RequestMapping("/show")
public String show() {
return "测试并发:"+new SimpleDateFormat("yyy-MM-dd HH:mm:ss").format(new Date());
}
}
配置文件中修改tomcat连接数
server:
port: 8092
tomcat:
max-threads: 10
使用压测工具JMeter测试
由于createOrder()方法堆积了大量请求,再去请求show()方法,可以看到请求阻塞…
3、服务雪崩
简单来说在分布式系统中,由于网络或者自身原因,服务一般无法保证100%可用。如果一个服务出现了问题,调用的这个服务就会出现线程阻塞的情况,此时若有大量的请求涌入,就会出现多条线程阻塞等待,进而导致服务瘫痪。正是服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成毁灭性的后果,也就是常说的雪崩效应,如图所示。
服务发生雪崩的原因有很多,比如不合理的容量设计(service B只能接受10个线程访问,但是给了20个线程),或者并发下一个方法响应变慢,或者某个服务资源耗尽等,正常情况下无法杜绝源头的发生,只能做好足够的容错,保证一个服务发生问题,而不影响其他服务正常运行,雪落而不雪崩,
也可以总结三句话:
1、不被上游服务压垮(service A有大量请求过来时,保证自身(service B)始终是可用的)
2、不被下游服务拖垮(service C挂掉时,自身不被影响)
3、外部环境保证可用(保证机器正常运行,不能运行着突然断电等情况发生)
4、常见容错方案
隔离、超时、限流、熔断、降级
4.1 隔离
指的是系统按照一定原则划分若干个服务模块,各个模块之间相对独立、无强依赖,当有故障发生时,
能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其他模块,不影响整体的系统服务,
如线程池隔离和信号量隔离
4.2 超时
指的是在上游服务调用下游服务的时候,设置一个最大响应时间,如果超过这个时间,下游未作出反应,
就断开请求,释放掉线程,达到自我保护的目的。
4.3 限流
限制系统的输入和输出流量,达到保护系统目的。为了保证系统的稳固运行,一旦达到设置的阈值,就需要限流。
4.4 熔断
如果下游服务因为访问压力过大而影响变慢或者失败,上游服务为了保证系统整体可用性,
可以暂时切断对下游服务的调用,这种局部牺牲,保全整体的措施就是熔断。
服务熔断的三种状态
1、熔断关闭 closed
熔断没有故障时,熔断器所处的状态,对于调用方不做任何限制
2、熔断开启 open
后续对该服务接口的调用不在经过网络,直接执行本地fallback方法
3、半熔断Half-Open
尝试恢复调用,允许有限的流量调用该服务,并且监控调用成功率,如果成功率达到预期,则说明服务已经恢复,进入熔断关闭,如果成功率很低,则重新进入熔断开启。
4.5 降级
降级是为服务提供一个托底方案,一旦服务无法正常调用,就使用托底(备用)方案。