微服务之间是通过网络进行相互调用,如果单个服务出现问题,用户调用相关服务时或造成调用延迟或失败,进而可能让整个系统奔溃。提前做好应急措施,当遇到问题时,可及时启动应急方案,让系统进行自我调节和保护。
1 Hystrix
Hystrix 是一个用于处理分布式系统延迟和容错的库,旨在通过隔离服务的访问点、停止级联故障以及提供回退选项来提高系统的整体弹性。
1.1 Hystrix 入门
maven依赖:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
在Application类增加@EnableCircuitBreaker注解来开启Hystrix支持。
Hystrix 提供降级有2种方式:1)通过@HystrixCommand注解;2)通过继承HystrixCommand或HystrixObservableCommand类。
1.1.1 @HystrixCommand注解
java">@RequestMapping("/home")
@RestController
public class HomeController {private final RestTemplate restTemplate;private final UserService userService;public HomeController(RestTemplate restTemplate, UserService userService) {this.restTemplate = restTemplate;this.userService = userService;}@HystrixCommand(fallbackMethod = "infoCallback")@GetMapping("/info")public String info(String name) {return restTemplate.getForObject("http://provider/user/info?name=" + name + "consumer",String.class);}@HystrixCommand(fallbackMethod = "updateCallback")@PostMapping("/update")public String update(@RequestBody User user) {return userService.update(user);}public String infoCallback(String name) {return "/info 降级";}public String updateCallback(User user) {return "/update 降级," + user;}
}
fallbackMethod所指定的方法要与原方法具有相同的方法签名。
1.1.2 继承HystrixCommand
java">public class UserInfoFailCommand extends HystrixCommand<String> {private final RestTemplate restTemplate;private final String name;public UserInfoFailCommand(RestTemplate restTemplate, String name) {super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")));this.restTemplate = restTemplate;this.name = name;}@Overrideprotected String run() throws Exception {return restTemplate.getForObject("http://provider/user/info?name=" + name,String.class);}@Overrideprotected String getFallback() {return "userInfoFailCommand 降级 info";}
}
java">@RequestMapping("/home")
@RestController
public class HomeController {private final RestTemplate restTemplate;public HomeController(RestTemplate restTemplate) {this.restTemplate = restTemplate;}@GetMapping("/info3")public String info3(String name) {UserInfoFailCommand command = new UserInfoFailCommand(restTemplate,name);return command.execute();}
}
默认是阻塞式的,可提供同步和异步两种方式。同步通过execute()方法直接返回结果,异步执行通过queue()方法返回一个Future对象。
1.1.3 继承HystrixObservableCommand
java">public class UserInfoObservableCommand extends HystrixObservableCommand<String> {private final RestTemplate restTemplate;private final String name;public UserInfoObservableCommand(RestTemplate restTemplate, String name) {super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")));this.restTemplate = restTemplate;this.name = name;}@Overrideprotected Observable<String> construct() {String res = restTemplate.getForObject("http://provider/user/info?name=" + name, String.class);return Observable.just(res + ",UserInfoObservableCommand construct");}@Overrideprotected Observable<String> resumeWithFallback() {return Observable.just("UserInfoObservableCommand info 降级");}
}
java">@RequestMapping("/home")
@RestController
public class HomeController {private final RestTemplate restTemplate;public HomeController(RestTemplate restTemplate) {this.restTemplate = restTemplate;}@GetMapping("/info4")public String info4(String name) {UserInfoObservableCommand command = new UserInfoObservableCommand(restTemplate, name);Iterator<String> iterator = command.observe().toBlocking().getIterator();StringBuilder sb = new StringBuilder();while (iterator.hasNext()) sb.append(iterator.next());return sb.toString();}
}
非阻塞的。只能是异步调用。通过observe()和toObservable()方法返回一个Observable对象。observe()方法会立即发出请求,toObservable()方法只有在订阅该对象时,才会发出请求。
1.1.4 在Feign中使用Hystrix回退
java">@Component
public class UserServiceCallback implements UserService{@Overridepublic String info(String name) {return "info 降级2:" + name;}@Overridepublic String update(User user) {return "update 降级2:" + user;}}
java">@RequestMapping("/home")
@RestController
public class HomeController {private final UserService userService;public HomeController(UserService userService) {this.userService = userService;}@GetMapping("/info2")public String info2(String name) {return userService.info(name + "feign");}@PostMapping("/update")public String update(@RequestBody User user) {return userService.update(user);}
}
需要在配置文件中feign.hystrix.enabled属性设置为true。
1.2 容错机制分析
Hystrix能根据以下条件进行服务降级:
- 可配置依赖调用超时时间,当调用超时时,直接返回或进行服务降级。
- 为每个依赖关系/服务调用维护一个小的线程池(或信号量),如果已满,那么依赖服务调用将立即被拒绝,而不是排队等待。
- 对服务调用的执行状态(成功、失败、超时及线程拒绝等)进行统计,如果某服务调用的错误百分比高于阈值,则可通过手动或自动方式打开断路器。
1.2.1 Hystrix的执行过程
图 Hystrix的执行过程
1 命令封装与执行
通过命令模式,将用户对业务服务请求操作进行封装成HystrixCommand与HystrixObservableCommand。
2 结果缓存是否可用
如果开启了缓存功能,Hystrix在执行命令时首先会检查是否缓存命中,如果是则立即将缓存结果返回,并不再继续执行该命令。
3 断路器是否已打开
如果已打开,说明相应服务不可用,此时进行服务降级。
4 是否有资源执行
判断与该命令相关的线程池和队列是否已满(如果使用信号量隔离,则判断信号量是否已满),如果已满,则不执行该命令,进行服务降级。
5 执行业务逻辑
命令执行过程中,如果超时,那么执行线程将会抛出移除,此时会转入到fallback处理。
6 更新断路器健康数据
在执行业务逻辑的过程中,Hystrix将会把采集到的执行状态(成功、失败、拒绝和超时等)数据提交给断路器。
7 服务降级
在实现服务降级时,最好能提供一个默认的处理结果,该结果最好从内存缓存或一个静态逻辑处理中计算到,不要在有任何网络调用的依赖。
1.2.2 断路器原理分析
断路器根据收集到的命令状态数据,来决定是否打开。如果打开,后续的请求都会进行服务降级处理。然后隔一段时间尝试半开,放一部分流量请求进来,重新统计命令状态数据,如果满足条件,则断路器关闭。