<四>spring-cloud Hoxton.SR2 断路器hystrix搭建使用
文章目录
本地项目的基础环境
环境 | 版本 |
---|---|
jdk | 1.8.0_201 |
maven | 3.6.0 |
Spring-boot | 2.2.4.RELEASE |
Spring-cloud | Hoxton.SR2 |
本次例子是基于上一次构建的badger-spring-cloud-opfeign
项目的基础上,使用的;
《<一>spring-cloud Hoxton.SR2版 服务注册与发现–eureka搭建以及集群搭建》
《<二>spring-cloud Hoxton.SR2 负载均衡ribbon搭建使用》
《<三>spring-cloud Hoxton.SR2 负载均衡openfeign搭建使用》
具体代码信息,可以查看《码云》
Hystrix
用于处理分布式系统的延时和容错的开源库,在分布式系统里,整个链路调用不可避免的会调用失败(超时、异常……),Hystrix
能够保证在链路调用过程中出现问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性;
断路器本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要的占用,从而毕敏了故障在分布式系统中的蔓延,从而引发系统的雪崩;
1、项目搭建
1.1、新建badger-spring-cloud-opfeign-hystrix项目
使用上次构建的的badger-spring-cloud-opfeign
项目,完整复制一份;在新的项目的pom.xml文件中加入包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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.badger</groupId>
<artifactId>badger-spring-cloud-openfeign-hystrix</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>badger-spring-cloud-openfeign-hystrix</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<spring-cloud.version>Hoxton.SR2</spring-cloud.version>
</properties>
<dependencies>
<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-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- actuator监控信息 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1.2、主类上加@EnableHystrix
注解
/**
* @EnableEurekaClient 开启eureka的客户端
* @EnableFeignClients 开启feign的客户端
* @EnableHystrix 开启断路器
* @author liqi
*/
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
public class OpenfeignHystrixApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(OpenfeignHystrixApplication.class, args);
}
}
1.3、指定远程调用接口,异常后,执行的方法,有两种方式
第一种方式,实现@FeignClient
注解标注的接口里的方法,然后在@FeignClient
注解里,标注fallback属性,
feign接口代码如下:
/**
* feign接口 指定调用哪个接口
* 注解:@FeignClient 标注为feign接口 value 指向调用的模块名
* spring boot 2.0 只有一个服务端(value = "同一个名字"),只能写一个注解,
* 可以使用contextId 作为区分,而写多个@FeignClient
* fallback = DemoFeignApiImpl.class 断路器生效的回调方法
* @author liqi ,
*/
@FeignClient(value = "BADGER-SPRING-CLOUD-API", fallback = DemoFeignApiImpl.class)
public interface DemoFeignApi {
@GetMapping("/demo")
String demo();
}
断路器的回调方法如下:需要注意,加上@Component
注解,一定要把实现类加入到容器中
@Component
public class DemoFeignApiImpl implements DemoFeignApi {
@Override
public String demo() {
return "断路器生效";
}
}
第二种方式:实现feign.hystrix.FallbackFactory
回调的工厂类,传入接口的泛型,然后重写create方法,返回feign接口的实例;具体代码如下:
@Component
public class DemoApiFallbackFactory implements FallbackFactory<DemoFeignApi> {
@Override
public DemoFeignApi create(Throwable cause) {
return new DemoFeignApi() {
@Override
public String demo() {
return "断路器生效";
}
};
}
}
还需要在@FeignClient
注解的属性中,标注回调的工厂方法
/**
* feign接口 指定调用哪个接口
* 注解:@FeignClient 标注为feign接口 value 指向调用的模块名
* spring boot 2.0 只有一个服务端(value = "同一个名字"),只能写一个注解,
* 可以使用contextId 作为区分,而写多个@FeignClient
* fallback = DemoFeignApiImpl.class 断路器生效的回调方法
* fallbackFactory = fallbackFactory = DemoApiFallbackFactory.class 指定断路器回调的工厂
* @author liqi ,
*/
@FeignClient(value = "BADGER-SPRING-CLOUD-API", fallbackFactory = DemoApiFallbackFactory.class)
public interface DemoFeignApi {
@GetMapping("/demo")
String demo();
}
这两种形式,二选一就可以了。
1.4、在yaml配置文件中,开启断路器
server:
port: 7300
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
instance-id: ${spring.cloud.client.ip-address}:${server.port}
prefer-ip-address: true
spring:
application:
name: badger-spring-cloud-openfeign-hystrix
feign:
hystrix:
enabled: true
到这里,项目就搭建完成了
2、项目启动,测试使用
启动步骤如下:
- 启动eureka的服务,端口8761;
- 修改yaml的配置文件,启动端口为7000,启动服务的提供者badger-spring-cloud-api;
- 修改yaml的配置文件,启动端口为7001,启动服务的提供者badger-spring-cloud-api;
- 启动服务的消费者badger-spring-cloud-openfeign,端口为7200;
这里为了测试负载均衡,我们使用不同的端口(7000,7001)启动了两份badger-spring-cloud-api应用;
调用openfeign的服务 http://localhost:7300/feign/demo;
持续调用,会发现业务上的端口,有顺序的改变;这个也是默认的先过滤,在轮询的负载均衡算法生效了;以及远程调用badger-spring-cloud-api成功了。
我的地址是-->172.16.2.54:7000
我的地址是-->172.16.2.54:7001
我们关掉其中一个服务的提供者badger-spring-cloud-api,端口为7000的;假装远程服务调用失败,再次远程调用;会发现,会一直调用端口为7001的,断路器失效了吗?
另一个服务的提供者badger-spring-cloud-api,端口7001的,也关掉;再次远程调用;会发现,断路器生效了,跟想象中一样;
为什么会出现这个原因?其实,看过之前的文章的同学,也猜到了,就是默认的ribbon的默认的轮询算法的改变;
spring boot 1.x的时候,默认是轮询算法,请求失败,断路器直接生效
spring boot 2.x的时候,默认是过滤+轮询算法,请求失败,断路器不会立即生效,会重试
@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
if (this.propertiesFactory.isSet(IRule.class, name)) {
return this.propertiesFactory.get(IRule.class, config, name);
}
ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
rule.initWithNiwsConfig(config);
return rule;
}
大家可以直接切换成轮询算法,再测试一次,切换的方式,就是注入一个IRule接口的实例,就可以了
@Bean
public IRule myRule() {
// return new RoundRobinRule();达到的目的,用我们重新选择的随机算法替代默认的轮询。
return new RoundRobinRule();
// return new RetryRule();
}
我就不在演示了。
具体代码信息,可以查看《码云》