一、服务容错
1、服务存在的问题
微服务架构中,生产过程中我们或多或少的会遇到一些问题。如果服务提供者对服务消费者的响应非常的缓慢,那么服务消费对服务提供者的请求就会被迫等待,直到提供者响应或者超时。特别是在高并发的场景中,如果不做任何处理,每个服务消费者都一直请求服务提供者等待响应,大量请求不断涌入,占用消费者资源,最终导致消费者系统崩溃。
如最常见的电商系统,支付系统作为服务提供者,订单系统作为服务消费者。在高并发场景下,大量的订单进来,去请求支付系统进行支付。然而支付系统对来自订单系统的请求影响特别缓慢,订单系统就被迫等待,这时候还不断有来自订单系统的请求。长时间未响应,订单系统付款等待就会超时,导致用户支付失败,用户又重新进行支付,再次增加系统负载量,不仅订单系统面临资源被耗尽的风险,而且支付系统压力更大。
2、引发的雪崩效应
微服务架构中,服务之间存在调用关系是在所难免的,服务多级调用就会形成依赖关系。一旦上层服务不可用,中层服务固然会受影响,依赖中层服务的下层服务难免不熟影响,由上层服务引发的最终导致下层服务不可用,就形成了“级联故障”,这种现象业界一般称之为“雪崩效应”。如下图所示,由服务A引发的不可用最终导致服务C不可用,形成雪崩效应。
3、服务容错的手段
要想防止雪崩效应,大致有以下两种手段:
(1)设置网络超时
正常情况下,服务消费者去请求服务提供者,几十毫秒就可以响应返回。如果服务提供系统不可用或者网络异常,那么响应时间会很长或者最终超时。因此要为服务消费系统设置请求超时时间,这也是传统的做法。
(2)使用断路器模式
这里说的断路器和家用电路的断路器原理一样。家用电路,当电流瞬间过大,电路升温,断路器检测到就会立即切点电路,即自动跳闸,保证电路安全。当电路超载的问题被解决后,关闭断路器,电路就可以恢复正常了。
同理,对某个微服务的请求在短时间内有大量超时,通常说明该服务不可用,在去重新请求已经没有任何意义,只会增加系统资源的消耗。
断路器可以理解为对容易导致错误操作的代理。这种代理能够统计一段时间内调用失败的次数,并决定是正常请去请求服务提供者,还是直接返回失败。
断路器可以实现快速的失败,如果它在一段时间内检测的许多的类似错误(如超时),就会在一段时间之内,强迫对服务的快速调用失败,不在请求依赖的服务,这样消费系统就不再占用系统资源去等待响应超时。
断路器可以自动诊断所依赖的服务是否恢复正常。如果发现所依赖的服务已经恢复正常,那么就会恢复对依赖服务的正常请求。使用这种方式,就可以实现服务的“自我修复”。当依赖的服务不正常时就打开断路器进行快速失败,从而防止雪崩效应;当发现依赖的服务恢复正常时,关闭断路器,恢复正常的请求。
下图展示了断路器转换的逻辑
正常情况下断路器处于关闭状态,可以正常请求依赖的服务。一段时间之后, 请求失败率达到一定阀值,断路器就会打开,此时就会进入快速失败,不再去请求依赖的服务。断路器打开一段时间之后,就会自动进入“半开”状态,半开状态允许一个请求访问依赖服务,如果允许的这个请求能够成功,则关闭断路器;否则继续保持打开状态。
二、Hystrix实现容错
Hystrix是一个实现了超时机制和断路器模式的的工具类库。它由NetFlix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。
包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令使用独立的线程运行。采用了设计模式中的“命令模式”。
跳闸机制: 当某个服务的请求错误率超过一定阀值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。
资源隔离:Hystrix为每个服务都维护了一个小型线程池(或者信号量)。如果该服务线程池已满, 发往该依赖的请求就会立即被拒绝,而不是排队等候,从而加速失败判定。
监控:Hystrix可以近乎实时地监控运行指标和配置的变化,如成功、失败、超时以及被拒绝的请求等。
回退机制:当请求失败、超时、被拒绝,或者断路器打开时,执行回退逻辑,即服务降级,回退逻辑可以由开发人员自行编写。
自我修复:“自我修复”即断路器从打开到半开在再到关闭的状态转换,可以参见本文前边的描述。
GitHub地址:https://github.com/Netflix/Hystrix
三、Feign使用Hystrix
1、pom.xml引入maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
2、启动类上添加注解@EnableCircuitBreaker,启动断路器。
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
@SpringCloudApplication
@EnableCircuitBreaker
@EnableFeignClients(basePackages = {"com.ultrapower.uws"})
public class UwsDataApplication {
public static void main(String[] args) {
SpringApplication.run(UwsDataApplication.class);
}
}
3、与feign一起使用
import org.springframework.stereotype.Component;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
import java.util.Map;
@FeignClient(name = "uws-data-service", fallback = CollectDeviceHystrixClient.class)
public interface CollectDeviceFeignClient {
@PostMapping(value="/getDeviceLastestPhone")
String getDeviceLasestPhone(@RequestBody Map<String, Object> map);
}
/**
* 服务降级处理,回退类实现须CollectDeviceFeignClient接口
*/
@Component
class CollectDeviceHystrixClient implements CollectDeviceFeignClient {
@Override
public String getDeviceLasestPhone(Map<String, Object> map) {
return "no lastest phone";
}
}
四、脱离Feign使用
启动类添加@EnableCircuitBreaker,方法上添加@HystrixCommand
@SpringBootApplication
@EnableCircuitBreaker
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
@Component
public class StoreIntegration {
@HystrixCommand(fallbackMethod = "defaultStores")
public Object getStores(Map<String, Object> parameters) {
//do stuff that might fail
}
public Object defaultStores(Map<String, Object> parameters) {
return /* something useful */;
}
}
注解@HystrixCommand中的fallbackMethod属性指定了失败回退的方法defaultStores。@HystrixCommand注解由名为javanica(https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica)的hystrix-contrib提供。javanica是Hystrix的一个子项目,用于简化Hystrix的使用,SpringCloud自动将带有该注解的SpringBean封装在一个连接到Hystrix断路器的代理中。此外@HystrixCommand的配置非常灵活的,可以使用注解@HystrixProperty中的属性来配置@HystrixCommand。
更多详见https://github.com/Netflix/Hystrix/wiki/Configuration
五、Hystrix的监控
1、基于API的调用的监控
添加maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
bootstrap.yml设置包含“hystrix.stream”
management:
endpoints:
web:
exposure:
include: ["hystrix-stream"]
访问如: http://locahost:8080/actuator/hystrix.stream
2、基于Dashboard可视化监控
1、独立使用
引入maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
启动类添加注解@EnableHystrixDashboard
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
@SpringCloudApplication
@EnableHystrixDashboard
public class UwsDataApplication {
public static void main(String[] args) {
SpringApplication.run(UwsDataApplication.class);
}
}
application.yml配置端口
Server:
port: 8030
访问http://locahost:8030/hystrix.stream,也可以与Eureka结合
2、把hystrix-dashboard注册到Eureka Server
启动类添加@EnableHystrixDashboard和@EnableDiscoveryClient
配置application.yml
server:
port: 8030
spring:
application:
name: uws-hystrix-dashboard
#eureka
eureka:
client:
service-url:
defaultZone: http://uws:uws@localhost:8761/eureka
instance:
prefer-ip-address: true
instance-id: ${spring.application.name}:${spring.application.instance_id:${server.port}}
访问http://locahost:8030/hystrix.stream
参考:《Spring Cloud与Docker微服务架构实战》周立