本章我们将通过一个简单的实例来介绍 Hystrix 的具体用法,该实例基于“Netflix Hystrix 准备工作”章节提供的服务。
在使用 Hystrix 之前,我们需要先添加 maven 依赖。项目完整 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 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.11.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.hxstrive</groupId> <artifactId>hystrix-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>hystrix-demo</name> <description>hystrix-demo</description> <properties> <java.version>8</java.version> <spring-cloud.version>Hoxton.SR8</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- spring cloud 依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter</artifactId> </dependency> <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> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </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> <finalName>service-demo</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>8</source> <target>8</target> </configuration> </plugin> </plugins> </build> </project>
添加 application.yml 配置文件,在该配置文件中配置注册中心地址。内容如下:
server: port: 8080 spring: application: name: hystrix-demo ## 服务地址 eureka: client: enabled: true service-url: # 注册中心路径,表示我们向这个注册中心注册服务,如果向多个注册中心注册,用“,”进行分隔 defaultZone: http://localhost:8761/eureka instance: hostname: localhost # 心跳间隔5s,默认30s。每一个服务配置后,心跳间隔和心跳超时时间会被保存在server端, # 不同服务的心跳频率可能不同,server端会根据保存的配置来分别探活 lease-renewal-interval-in-seconds: 5 # 心跳超时时间10s,默认90s。从client端最后一次发出心跳后, # 达到这个时间没有再次发出心跳,表示服务不可用,将它的实例从注册中心移除 lease-expiration-duration-in-seconds: 10 logging: level: root: info # 设置 RestTemplate 日志级别为 DEBUG org.springframework.web.client.RestTemplate: DEBUG
添加 RestTemplate 配置,并且在该 restTemplate() 上面添加 @LoadBalanced 注解。开启负载均衡策略功能,代码如下:
package com.hxstrive.hystrix_demo.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * RestTemplate 配置类 * @author hxstrive.com */ @Configuration public class RestTemplateConfig { // 配置一个 RestTemplate @Bean // 添加该注解后,可以使得 RestTemplate 拥有负载均衡能力 @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } }
启动类是一个简单的 Spring Boot 启动类,但是该类使用 @EnableCircuitBreaker 注解开启断路器功能。代码如下:
package com.hxstrive.hystrix_demo; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * 入库类 * @author hxstrive.com */ @RestController @SpringBootApplication // 启用断路器 @EnableCircuitBreaker public class HystrixDemoApplication { @Value("${spring.application.name}") private String appName; @Value("${server.port}") private String appPort; public static void main(String[] args) { SpringApplication.run(HystrixDemoApplication.class, args); } @GetMapping("/") public String index() { return "appName=" + appName + ", appPort=" + appPort; } }
一个简单的控制器,提供 /demo1/getAllUsers 方法,该方法调用 UserService 服务的服务,然后调用远程服务。代码如下:
package com.hxstrive.hystrix_demo.controller; import com.hxstrive.hystrix_demo.dto.CommonReturn; import com.hxstrive.hystrix_demo.entity.User; import com.hxstrive.hystrix_demo.service.UserService; 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.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * 简单控制器 * @author hxstrive.com */ @RestController @RequestMapping("/demo1") public class Demo1Controller { @Autowired private UserService userService; @GetMapping("/getAllUsers") @HystrixCommand(fallbackMethod = "fallback") public CommonReturn<List<User>> getAllUsers() { return userService.getAllUsers(); } /** * 当服务调用失败时,调用此方法 */ private CommonReturn<List<User>> fallback() { return CommonReturn.fail("网络出现问题,调用 fallback 方法"); } }
用户服务类,通过服务名(见 Eureka)调用实际服务,代码如下:
package com.hxstrive.hystrix_demo.service; import com.alibaba.fastjson.JSONObject; import com.hxstrive.hystrix_demo.dto.CommonReturn; import com.hxstrive.hystrix_demo.entity.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.List; /** * 用户服务 * @author hxstrive.com * @since 1.0.0 2024/11/13 15:20 */ @Service public class UserService { @Autowired private RestTemplate restTemplate; public CommonReturn<List<User>> getAllUsers() { // 如果你要实现负载均衡,调用地址不能写具体主机地址 // 需要将主机地址替换成对于服务的服务名,即 spring.application.name 指定的值 // 你也可以到 Eureka server 中去查看 String url = "http://SERVICE-DEMO/user/getAllUsers"; // 使用 ParameterizedTypeReference 来保留泛型信息 ParameterizedTypeReference<CommonReturn<List<User>>> typeRef = new ParameterizedTypeReference<CommonReturn<List<User>>>() {}; ResponseEntity<CommonReturn<List<User>>> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, typeRef); return responseEntity.getBody(); } // 调用根据用户ID获取用户信息的接口 public CommonReturn<User> getUserById(Long id) { String url = "http://SERVICE-DEMO/user/getUserById?id=" + id; // 使用 ParameterizedTypeReference 来保留泛型信息 ParameterizedTypeReference<CommonReturn<User>> typeRef = new ParameterizedTypeReference<CommonReturn<User>>() {}; ResponseEntity<CommonReturn<User>> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, typeRef); return responseEntity.getBody(); } // 调用创建新用户的接口 public CommonReturn<User> createUser(User user) { String url = "http://SERVICE-DEMO/user/createUser"; HttpEntity<User> requestEntity = new HttpEntity<>(user); // 使用 ParameterizedTypeReference 来保留泛型信息 ParameterizedTypeReference<CommonReturn<User>> typeRef = new ParameterizedTypeReference<CommonReturn<User>>() {}; ResponseEntity<CommonReturn<User>> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, typeRef); return responseEntity.getBody(); } // 调用更新用户信息的接口 public CommonReturn<User> updateUser(Long id, User updatedUser) { String url = "http://SERVICE-DEMO/user/updateUser?id=" + id; HttpEntity<User> requestEntity = new HttpEntity<>(updatedUser); // 使用 ParameterizedTypeReference 来保留泛型信息 ParameterizedTypeReference<CommonReturn<User>> typeRef = new ParameterizedTypeReference<CommonReturn<User>>() {}; ResponseEntity<CommonReturn<User>> responseEntity = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, typeRef); return responseEntity.getBody(); } // 调用删除用户的接口 public CommonReturn<String> deleteUser(Long id) { String url = "http://SERVICE-DEMO/user/deleteUser?id=" + id; // 使用 ParameterizedTypeReference 来保留泛型信息 ParameterizedTypeReference<CommonReturn<String>> typeRef = new ParameterizedTypeReference<CommonReturn<String>>() {}; ResponseEntity<CommonReturn<String>> responseEntity = restTemplate.exchange(url, HttpMethod.DELETE, null, typeRef); return responseEntity.getBody(); } }
通用返回对象实体,代码如下:
package com.hxstrive.hystrix_demo.dto; import lombok.Data; /** * 通用返回对象 * @author hxstrive.com */ @Data public class CommonReturn<T> { private int code; private String message; private T data; private String appName; private String port; public static <T> CommonReturn<T> success(T data) { CommonReturn<T> commonReturn = new CommonReturn<>(); commonReturn.setCode(1); commonReturn.setData(data); return commonReturn; } public static <T> CommonReturn<T> success() { return success(null); } public static <T> CommonReturn<T> fail(String message) { CommonReturn<T> commonReturn = new CommonReturn<>(); commonReturn.setCode(0); commonReturn.setMessage(message); return commonReturn; } public CommonReturn<T> ext(String appName, String port) { this.setAppName(appName); this.setPort(port); return this; } }
用户实体类,代码如下:
package com.hxstrive.hystrix_demo.entity; import lombok.Builder; import lombok.Data; import lombok.ToString; /** * 用户实体 * @author hxstrive.com */ @Data @Builder @ToString public class User { private Long id; private String name; private Integer age; public User() {} public User(Long id, String name, Integer age) { this.id = id; this.name = name; this.age = age; } }
此时,启动 HystrixEurekaServerApplication、HystrixServiceUserApplication 和 HystrixDemoApplication 服务,成功启动后服务状态如下图:
此时,浏览器访问 http://localhost:8080/demo1/getAllUsers 地址,效果如下图:
如果我们尝试将“HystrixServiceUserApplication”服务停止,再次访问 http://localhost:8080/demo1/getAllUsers 地址,效果如下图:
通过上图,已经触发了 @HystrixCommand(fallbackMethod = "fallback") 注解 fallbackMethod 方法配置的 fallback 方法。fallback 方法代码如下:
/** * 当服务调用失败时,调用此方法 */ private CommonReturn<List<User>> fallback() { return CommonReturn.fail("网络出现问题,调用 fallback 方法"); }