雪崩效应
在微服务架构中,一个请求需要调用多个服务是非常常见的。如客户端访问A服务,而A服务需要调用B
服务,B服务需要调用C服务,由于网络原因或者自身的原因,如果B服务或者C服务不能及时响应,A服
务将处于阻塞状态,直到B服务C服务响应。此时若有大量的请求涌入,容器的线程资源会被消耗完毕,
导致服务瘫痪。服务与服务之间的依赖性,故障会传播,造成连锁反应,会对整个微服务系统造成灾难
性的严重后果,这就是服务故障的“雪崩”效应。
雪崩是系统中的蝴蝶效应导致其发生的原因多种多样,有不合理的容量设计,或者是高并发下某一个方
法响应变慢,亦或是某台机器的资源耗尽。从源头上我们无法完全杜绝雪崩源头的发生,但是雪崩的根
本原因来源于服务之间的强依赖,所以我们可以提前评估,做好熔断,隔离,限流。
服务隔离
顾名思义,它是指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖。
当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体
的系统服务。
熔断降级
熔断这一概念来源于电子工程中的断路器(Circuit Breaker)。在互联网系统中,当下游服务因访问压
力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这
种牺牲局部,保全整体的措施就叫做熔断。
所谓降级,就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的
fallback回调,返回一个缺省值。 也可以理解为兜底
服务限流
限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说
系统的吞吐量是可以被测算的,为了保证系统的稳固运行,一旦达到的需要限制的阈值,就需要限制流
量并采取少量措施以完成限制流量的目的。比方:推迟解决,拒绝解决,或者者部分拒绝解决等等。
Hystrix介绍
Hystrix 是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失
败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。
包裹请求:使用 HystrixCommand包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用
了设计模式中的“命令模式”。
跳闸机制:当某服务的错误率超过一定的阈值时, Hystrix可以自动或手动跳闸,停止请求该服务一段时间。
资源隔离: Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,
发往该依赖的请求就被立即拒绝,而不是排队等待,从而加速失败判定。
监控: Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。
回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑由开发人员
自行提供,例如返回一个缺省值。
自我修复:断路器打开一段时间后,会自动进入 “半开”状态。
整合Hystrix
-
order-service
-
pom.xml
-
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
-
web层
1 @RestController 2 @RequestMapping("/api/v1/order") 3 public class OrderController { 4 5 6 @Autowired(required = false) 7 private ProductOrderServiceImpl productOrderService; 8 9 10 @RequestMapping("/save") 11 @HystrixCommand(fallbackMethod="saveOrderFail") 12 public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id") int productId){ 13 14 Map<String, Object> data = new HashMap<>(); 15 data.put("code", 0); 16 data.put("data", productOrderService.save(userId, productId)); 17 return data; 18 } 19 20 //注意,方法签名一定要要和api方法一致 21 public Object saveOrderFail(int userId,int productId){ 22 23 Map<String, Object> msg = new HashMap<>(); 24 msg.put("code", -1); 25 msg.put("msg", "抢购人数太多,您被挤出来了,稍等重试"); 26 return msg; 27 } 28 29 30 }
-
application.yml
1 server: 2 port: 8781 3 4 5 #指定注册中心地址 6 eureka: 7 client: 8 serviceUrl: 9 defaultZone: http://localhost:8761/eureka/ 10 11 #服务的名称 12 spring: 13 application: 14 name: order-service 15 16 ###配置请求超时时间 17 hystrix: 18 command: 19 default: 20 execution: 21 isolation: 22 thread: 23 timeoutInMilliseconds: 7000 24 ribbon: 25 ##指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间。 26 ReadTimeout: 3000 27 ##指的是建立连接后从服务器读取到可用资源所用的时间。 28 ConnectTimeout: 3000 29 30 #自定义负载均衡策略 31 #product-service: 32 # ribbon: 33 # NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
-
-
product-service
-
service层
1 package com.topcheer.producerservice.controller; 2 3 import com.topcheer.producerservice.domain.Product; 4 import com.topcheer.producerservice.service.ProductService; 5 import org.springframework.beans.BeanUtils; 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.beans.factory.annotation.Value; 8 import org.springframework.web.bind.annotation.*; 9 10 import java.util.concurrent.TimeUnit; 11 12 @RestController 13 @RequestMapping("/api/v1/product") 14 public class ProductController { 15 16 17 18 @Value("${server.port}") 19 private String port; 20 21 @Autowired 22 private ProductService productService; 23 24 /** 25 * 获取所有商品列表 26 * @return 27 */ 28 @RequestMapping("list") 29 public Object list(){ 30 return productService.listProduct(); 31 } 32 33 34 /** 35 * 根据id查找商品详情 36 * @param id 37 * @return 38 */ 39 @RequestMapping("find") 40 public Object findById(int id){ 41 42 try { 43 TimeUnit.SECONDS.sleep(2); 44 } catch (InterruptedException e) { 45 e.printStackTrace(); 46 } 47 Product product = productService.findById(id); 48 49 Product result = new Product(); 50 BeanUtils.copyProperties(product,result); 51 result.setName( result.getName() + " data from port="+port ); 52 return result; 53 } 54 55 }
启动类也要添加注解
-
SpringBootApplication @EntityScan("com.topcheer.order.entity") @EnableFeignClients @EnableHystrix public class OrderApplication { /** * 使用spring提供的RestTemplate发送http请求到商品服务 * 1.创建RestTemplate对象交给容器管理 * 2.在使用的时候,调用其方法完成操作 (getXX,postxxx) * * @LoadBalanced : 是ribbon提供的负载均衡的注解 */ @LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(OrderApplication.class,args); } }
注:也可以卸载整个类上,代表整个类都会走这个异常
-
-
结果:
当前面的ribbon配置3秒的时候,可以直接返回结果。
当前面的ribbon配置2秒的时候,会被HystrixCommand里面的异常方法捕获到。