不惑之年的硬件牛人转到软件自学之spring cloud:(八)实现hystrix容错机制功能及整合Feign功能
前言:笔者曾经有18年的硬件研发经验,从(1)51单片机到(2)FPGA到(3)嵌入式ARM(ARM9到CORTEX A9)全都研发设计过,产品从(1)B超的整机研发到(2)智能家居系统到(3)无线电监测机到(4)平板电脑研发到(5)路灯智能控制到(5)工业电脑均有涉及,从(1)普通的电子技术工程师到(2)副总工程师到(3)副总经理到(4)事业部总经理。。。目前已经步入不惑之年的我对于物联网技术的热衷,决定从硬件开始全面转到物联技术框架之一的spring cloud技术,把我的整个学习经历和大家一起分享,也期待在之后有更多机会和大家一起合作,探讨。
今天是:2018年4月21日(在公司加班) 研究主题:实现hystrix容错机制功能及整合Feign功能
一、实现hystrix容错机制的目的:
1、我们在很多系统框架设计的时候,由于不可避免的会出现部分服务或机器出问题,如下图:
如果用户访问服务A模块,通过WEB接口或其他方式来访问基础服务模块,并访问数据库,但由于某些原因造成数据库无法访问,那么会出现用户不断地请求服务A模块,而服务A模块则继续请求基础模块并不停连接数据库,但数据库又连接不上,如此恶行循坏就会造成整个应用网络瘫痪。因为为了很好的解决这个问题,我们用Hystrix技术通过添加延迟阈值以及容错逻辑,来帮助我们控制分布式系统间组件的交付。Hystrix通过格力服务间的访问点、停止它们之间的级联故障、提供可回退操作来实现容错。
2、就如上图所示,如果发生当前基础服务模块或数据库不可用时,服务A将对其进行“熔断”,在一定的时间内,服务A都不会再调用基础服务,以维持本身的稳定。
3、我们说的以上回退逻辑方式就是断路器的开启,而断路器开启必须满足两个条件:
第一个条件是:整个链路达到一定阈值,默认情况下,10秒内产生超过20次请求,那么这个条件实际生产过程中是可以自行设置的;比如我自己的可以服务调用者的application.yml文件中,使用如下,表示不能超过3次请求:
circuitBreaker: requestVolumeThreshold: 3
第二个条件是: 在满足第一个条件的情况下,如果请求的错误百分比大于阈值,将会打开断路器,默认为50%,而我在服务提供者的PersonController类的调用函数hello()中产生了800毫秒的延迟
@RequestMapping(value = "/hello", method = RequestMethod.GET) public String hello() throws Exception { Thread.sleep(800); return "Hello World"; }
同时在服务调用者的application.yml文件中有500毫秒的重启调用,但这样会一直调用不了服务提供者,因为时间比服务提供者时间更短,因此满足这个条件,肯定就会打开断路器了
hystrix: command: HelloClient#hello(): execution: isolation: thread: timeoutInMilliseconds: 500
以上理论一大堆,对于我做硬件的人来说太麻烦了,直接上项目实践。
二、还是沿用之前的三个项目:
1、将“cjb-feign-eureka”改为“cjb-hystrix-eureka”注册中心项目,里面该改名字的地方自己去改,反正和项目名字对应就行了,并运行这个注册中心,在8888端口:
2、将“cjb-feign-server”服务提供者改为“cjb-hystrix-server”,并运行在8081端口(注意里面由于之前要跑两个端口,会有两个主程序,但这里只用一个就行了),里面该改名字的地方自己去改,反正和项目名字对应就行了,并注册到上面那个注册中心:
3、在服务提供者“cjb-hystrix-server”我们只测试hellocjb( )函数的路径“/hello”,因此需要在这里加上上面说的延时时间800毫秒,表示这个函数是需要延时800毫秒后才能return的
@RequestMapping(value = "/hello", method = RequestMethod.GET) public String hellocjb() throws Exception{ Thread.sleep(800); return "cjb is good!"; }
其他地方就不用改了
三、改造服务调用者“cjb-hystrix-client”,将之前的“cjb-feign-client”改为:“cjb-hystrix-client”
1、为“pom.xml”中添加hystrix的依赖,又是在starter后面加这个,你懂噻
<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"> <modelVersion>4.0.0</modelVersion> <groupId>com.example.demo</groupId> <artifactId>cjb-hystrix-client</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> </dependencies> </project>
2、在主程序“CjbClient”中增加“@EnableCircuitBreaker”的注解
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.feign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker @ServletComponentScan @EnableFeignClients public class CjbClient { @LoadBalanced @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(CjbClient.class, args); } }
3、在“application.yml”中增加打开feign的hystrix的开关,并请求时间500毫秒,不超过三次重连接的设置,并注意里面会有提到:PersonClient#hellocjb()这个函数的调用:也就是在personclient类中调用hellocjb()的方法:
;server: port: 8100 spring: application: name: cjb-hystrix-client cloud: loadbalancer: retry: false eureka: instance: hostname: localhost client: serviceUrl: defaultZone: http://localhost:8888/eureka/ feign: hystrix: enabled: true hystrix: command: PersonClient#hellocjb(): execution: isolation: thread: timeoutInMilliseconds: 500 circuitBreaker: requestVolumeThreshold: 3
4、关键的地方来了,在"PersonClient.class"中增加fallback=PersonClientFallback.class的一个回退类,表示一旦出现不能访问“ cjb-hystrix-server”的话,因为之前由于延时800毫秒,服务调用者用500毫秒的调用时间肯定访问不了,因此会出现不能访问的问题,因此就会回退到PersonClientFallback.class的代码里面,完整代码如下:
package com.example.demo; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @FeignClient(name = "cjb-hystrix-server",fallback = PersonClientFallback.class) public interface PersonClient { @RequestMapping(value = "/hello",method = RequestMethod.GET) public String hellocjb(); @RequestMapping(value = "/person/{personId}",method = RequestMethod.GET) Person getPerson(@PathVariable("personId") Integer personId); }
5、新增“PersonClientFallback”继承PersonClient这个类,并输出错误提示:“cjb:fallback error!”,完整代码如下:
package com.example.demo; import org.springframework.stereotype.Component; @Component public class PersonClientFallback implements PersonClient { public String hellocjb() { return "cjb:fallback error!"; } public Person getPerson(Integer personId) { return null; } }
里面的两个方式用右键的“Genernate..”:
并修改hellocjb()方法里面的输出“cjb:fallback error!”即可。
OK,我们运行一下这个服务调用者程序,并在端口8100中运行,即可进入回退方法并会打开熔断器中:
四、紧接着我们马上实现一个监控器,也就是监控服务调用者会不会出现断路器打开的状态:
1、我们新建一个监控端叫“cjb-hystrix-dashboard”,在“pom.xml”中增加依赖actuator
<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"> <modelVersion>4.0.0</modelVersion> <groupId>com.example.demo</groupId> <artifactId>cjb-hystrix-dashboard</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>1.5.3.RELEASE</version> </dependency> </dependencies> </project>
2、在主函数中指定端口8082
package com.example.demo; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; @SpringBootApplication @EnableHystrixDashboard public class MyApplication { public static void main(String[] args) { // 设置启动的服务器端口 new SpringApplicationBuilder(MyApplication.class).properties( "server.port=8082").run(args); } }
运行程序,并输入网址:http://localhost:8082/hystrix
在上图中输入服务调用者的网址:http://localhost:8100/hystrix.stream
五、如果需要完整代码的朋友,可以加入作者QQ群:智物联的spring cloud,入群说明:spring cloud代码需求