前面博文我们实现了一个简单的分布式架构,通过服务集群实现高可用,通过sleuth实现服务跟踪。下面通过Hystrix断路器对服务集群某接口不可用的情况进行识别和反馈,并进一步优化使用HystrixDashboard对断路器进行监控、使用Turbine对断路器监控情况进行聚合分析。
首先,我们将接口服务的消费者进行优化,添加Hystrix断路器来识别接口不可用情况并给出反馈。前面博文中的系统结构,使用zuul集群对接口进行消费,使用一个zuul工程实现balance负载均衡,那么我们首先看看zuul如何实现断路器功能。然后再依次学习一下Ribbon和Feign如何集成Hystrix断路器。
一、zuul的FallbackProvider功能
由于springCloud在不断的更新,因此不同版本的zuul中实现熔断的interface也在不断变化,我们前面博文里搭建的springCloud系统使用的是SpringCloud F版本,Springboot 2.0 版本。经过无数的坑之后,终于找到了zuul在的熔断方法。。。如果你的工程springCloud版本或springboot版本跟我不一致,下面就当没看见吧。。。
在之前做好的zuul工程中添加熔断控制类 client1fallback 实现 FallbackProvider接口,源码如下:
package com.testzuul.zuul;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@Component
public class client1fallback implements FallbackProvider {
@Override
public String getRoute() {
return "service-client1";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
System.out.println("route:"+route);
System.out.println("exception:"+cause.getMessage());
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "ok";
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("service-client1 fallback .".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
为了少走弯路,我连import都粘进来了。。。
其他内容全都没有修改,留意getRoute函数,返回值要跟application name(serviceID)保持一致,用来识别这是哪个微服务的熔断控制类。当然,这也意味着你有多少微服务,就要创建多少个熔断控制类了。
编译运行后,关闭service-client1这个服务集群中的所有服务,测试一下。
启动service-client1服务,再试一次。
zuul集成的断路机制只要是识别微服务是不是还在(刚刚识别的是service-client1这个微服务),而不是具体哪个接口的宕机,下面学习一下在Ribbon中集成Hystrix断路器来进一步处理接口级别的熔断机制。
二、Ribbon集成Hystrix断路器
pom文件中添加Hystrix起步依赖(spring-cloud-starter-netflix-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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.testribbon</groupId>
<artifactId>testribbon</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>testribbon</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>com.springcloud</groupId>
<artifactId>testspringcloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
</dependencies>
</project>
修改properties配置文件
eureka.client.serviceUrl.defaultZone=http://10.48.8.231:8761/eureka/
#eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
eureka.instance.prefer-ip-address=true
spring.zipkin.base-url=http://10.48.8.231:9411
#spring.zipkin.base-url=http://localhost:9411
#spring.sleuth.sampler.percentage=1.0
spring.sleuth.sampler.probability=1
server.port: 8751
spring.application.name=testRibbon
**application.java入口类添加注解
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableHystrix
public class TestribbonApplication {
public static void main(String[] args) {
SpringApplication.run(TestribbonApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
添加接口调用的控制类 clientControler
@RestController
public class clientControler {
@Autowired
clientService client;
@GetMapping(value = "/client1")
public String client1(@RequestParam String name) {
return client.client1Service( name );
}
@GetMapping(value = "/client2")
public String client2(@RequestParam String name) {
return client.client2Service( name );
}
}
创建接口调用的service类 clientService
@Service
public class clientService {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "client1Error")
public String client1Service(String name) {
return restTemplate.getForObject("http://SERVICE-CLIENT1/hi?name="+name,String.class);
}
public String client1Error(String name) {
return "hi,"+name+",sorry,fallback!";
}
@HystrixCommand(fallbackMethod = "client2Error")
public String client2Service(String name) {
return restTemplate.getForObject("http://SERVICE-CLIENT2/hi?name="+name,String.class);
}
public String client2Error(String name) {
return "hi,"+name+",sorry,fallback!";
}
}
编译运行后浏览器输入 http://localhost:8751/client1?name=qftest 和 http://localhost:8751/client2?name=qftest
下图是我开了client2,没开client1的效果。
三、Feign集成Hystrix断路器
Feign是自带hystrix断路器的,因此pom文件无需添加新的依赖包。SpringCloud D版本以上断路器默认是关闭的,因此修改配置文件,添加feign.hystrix.enabled=true打开断路器即可
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ eureka.instance.prefer-ip-address=true spring.zipkin.base-url=http://localhost:9411 #spring.sleuth.sampler.percentage=1.0 spring.sleuth.sampler.probability=1 server.port: 8752 spring.application.name=testFeign #启动feign自带的hystrix断路器 feign.hystrix.enabled=true
**application.java入口类添加注解
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableHystrix
控制类testcontroler也没有变化
@RestController public class testcontroler { @Autowired client1service test1; @Autowired client2service test2; @GetMapping(value = "/client1") public String client1(@RequestParam String name) { return test1.testFunction( name ); } @GetMapping(value = "/client2") public String client2(@RequestParam String name) { return test2.testFunction( name ); } }
client1service接口则不能只声明个interface函数了,要添加fallback的指定
@FeignClient(value = "service-client1",fallback = Client1serviceimpl.class) public interface client1service { @RequestMapping(value = "/hi",method = RequestMethod.GET) String testFunction(@RequestParam(value = "name") String name); }
其中,FeignClient注解指定了接口对应的微服务service-client1和熔断处理类client1serviceimpl,接口中的每个函数声明都对应着一个微服务接口,这一点没什么变化。
下面添加client1serviceimpl类实现client1service接口,其实是实现fallback。
@Component public class Client1serviceimpl implements client1service { @Override public String testFunction(String name) { return "hi,"+name+",sorry,fallback!"; } }
当feign程序调用接口时,如果接口没有反馈则会快速执行client1serviceimpl中对应的熔断处理函数。
编译运行,效果与Ribbon相同。
四、HystrixDashboard断路器监控
访问接口提示报错信息,说明我们的Hystrix断路器已生效,下面我们以zuul工程为例,通过HystrixDashboard对服务的熔断情况进行查看监控。
pom文件添加基础依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
properties配置文件中添加必要的配置信息
#配置 Hystrix Dashboard
management.server.port=9001
management.endpoints.web.base-path=/actuator
management.endpoints.web.exposure.include='*'
入口类添加注解
@EnableHystrix @EnableHystrixDashboard
重新编译执行zuul程序,浏览器调用zuul接口(反复关闭service-client1和service-client2)看到成功调用和熔断信息后就可以尝试查看熔断情况了。
浏览器输入 http://localhost:8751/hystrix 注意localhost如果不是本地测试的化换成自己的ip、8751是服务的端口号,并不是刚刚配置的management.server.port对应的端口号。如下图
输入要要看的查看的监控信息 http://localhost:9001/actuator/hystrix.stream 这个时候的端口号才是刚刚配置的9001,读取的数据地址是management.endpoints.web.base-path配置的actuator,数据流是hystrix.stream。当然这个url也可以直接粘贴到浏览器上去看具体的数据流内容。如下图:
至此我们已经可以查看某个微服务的熔断信息了,Ribbon和Feign的改造方式基本相同。但是,这种改造方式只能查看某个微服务的熔断情况,当微服务较多时呢?我们希望聚合所有的微服务,将可视化信息集中起来查看,引入下面章节。
五、Turbine断路器聚合监控
Turbine是一个独立的server,同样作为eureka client将自己注册到注册中心,通过配置监控的serviceID从注册中心获取到各service的ip,然后去拉取hystrix.stream数据流进行统一监控。
我将上面创建的Ribbon和Feign都配置了HystrixDashboard,然后启动了这两个负载均衡接口调用程序,下面新创建一个springboot工程,配置成TurbineServer实现断路器的聚合监控。
创建springboot工程命名为turbineserver,修改pom文件
<?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"> <modelVersion>4.0.0</modelVersion> <groupId>com.turbine</groupId> <artifactId>turbineserver</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>turbineserver</name> <description>Demo project for Spring Boot</description> <parent> <groupId>com.springcloud</groupId> <artifactId>testspringcloud</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency> <!-- hystrix起步依赖包 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!-- hystrix dashboard 依赖包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <!-- turbine 依赖包 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-turbine</artifactId> </dependency> </dependencies> </project>
**application.java入口类添加注解
@SpringBootApplication @EnableEurekaClient @EnableDiscoveryClient @RestController @EnableHystrix @EnableHystrixDashboard @EnableCircuitBreaker @EnableTurbine
修改application.properties配置文件,添加turbine相关配置
#配置eureka client eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ eureka.instance.prefer-ip-address=true #配置zipkin服务跟踪 spring.zipkin.base-url=http://localhost:9411 spring.sleuth.sampler.probability=1 #配置自己的端口和serviceID server.port: 9004 spring.application.name=turbine-server #配置 Hystrix Dashboard management.server.port=9003 management.endpoints.web.base-path=/actuator management.endpoints.web.exposure.include="*" management.endpoints.web.cors.allowed-origins="*" management.endpoints.web.cors.allowed-methods="*" #配置 turbine turbine.app-config=testfeign,testribbon turbine.aggregator.cluster-config=default turbine.cluster-name-expression=new String("default") turbine.combine-host-port=true turbine.instanceUrlSuffix.default=actuator/hystrix.stream
OK,编译运行看下结果
浏览器输入 http://localhost:9004/turbine.stream看一下数据流情况
浏览器输入 http://localhost:9004/hystrix
页面输入http://localhost:9004/turbine.stream
收工睡觉。