Introducción
Problemas que enfrentan los sistemas distribuidos
Las aplicaciones en arquitecturas distribuidas complejas tienen muchas dependencias, cada una de las cuales inevitablemente fallará en algún momento. Si la aplicación host no está aislada de estas fallas externas, tiene el potencial de ser derribada por ellas.
Por ejemplo, para una aplicación que depende de 30 servicios, cada uno con un tiempo de actividad del 99,99 %, puede esperar lo siguiente:
99,9930 = 99,7 % disponible
En otras palabras, el 0,03% de 100 millones de solicitudes = 3000000 fallarán
El servicio de 2 horas al mes no está disponible si todo funciona
la realidad suele ser peor
Cuando todo está bien, la solicitud se ve así:
Cuando uno de los sistemas tiene latencia, puede bloquear toda la solicitud del usuario:
En el caso de mucho tráfico, la demora de una dependencia de back-end puede hacer que todos los recursos en todos los servidores se saturen en unos pocos segundos (PD: significa que las solicitudes posteriores no se atenderán de inmediato
avalancha de servicio
Cuando se llaman a varios microservicios, suponga que el microservicio A llama al microservicio B y al microservicio C, y el microservicio B y el microservicio C llaman a otros microservicios. Si el tiempo de respuesta de la llamada de un microservicio en el enlace de distribución es demasiado largo o no está disponible, la llamada al microservicio A ocupará más y más recursos del sistema, lo que provocará que el sistema se bloquee, el llamado "efecto avalancha".
Para aplicaciones de alto tráfico, una sola dependencia de back-end puede saturar todos los recursos en todos los servidores en segundos. Peor que fallar, estas aplicaciones también pueden causar una mayor latencia entre los servicios, sobrecargando las colas de respaldo, los subprocesos y otros recursos del sistema, lo que genera más fallas en cascada en todo el sistema. Todo esto representa la necesidad de aislar y administrar fallas y latencias para que la falla de una sola dependencia no pueda derribar una aplicación o sistema completo.
Por lo general, cuando descubre que una instancia de un módulo falla, este módulo seguirá recibiendo tráfico en este momento, y luego este módulo problemático también llama a otros módulos, lo que provocará fallas en cascada o avalanchas.
Hystrix
El sitio web oficial github.com/Netflix/Hys… deja de actualizar.
Hystrix es una biblioteca de código abierto para lidiar con el retraso y la tolerancia a fallas de los sistemas distribuidos. En los sistemas distribuidos, muchas dependencias fallarán inevitablemente en llamadas, tiempos de espera, excepciones, etc.Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,提高分布式系统的弹性
熔断机制是应对雪崩效应的一种微服务链路保户机制,当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的相应信息。当检测当该节点微服务调用响应正常后恢复调用链路,熔断机制的注解是@HystrixCommand
“熔断器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(保险丝),,某个异常条件被触发,直接熔断整个服务。,向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出吊牌用方法无法处理的异常,就保证了服务调用方的线程不会被长时间占用,避免故障在分布式系统中蔓延,乃至雪崩
当你使用Hystrix来包装每个依赖项时,上图中所示的架构会发生变化,如下图所示:
每个依赖项相互隔离,当延迟发生时,它会被限制在资源中,并包含回退逻辑,该逻辑决定在依赖项中发生任何类型的故障时应作出何种响应
重要概念
服务降级:服务器忙,请稍候再试,不让客户端等待并立刻返回一个友好提示,fallback
哪些情况会触发降级
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
服务熔断:类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
服务限流:秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
环境代建
新建cloud-provider-hystrix-payment8001
,7001服务注册中心方便启动测试成单机版
hystrix一般用于消费端,但是也可以用于服务端
pom
主要引入hystrix依赖,eureka client依赖
<dependencies>
<!-- hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.kylin</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml
server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
主启动类
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
service
创建PaymentService
接口,编写两个方法一个能正常运行,一个会停止几秒在运行
public interface PaymentService {
String paymentInfo_OK(Integer id);
String paymentInfo_TimeOut(Integer id);
}
编写PaymentServiceImpl
实现类实现接口
@Service
public class PaymentServiceImpl implements PaymentService {
@Override
public String paymentInfo_OK(Integer id) {
return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id:" +id+ "\t" + "哈哈哈~";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
int timeNumber = 3;
try {
//停止timeNumber秒
TimeUnit.SECONDS.sleep(timeNumber);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" +id+ "\t" + "哈哈哈~"+"耗时(秒):"+timeNumber;
}
}
controller
@RestController
@Slf4j
public class PaymentController {
@Resource
PaymentService paymentService;
@Value("${server.port}")
String ServerPort;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String result = paymentService.paymentInfo_OK(id);
log.info("****result:"+result);
return result;
}
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentService.paymentInfo_TimeOut(id);
log.info("****result:"+result);
return result;
}
}
测试
启动7001,8001进行测试
访问http://localhost:8001/payment/hystrix/ok/31
测试
访问http://localhost:8001/payment/hystrix/timeout/31
耗时至少3秒
环境代建成功!
Jmeter压测测试
官网jmeter.apache.org/下载解压,bin目录下…
上述在非高并发情形下,还能勉强满足 我们使用开启Jmeter,来20000个并发压死8001,20000个请求都去访问paymentInfo_TimeOut服务
保存
添加HTTP请求
配置请求路径
点击运行
查看后台,正在有不断的正在进行访问测试
此时再次访问http://localhost:8001/payment/hystrix/ok/31
可正常访问的请求。也会变得慢了起来,以前是毫秒就能完成,现在要2秒左右
为什么会变慢:tomcat的默认的工作线程数被打满了,没有多余的线程来分解压力和处理。
上面还是服务提供者8001自己测试,假如此时外部的消费者80也来访问,那消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖死
加入服务消费者测试
新建cloud-consumer-feign-hystrix-order80
pom
<dependencies>
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.kylin</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml
server:
port: 80
eureka:
client:
register-with-eureka: true #表示不向注册中心注册自己
fetch-registry: true #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
spring:
application:
name: cloud-provider-hystrix-order
主启动类
使用到了OpenFeign
使用注解开启@EnableFeignClients
,这里省略了@EnableEurekaClient
简而言之就是当我们引用了EurekaClient的依赖后,并且我们的两个开关不手动置为false,Spring就会自动帮助我们执行EurekaAutoServiceRegistration类里的start()方法,而注册的动作就是在该方法里完成的。所以,我们的EurekaClient工程,并不需要显式的在SpringBoot的启动类上标注@EnableEurekaClient注解,也可注册到注册中心。
@SpringBootApplication
@EnableFeignClients
//@EnableEurekaClient//我们的两个开关不手动置为false,Spring就会自动帮助我们执行注册
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class,args);
}
}
service
@FeignClient("CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
controller
@RestController
@Slf4j
public class OrderHystrixController {
@Resource
PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_OK(id);
return result;
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
}
测试
启动80,8001,7001
8001自测访问http://localhost:8001/payment/hystrix/ok/31
http://localhost:8001/payment/hystrix/timeout/31
一切正常
80端http://localhost/consumer/payment/hystrix/ok/80
访问正常的速度也十分快
Jmeter测试
80要么出现报错
要么就变得访问十分缓慢
故障现象和导致原因
8001同一层次的其他接口服务被困死,因为tomcat线程里面的工作线程已经被挤占完毕。80此时调用8001,客户端访问响应缓慢,转圈圈
正因为有上述故障或不佳表现,才有我们的降级/容错/限流等技术诞生
解决
超时导致服务器变慢(转圈)-> 超时不再等待
出错(宕机或程序运行出错)-> 出错要有兜底
对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级
对方服务(8001)down机了,调用者(80)不能一直卡死等待,必须有服务降级
对方服务(8001)OK,调用者(80)自己出故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级
服务降级
通过@HystrixCommand
注解的fallbackMethod
属性指定降级方法。groupKey
和commandKey
默认为方法名(当然threadPoolKey不指定时,默认和groupKey一致,所以也是方法名),也可以指定这三个key值,配置文件通过groupKey,commandKey,threadPoolKey使用恰当的配置。commandProperties
和threadPoolProperties
是通过@HystrixProperty
的name value键值对进行配置。
服务提供者
设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处理,作服务降级fallback
service
如果再调用的方法上有形参,那么fallback方法也同样需要形参;不然会报 找不到fallback方法异常
@Service
public class PaymentServiceImpl implements PaymentService {
@Override
public String paymentInfo_OK(Integer id) {
return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id:" + id + "\t" + "哈哈哈~";
}
@Override
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
public String paymentInfo_TimeOut(Integer id) {
int timeNumber = 5;
try {
//停止timeNumber秒
TimeUnit.SECONDS.sleep(timeNumber);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" + "哈哈哈~" + "耗时(秒):" + timeNumber;
}
public String paymentInfo_TimeOutHandler(Integer id) {
return "线程池:" + Thread.currentThread().getName() + " 系统繁忙或者运行报错,请稍后再试,id:" + "\t" + "/(ㄒoㄒ)/~~";
}
}
主启动类
在启动类上加上@EnableCircuitBreaker
,开启断路器功能
@SpringBootApplication
@EnableEurekaClient
//开启断路器功能
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
测试
启动7001,80,访问http://localhost:8001/payment/hystrix/timeout/31
用来单独的线程池来处理,当等待时间超过了3秒调用fallbackMethod
这个异常属于超时异常能被处理,我们修改异常为其他异常看看能不能被处理
@Service
public class PaymentServiceImpl implements PaymentService {
@Override
public String paymentInfo_OK(Integer id) {
return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id:" + id + "\t" + "哈哈哈~";
}
@Override
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
public String paymentInfo_TimeOut(Integer id) {
int num = 10/0;
/* int timeNumber = 5;
try {
//停止timeNumber秒
TimeUnit.SECONDS.sleep(timeNumber);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
//return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" + "哈哈哈~" + "耗时(秒):" + timeNumber;
return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" + "哈哈哈~";
}
public String paymentInfo_TimeOutHandler(Integer id) {
return "线程池:" + Thread.currentThread().getName() + " 系统繁忙或者运行报错,请稍后再试,id:" + "\t" + "/(ㄒoㄒ)/~~";
}
}
重启测试访问http://localhost:8001/payment/hystrix/timeout/31
同样出现异常使用fallbackMethod,一进入请求中发生报错
结论:只要当前服务不可用,就会进行服务降级,调用fallbackMethod
服务消费者
进行客户端降级保护,一般服务降级用在客户端
yml
修改80客服端的配置文件,当前我们使用的是Feign做服务调用,开启使用Hystrixfeign.hystrix.enabled=true
server:
port: 80
spring:
application:
name: cloud-provider-hystrix-order
eureka:
client:
register-with-eureka: true #表示不向注册中心注册自己
fetch-registry: true #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
feign:
hystrix:
enabled: true #feign开启hystrix
主启动类
使用@EnableHystrix
开启Hystrix
@SpringBootApplication
@EnableFeignClients
//@EnableEurekaClient//我们的两个开关不手动置为false,Spring就会自动帮助我们执行注册
@EnableHystrix//开启Hystrix
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class,args);
}
}
controller
如果再调用的方法上有形参,那么fallback方法也同样需要形参;不然会报 找不到fallback方法异常
同样使用@HystrixCommand
注解
当调用时间超过了1.5秒,运行fallbackMethod方法
@RestController
@Slf4j
public class OrderHystrixController {
@Resource
PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_OK(id);
return result;
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,/(ㄒoㄒ)/~~";
}
}
测试
启动80 8001 7001http://localhost/consumer/payment/hystrix/timeout/31
出现报错
但是时间确实1秒多钟就进行了服务降级,而不是想象中的1.5秒多出现超时报错。这是为什么了?
原来是Hystrix的配置问题,hystrix.command.default.execution.timeout.enabled
,为false则超时控制有ribbon控制,为true则hystrix超时和ribbon超时都是用,但是谁小谁生效,默认为true。Hystrix的hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
超时时长默认1秒。ribbon的默认超时控制也为1秒,所以现在整个服务的超时时间都为一秒钟
我们修改配置文件,将ribbon和hystrix的超时控制时间修改为10秒
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 100000 #hystrix超时控制时长
#
##设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接后从服务器读取到可用资源所用的时间
ReadTimeout: 10000
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ConnectTimeout: 10000
重启80,再次访问http://localhost/consumer/payment/hystrix/timeout/31
同样出现了超时报错,进入了fallbackMethod方法。等待时长也是和我们期望的一样至少1.5秒。
为了进一步测试,我们将80的可以等待的超时时长改为10秒
重新启动80,访问http://localhost/consumer/payment/hystrix/timeout/31
,成功调用了8001提供的服务,等待时间也至少大于3秒。!
测试成功,但也存在问题,每个业务方法对应一个兜底的方法,代码膨胀,如果每一个方法都要对应一个配置,代码量爆炸。缺少全局的配置
全局服务降级
使用@DefaultProperties
注解,配置defaultFallback
属性,值为方法名。使用了@HystrixCommand
没有配置fallback的就是用全局的,配置了就使用自己的。就近原则
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystrixController {
@Resource
PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_OK(id);
return result;
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
/* @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "10000")
})*/
@HystrixCommand
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
int i = 10/0;
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,/(ㄒoㄒ)/~~";
}
//全局fallback
public String payment_Global_FallbackMethod(){
return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
}
}
测试
重启80,访问http://localhost/consumer/payment/hystrix/timeout/31
全局服务降级配置成功!解决了代码膨胀的问题
通配服务降级
上文我们的fallback方法和业务逻辑混一起,是有点混乱的。
根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新新建一个类PaymentFallbackService
实现该接口,统一为接口里面的方法进行异常处理。当这个Service的方法出现了异常,而请求中没有配置@HystrixCommand
则使用我们这个类中的异常处理方法。
新建PaymentFallbackService
类实现PaymentHystrixService
,重写方法里是对该方法出现异常的操作。
@Service
public class PaymentFallbackService implements PaymentHystrixService {
@Override
public String paymentInfo_OK(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_OK , (┬_┬)";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_TimeOut , (┬_┬)";
}
}
PaymentHystrixService中的@FeignClient
配置fallback属性,值为PaymentFallbackService.class
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
测试
重新启动80测试,访问http://localhost/consumer/payment/hystrix/ok/31
因为改请求没有配置@HystrixCommand
,所以当Service访问服务出现异常时,则会掉用fallback类中的对应方法
目前一切正常
当我们把8001服务给关闭后,Service访问服务出现异常时,则会掉用fallback类中的对应方法。
再次访问,服务降级配置成功!
服务熔断
熔断机制是应对雪崩效应的一种微服务链路保护机制,
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回”错误”的响应信息。当检测到该节点微服务响应正常后恢复调用链路,在SpringCloud框架机制通过Hystrix实现,Hystrix会监控微服务见调用的状况,当失败的调用到一个阈值,缺省是5秒内20次调用失败就会启动熔断机制,熔断机制的注解是@HystrixCommand
修改cloud-provider-hystrix-payment8001
Servcie
public interface PaymentService {
String paymentInfo_OK(Integer id);
String paymentInfo_TimeOut(Integer id);
String paymentCircuitBreaker(@PathVariable("id") Integer id);
}
//服务熔断
@Override
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
if (id < 0){
throw new RuntimeException("*****id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
}
circuitBreaker.enabled:是否启用熔断器,默认为 true;
circuitBreaker.forceOpen: circuitBreaker.forceClosed:是否强制启用/关闭熔断器,强制启用关闭都想不到什么应用的场景,保持默认值,不配置即可。
circuitBreaker.requestVolumeThreshold:启用熔断器功能窗口时间内的最小请求数。试想如果没有这么一个限制,我们配置了 50% 的请求失败会打开熔断器,窗口时间内只有 3 条请求,恰巧两条都失败了,那么熔断器就被打开了,5s 内的请求都被快速失败。此配置项的值需要根据接口的 QPS 进行计算,值太小会有误打开熔断器的可能,值太大超出了时间窗口内的总请求数,则熔断永远也不会被触发。建议设置为 QPS * 窗口秒数 * 60%
。
circuitBreaker.errorThresholdPercentage:在通过滑动窗口获取到当前时间段内 Hystrix 方法执行的失败率后,就需要根据此配置来判断是否要将熔断器打开了。 此配置项默认值是 50,即窗口时间内超过 50% 的请求失败后会打开熔断器将后续请求快速失败。
circuitBreaker.sleepWindowInMilliseconds:熔断器打开后,所有的请求都会快速失败,但何时服务恢复正常就是下一个要面对的问题。熔断器打开时,Hystrix 会在经过一段时间后就放行一条请求,如果这条请求执行成功了,说明此时服务很可能已经恢复了正常,那么会将熔断器关闭,如果此请求执行失败,则认为服务依然不可用,熔断器继续保持打开状态。此配置项指定了熔断器打开后经过多长时间允许一次请求尝试执行,默认值是 5000。
controller
//===服务熔断
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
String result = paymentService.paymentCircuitBreaker(id);
log.info("*******result:"+result);
return result;
}
测试
启动8001 7001http://localhost:8001/payment/circuit/31
访问http://localhost:8001/payment/circuit/-31
Después de varias visitas a identificaciones negativas, el servicio se interrumpirá. En este momento, acceder a identificaciones positivas también llamará al método alternativo
Después de un período de tiempo, envíe una solicitud correcta con una identificación positiva y el servicio se cerrará
Proceso de trabajo
Sitio web oficial github.com/Netflix/Hys...
Supervisión del servicio HystrixDashboard
Hystrix también proporciona monitoreo de llamadas en tiempo real (Hystrix Dashboard).Hystrix registrará continuamente la información de ejecución de todas las solicitudes iniciadas a través de Hystrix y las mostrará a los usuarios en forma de informes estadísticos y gráficos, incluyendo cuántas solicitudes se ejecutan por segundo y cuántos tienen éxito, cuántos fracasos, etc.
Netflix ha implementado el monitoreo de los indicadores anteriores a través del proyecto hystrix-metrics-event-stream. Spring Cloud también proporciona la integración de Hystrix Dashboard, que convierte el contenido de monitoreo en una interfaz visual.
para construir
nueva construccióncloud-consumer-hystrix-dashboard9001
pompón
<dependencies>
<!--新增hystrix dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml
Simplemente configure el número de puerto en 9001
server:
port: 9001
clase principal de inicio
Úselo @EnableHystrixDashboard
para abrir la interfaz gráfica de Hystrix
configuración de monitoreo
Si desea monitorear las clases de microservicios proporcionadas, debe importar la configuración de dependencia de monitoreo, que hemos configurado antes
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
prueba
Inicie 9001, visite http://localhost:9001/hystrix
, construya con éxito
combate
Inicie cloud-consumer-hystrix-dashboard9001
el microservicio y luego monitoree el microservicio 8001
Revisarcloud-provider-hystrix-payment8001
clase principal de inicio
@Bean
public ServletRegistrationBean getServlet(){
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
La nueva versión de Hystrix debe especificar la ruta de monitoreo en la clase de inicio principal MainAppHystrix8001; de lo contrario, se informará un error No se puede conectar a Command Metric Stream
prueba
Inicie 8001 y complete los datos en la interfaz de monitoreohttp://localhost:8001/hystrix.stream
Haga clic en el botón para monitorear 8001
Acceda a los errores y corrija las pruebas por separado