SpringCloud整合Hystrix
1、Hystrix简介
Hystrix是由Nefflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或第三方库,防止级联失败,从而提升系统的可用性、容错性与局部应用的弹性,是一个实现了超时机制和熔断器模式的工具类库。
2、Hystrix设计原则
- 防止任何单独的依赖耗尽资源(线程),过载立即切断并快速失败,防止排队。
- 尽可能提供回退以保护用户免受故障。
- 使用隔离技术(例如隔板、泳道和断路器模式)来限制任何一个依赖的影响。
- 通过近实时的指标,监控和告警,确保故障被及时发现。
- 通过动态修改配置属性,确保故障及时恢复。
- 防止整个依赖客户端执行失败,而不仅仅是网络通信。
3、Hystrix工作原理
- 使用命令模式将所有对外部服务(或依赖关系)的调用包装再HystrixCommand或HystrixObservableCommand对象中,并将该对象放在单独的线程中执行。
- 每个依赖都维护一个线程池(或信号量),线程池被耗尽则拒绝请求(而不是让请求排队)。
- 记录请求成功,失败,超时和线程拒绝。
- 服务错误百分比超过阀值,熔断器开关自动打开,一段时间内停止对该服务的所有请求。
- 请求失败,被拒绝,超时或熔断时执行降级逻辑。
- 近实时地讲课指标和配置的修改。
当使用Hystrix封装每个基础依赖项时,每个依赖项彼此隔离,受到延迟时发生饱和的资源的限制,并包含回退逻辑,该逻辑决定了在依赖项中发生任何类型的故障时做出什么响应。
4、Hystrix整合
1)实现eureka-server
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> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath/> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>eureka-server</artifactId> <version>1.0</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </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.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>2.6</version> <configuration> <excludes> <exclude>*.**</exclude> <exclude>*/*.xml</exclude> </excludes> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>lib/</classpathPrefix> <useUniqueVersions>false</useUniqueVersions> <mainClass>cn.kenlab.org.EurekaServerApplication</mainClass> </manifest> <manifestEntries> <Class-Path>./resources/</Class-Path> </manifestEntries> </archive> <outputDirectory>${project.build.directory}</outputDirectory> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.0.1</version> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib/</outputDirectory> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.2.0</version> <executions> <execution> <id>copy-resources</id> <phase>package</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <resources> <resource> <directory>src/main/resources</directory> </resource> </resources> <outputDirectory>${project.build.directory}/resources</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
编写启动类,添加@EnableEurekaServer和@SpringBootApplication注解:
@EnableEurekaServer @SpringBootApplication public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class,args); } }
配置文件application.properties内容:
server.port=8761 eureka.client.register-with-eureka=false eureka.client.fetch-registry=false
2)创建服务提供者
pob文件添加依赖
<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>
实现启动类,添加@EnableEurekaClient和@SpringBootApplication注解。
@SpringBootApplication @EnableEurekaClient public class UserServicerApplication { public static void main(String[] args) { SpringApplication.run(UserServicerApplication.class,args); } }
实现UserController接口:
@RestController @RequestMapping("/User") public class UserController { static Map<Integer, User> userMap = new HashMap<>(); static { //模拟数据库 User user1 = new User(1, "张三", "123456"); userMap.put(1, user1); User user2 = new User(2, "李四", "123123"); userMap.put(2, user2); } @RequestMapping(value = "/getUser",method = RequestMethod.GET) public User getUser(int id) { User user = userMap.get(id); return user; } }
配置文件application.properties内容:
eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka/ eureka.client.register-with-eureka=true eureka.client.fetch-registry=true server.port=8082 spring.application.name=service
3)在Ribbon中使用熔断器
pom中添加依赖,熔断器在spring-cloud-starter-netflix-hystrix包中。
<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>
启动类中加上@EnableHystrix注解,开启熔断器功能。
@EnableHystrix //在启动类上添加@EnableHystrix注解开启Hystrix的熔断器功能。 @SpringBootApplication @EnableEurekaClient public class ConsumerRibbonApplication { //当添加@LoadBalanced注解,就代表启动Ribbon,进行负载均衡 @LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerRibbonApplication.class,args); } }
实现HystrixRibbonController类,在需要有熔断机制的方法上添加@HystrixCommand,属性fallbackMethod是熔断时返回的方法。
@RestController @RequestMapping("/Hystrix/User") public class HystrixRibbonController { @Autowired private RestTemplate restTemplate; /** * 调用 user微服务 */ @HystrixCommand(fallbackMethod = "getDefaultUser") @RequestMapping(value = "getUser",method = RequestMethod.GET) public String getUser(Integer id) { String url = "http://service/User/getUser?id=" + id; return restTemplate.getForObject(url, String.class); } public String getDefaultUser(Integer id) { System.out.println("熔断,默认回调函数"); return "{\"id\":-1,\"name\":\"熔断用户\",\"password\":\"123456\"}"; } }
测试。正常情况下,在postman中访问192.168.1.136:8080/Hystrix/User/getUser?id=1,服务正常。如下图所示:
然后停掉service服务,再次访问192.168.1.136:8080/Hystrix/User/getUser?id=1,此时会触发熔断。运行结果如下所示:
4)在Feign中使用熔断器
pom文件中添加依赖。由于Feign依赖中已经加入了Hystrix依赖,因此,项目中添加Feign依赖后,无需添加Hstrix的其它依赖。只需要在application.properties文件中添加feign.hystrix.enabled=true,开启熔断机制即可,默认为false。
<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-openfeign</artifactId> </dependency>
启动类与普通feign启动类实现方式相同。即加上@EnableFeignClients。
@SpringBootApplication @EnableEurekaClient @EnableFeignClients public class ConsumerFeignApplication { public static void main(String[] args) { SpringApplication.run(ConsumerFeignApplication.class,args); } }
配置文件application.properties中加入feign.hystrix.enabled=true,开启熔断机制。
eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka/ eureka.client.register-with-eureka=true eureka.client.fetch-registry=true server.port=8081 spring.application.name=Feign feign.hystrix.enabled=true
实现feign客户端,调用服务提供者service模块的getUser接口,并配置fallback快速失败处理类UserFeignBackImpl。
//表示"service"的服务,指定fallback @FeignClient(value = "service",fallback = UserFeignBackImpl.class) public interface UserInterface { @RequestMapping(value = "/User/getUser",method = RequestMethod.GET) public String getUser(@RequestParam("id") int id); }
实现UserFeignBackImpl类。
@Component public class UserFeignBackImpl implements UserInterface { @Override public String getUser(int id) { return "{\"id\":-1,\"name\":\"熔断用户\",\"msg\":\"请求异常,返回熔断用户!\"}"; } }
实现外部访问接口。
@RestController @RequestMapping("/HystrixFeign/User") public class FeignHystrixController { @Autowired private UserInterface userInterface; @RequestMapping(value = "getUser",method = RequestMethod.GET) public String getUser(int id) { return userInterface.getUser(id); } }
测试方法同上。