SpringCloud之Hystrix服务熔断器学习笔记步步截图(附带学习时写的源码项目)
SpringCloud
整个spring cloud学习时写的项目源码:git@gitee.com:HumorChen/spring-cloud-parent.git
上一篇博客为:eureka(服务治理)SpringCloud服务注册发现(服务治理)之Eureka学习笔记步步截图(附带学习时写的源码项目)
读这篇博客前先下载上面的git项目。
初识Spring Cloud
什么是微服务
- "微服务”一词源于Martin Fowler的名为Microservices的博文,可以在他的官方博客上找到
http://martinfowler.com/articles/microservices.html - 微服务是系统架构上的一种设计风格,它的主旨是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间一般通过HTTP的RESTfuLAPI进行通信协作。
- 被拆分成的每一个小型服务都围绕着系统中的某一项或些耦合度较高的业务功能进行构建,并且每个服务都维护着白身的数据存储、业务开发自动化测试案例以及独立部署机制。
SpringCloud简介
-
spring cloud 是一系列框架的有序集合
-
spring cloud 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟的、经得起实际考验的框架组合起来
-
通过Spring Boot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包
-
它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务注册发现、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
-
Spring Cloud版本命名方式采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本时间顺序,比如:最早的Release版本: Angel,第二个Release版本: Brixton,然后是Camden、Dalston、Edgware,Finchley,Greenwich,Hoxton
-
spring cloud版本和springboot版本对应关系
Spring Cloud 与Dubbo对比
- Spring Cloud 与 Dubbo都是实现微服务有效的工具。
- Dubbo只是实现了服务治理,而Spring Cloud子项目分别覆盖了微服务架构下的众多部件。
- Dubbo使用RPC通讯协议,Spring Cloud使用RESTful完成通信,Dubbo效率略高于Spring Cloud。
小结
- 微服务就是将项目的各个模块拆分为可独立运行、部署、测试的架构设计风格。
- Spring公司将其他公司中微服务架构常用的组件整合起来,并使用SpringBoot简化其开发、配置。称为Spring Cloud
- Spring Cloud 与Dubbo都是实现微服务有效的工具。Dubbo性能更好,而Spring Cloud功能更全面。
Hystrix熔断器
-
Hystix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败(雪崩)
-
雪崩:一个服务失败,导致整条链路的服务都失败的情形。
-
Hystrix主要功能
-
隔离
1.线程池隔离2.信号量隔离
-
降级:异常,超时
-
熔断
-
限流
-
Hystrix降级-服务提供方
- 在服务提供方,引入hystrix依赖
- 定义降级方法
- 使用@HystrixCommand注解配置降级方法
- 在启动类上开启Hystrix功能:@EnableCircuitBreaker
-
启动一个之前的eureka-server跑在8761端口
-
创建hystrix-consumer和hystrix-provider模块,分别把前面Feign的consumer和provider的main文件夹以及pom.xml里的依赖复制到对应的模块去
-
给provider加入hystrix的依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>spring-cloud-parent</artifactId> <groupId>com.fpa</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>hystrix-provider</artifactId> <dependencies> <!--spring boot web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- eureka-client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- hystrix --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> </dependencies> </project>
-
给provider模块的启动类加入注解@EnableCircuitBreaker
package com.fpa.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker//开启hystrix熔断器 public class ProviderApp { public static void main(String[] args) { SpringApplication.run(ProviderApp.class,args); } }
-
给之前的接口增加一个降级后的方法
package com.fpa.provider.controller; import com.fpa.provider.bean.Goods; import com.fpa.provider.service.GoodsService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/goods") public class GoodsController { @Autowired private GoodsService goodsService; @GetMapping("/{id}") @HystrixCommand(fallbackMethod = "findOne_fallback")//指定降级后执行的方法名字 public Goods findOne(@PathVariable("id") int id){ return goodsService.findOne(id); } /** * 上面的方法一旦降级后就执行这个方法的逻辑 * @param id * @return */ public Goods findOne_fallback(int id){ Goods goods = new Goods(); goods.setTitle("服务降级了呀"); return goods; } }
-
启动provider,调用后发现正常返回了数据,不摆图了
-
给接口里加一个异常进去,测一下一但发生异常之后的变化
package com.fpa.provider.controller; import com.fpa.provider.bean.Goods; import com.fpa.provider.service.GoodsService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/goods") public class GoodsController { @Autowired private GoodsService goodsService; @GetMapping("/{id}") @HystrixCommand(fallbackMethod = "findOne_fallback")//指定降级后执行的方法名字 public Goods findOne(@PathVariable("id") int id){ //故意制造一个异常出来 int a = 1/0; return goodsService.findOne(id); } /** * 上面的方法一旦降级后就执行这个方法的逻辑 * @param id * @return */ public Goods findOne_fallback(int id){ Goods goods = new Goods(); goods.setTitle("服务降级了呀"); return goods; } }
-
重启provider看效果,调用了指定的降级后的方法
-
故意制造一个超时,然后重启provider并测试接口
package com.fpa.provider.controller; import com.fpa.provider.bean.Goods; import com.fpa.provider.service.GoodsService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/goods") public class GoodsController { @Autowired private GoodsService goodsService; @GetMapping("/{id}") @HystrixCommand(fallbackMethod = "findOne_fallback")//指定降级后执行的方法名字 public Goods findOne(@PathVariable("id") int id){ //故意制造一个异常出来 // int a = 1/0; //故意制造一个超时,因为hystrix默认也是1秒超时 try { Thread.sleep(2000); }catch (Exception e){} return goodsService.findOne(id); } /** * 上面的方法一旦降级后就执行这个方法的逻辑 * @param id * @return */ public Goods findOne_fallback(int id){ Goods goods = new Goods(); goods.setTitle("服务降级了呀"); return goods; } }
进入了1秒的等待后发现超时则返回了降级后方法给的结果
-
有的接口可能查询的数据量多,超过了1秒,那我们就修改降级配置时间
commandProperties属性
@HystrixCommand(fallbackMethod = "findOne_fallback",commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")})
package com.fpa.provider.controller; import com.fpa.provider.bean.Goods; import com.fpa.provider.service.GoodsService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/goods") public class GoodsController { @Autowired private GoodsService goodsService; @GetMapping("/{id}") @HystrixCommand(fallbackMethod = "findOne_fallback",commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")})//指定降级后执行的方法名字 public Goods findOne(@PathVariable("id") int id){ //故意制造一个异常出来 // int a = 1/0; //故意制造一个超时,因为hystrix默认也是1秒超时 try { Thread.sleep(2000); }catch (Exception e){} return goodsService.findOne(id); } /** * 上面的方法一旦降级后就执行这个方法的逻辑 * @param id * @return */ public Goods findOne_fallback(int id){ Goods goods = new Goods(); goods.setTitle("服务降级了呀"); return goods; } }
-
再去测试的时候发现等了2秒后返回了正常的数据
Hystrix降级-服务消费方
- feign组件已经集成了hystrix组件。
- 定义feign调用接口实现类,复写方法,即降级方法
- 在@FeignClient注解中使用fallback属性设置降级处理类。
- 配置开启feign.hystrix.enabled = true
-
创建GoodsFeignClient接口的实现类
package com.fpa.consumer.interfaces.fallbackImpl; import com.fpa.consumer.bean.Goods; import com.fpa.consumer.interfaces.GoodsFeignClient; import org.springframework.stereotype.Component; /** * 服务消费方的降级实现 */ @Component public class GoodsFeignClientFallback implements GoodsFeignClient { @Override public Goods findOne(int id) { Goods goods = new Goods(); goods.setTitle("服务消费方降级"); return goods; } }
-
给接口配置上fallback
fallback = GoodsFeignClientFallback.class
package com.fpa.consumer.interfaces; import com.fpa.consumer.bean.Goods; import com.fpa.consumer.config.FeignLogConfig; import com.fpa.consumer.interfaces.fallbackImpl.GoodsFeignClientFallback; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(value = "provider-app",configuration = FeignLogConfig.class,fallback = GoodsFeignClientFallback.class) public interface GoodsFeignClient { @GetMapping("/goods/{id}") public Goods findOne(@PathVariable("id") int id); }
-
将consumer项目配置里的ribbon读取超时时间从3秒设置到1秒
spring: profiles: active: dev application: name: consumer-app #设置Ribbon的超时时间 ribbon: connectTimeout: 1000 #连接超时时间默认1s ReadTimeout: 1000 #逻辑处理的超时时间默认1s logging: level: com.fpa: debug
-
给consumer项目的配置里加入启用hystrix熔断器
spring: profiles: active: dev application: name: consumer-app #设置Ribbon的超时时间 ribbon: connectTimeout: 1000 #连接超时时间默认1s ReadTimeout: 1000 #逻辑处理的超时时间默认1s logging: level: com.fpa: debug # 开启服务消费方 hystrix熔断器 feign: hystrix: enabled: true
-
启动consumer,由于之前provider是2秒后才返回数据的,而consumer这边默认还是1秒钟超时,因此会触发consumer的服务降级
Hystrix熔断机制
熔断机制介绍
-
Hystrix熔断机制,用于监控微服务调用情况,当失败的情况达到预定的阈值(5秒失败20次),会打开断路器,拒绝所有请求,直到服务恢复正常为止。
失败次数达到一定阈值之后,比如5秒20次,那么开启熔断器,拒绝所有请求,全部走直接走fallback了,5秒后尝试放进来一部分请求,如果请求成功次数达到阈值则关闭熔断器,如果请求还是失败的,那继续开5秒熔断器,如此循环直到恢复。
熔断机制测试
-
测试熔断器,修改provider的接口,id为1给他报异常,其他的正常返回
package com.fpa.provider.controller; import com.fpa.provider.bean.Goods; import com.fpa.provider.service.GoodsService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/goods") public class GoodsController { @Autowired private GoodsService goodsService; @GetMapping("/{id}") @HystrixCommand(fallbackMethod = "findOne_fallback",commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")})//指定降级后执行的方法名字 public Goods findOne(@PathVariable("id") int id){ //故意制造一个异常出来 // int a = 1/0; //故意制造一个超时,因为hystrix默认也是1秒超时 /*try { Thread.sleep(2000); }catch (Exception e){}*/ //熔断器测试 if (id == 1){ int a = 1/0; } return goodsService.findOne(id); } /** * 上面的方法一旦降级后就执行这个方法的逻辑 * @param id * @return */ public Goods findOne_fallback(int id){ Goods goods = new Goods(); goods.setTitle("服务降级了呀"); return goods; } }
-
重启provider后我们去浏览器看id为1的结果和id为2的结果
为1的返回了降级的结果,为2的正常返回,现在我们疯狂刷新1的那个页面,触发熔断器,然后访问1和2发现都返回了降级的结果,而5秒之后再访问,发现又恢复了,1的还是返回降级,2则返回正常的结果
刚开始访问的:
疯狂刷新1的30次之后再访问1的和2的
过5秒之后再访问1和2
自定义熔断参数
package com.fpa.provider.controller;
import com.fpa.provider.bean.Goods;
import com.fpa.provider.service.GoodsService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsService goodsService;
@GetMapping("/{id}")
@HystrixCommand(fallbackMethod = "findOne_fallback", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),//超时时间
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000"),//监控时间黑认5000毫秒
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),//失败次数。默认20次
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")//失败率默认50%
})//指定降级后执行的方法名字
public Goods findOne(@PathVariable("id") int id) {
//故意制造一个异常出来
// int a = 1/0;
//故意制造一个超时,因为hystrix默认也是1秒超时
/*try {
Thread.sleep(2000);
}catch (Exception e){}*/
//熔断器测试
if (id == 1) {
int a = 1 / 0;
}
return goodsService.findOne(id);
}
/**
* 上面的方法一旦降级后就执行这个方法的逻辑
*
* @param id
* @return
*/
public Goods findOne_fallback(int id) {
Goods goods = new Goods();
goods.setTitle("服务降级了呀");
return goods;
}
}
Hystrix熔断监控
Hystrix熔断监控介绍
- Hystrix提供了Hystrix-dashboard功能,用于时监控微服务运行状态。
- 但是Hystrix-dashboard只能监控一个微服务。
- Netflix还提供了Turbine,进行聚合监控。
turbine示意图
Turbine聚合监控
搭建监控模块
创建hystrix-monitor模块,使用Turbine聚合监控多个Hystrix dashboard功能
-
创建hystrix-monitor模块,修改pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>spring-cloud-parent</artifactId> <groupId>com.fpa</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>hystrix-monitor</artifactId> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <!-- hystrix-dashboard --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <!-- netflix-turbine --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-turbine</artifactId> </dependency> <!-- actuator --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- netflix-eureka-client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- springboot test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
-
创建启动类
package com.fpa.monitor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; import org.springframework.cloud.netflix.turbine.EnableTurbine; @SpringBootApplication @EnableEurekaClient //启用 eureka @EnableTurbine //开启 turbine聚合功能 @EnableHystrixDashboard //开启hystrix dashboard仪表盘 public class HystrixMonitor { public static void main(String[] args) { SpringApplication.run(HystrixMonitor.class,args); } }
-
修改配置文件
application.yml
spring: profiles: active: dev application: name: hystrix-monitor
application-dev.yml
server: port: 8769 turbine: combine-host-port: true # 配置需要被监控的服务名称列表 app-config: provider-app,consumer-app cluster-name-expression: "'default'" aggregator: cluster-config: default #instanceUrl Suffix: /actuator/hystrix.stream eureka: client: service-url: defaultZone: http://localhost:8761/eureka #eureka服务器地址
修改被监控模块
-
consumer和provider都引入依赖
<!-- hystrix --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!-- hystrix-dashboard --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <!-- actuator --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
配置Bean和加入注解@EnableHystrixDashboard
package com.fpa.provider; import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; import org.springframework.context.annotation.Bean; @SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker//开启hystrix熔断器 @EnableHystrixDashboard //启用Hystrix Dashboard注解 public class ProviderApp { public static void main(String[] args) { SpringApplication.run(ProviderApp.class,args); } /** * Hystrix 聚合监控所需要配置的Bean * @return */ @Bean public ServletRegistrationBean getServlet() { HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); registrationBean.setLoadOnStartup(1); registrationBean.addUrlMappings("/actuator/hystrix.stream"); registrationBean.setName("HystrixMetricsStreamServlet"); return registrationBean; } }
package com.fpa.consumer; import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; @SpringBootApplication @EnableEurekaClient @EnableDiscoveryClient @EnableFeignClients//开启feign功能 @EnableHystrixDashboard //启用Hystrix Dashboard注解 public class ConsumerApp { public static void main(String[] args) { SpringApplication.run(ConsumerApp.class, args); } /** * Hystrix 聚合监控所需要配置的Bean * @return */ @Bean public ServletRegistrationBean getServlet() { HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet); registrationBean.setLoadOnStartup(1); registrationBean.addUrlMappings("/actuator/hystrix.stream"); registrationBean.setName("HystrixMetricsStreamServlet"); return registrationBean; } }
启动测试
-
启动eureka-server跑在8761端口
-
启动hystrix-provider、hystrix-consumer、hystrix-monitor
-
访问http://localhost:8769/hystrix
-
输入 某个服务的http://localhost:8000/actuator/hystrix.stream进入看的就是这个服务的监控
-
输入http://localhost:8769/turbine.stream看的就是所有服务的监控情况(记住聚合这个的url没有actuator前缀!!!!)
circuit Cloused意思是熔断器是关闭的,另外其他的颜色就代表着不同的请求,在右上角
本文来自博客园,作者:HumorChen99,转载请注明原文链接:https://www.cnblogs.com/HumorChen/p/18039561