一. 项目spring-cloud-user-service-8081
1. 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2. 启动类添加注解
@EnableCircuitBreaker // 开启熔断Hystrix
3. 配合控制类使用restTemplate
@Configuration
public class RestTemplateConfiguration {
@Bean
public RestTemplate buildRestTemplate(RestTemplateBuilder restTemplateBuilder) {
System.out.println("====== RestTemplateConfiguration buildRestTemplate ======");
return restTemplateBuilder.build();
}
}
4. 测试控制类
@RestController
public class TestHystrixController {
public final static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Autowired
private RestTemplate restTemplate;
/**
* 熔断触发降级 - 请求失败
*
* @param num 个数
* @return 结果
*/
@HystrixCommand(fallbackMethod = "zswHystrixFallback", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50") })
@RequestMapping("/order/{num}")
public String queryOrder(@PathVariable Integer num) {
System.out.println("TestHystrixController queryOrder ------ num: " + num + ", 时间: "
+ simpleDateFormat.format(new Date()));
if (num % 2 == 0) {
System.out.println("num是偶数 - 不走远程访问 - 不会触发断路器逻辑");
return "num是偶数 - 不走远程访问 - 不会触发断路器逻辑";
}
System.out.println("num是奇数 - 访问远程 - 订单信息服务器8082 - 如果调不通就走断路器");
return restTemplate.getForObject("http://127.0.0.1:8082/getOrder", String.class);
}
/**
* 熔断触发降级 - 回退方案(降级处理方案)
*
* @param num 请求参数
* @return 结果
*/
public String zswHystrixFallback(Integer num) {
// 可以在此做兜底的数据处理,例如从广告服务器上查询最新的广告失败了,可以返回固定的广告信息
System.out.println("TestHystrixController zswHystrixFallback ------ num: " + num + " 时间: "
+ simpleDateFormat.format(new Date()));
return "熔断触发降级 - zswHystrixFallback - 回退方案 - 请求失败!!!";
}
/**
* 请求超时 - 触发降级 (默认的timeoutInMilliseconds , 是1000 即 1s, 1秒钟远程连接还未响应就会触发熔断)
*
* @param num 个数
* @return 结果
*/
@HystrixCommand(fallbackMethod = "zswTimeoutFallback", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"), })
@RequestMapping("/queryOrderTimeout/{num}")
public String queryOrderTimeout(@PathVariable Integer num) {
System.out.println("TestHystrixController queryOrderTimeout ------ num: " + num + ", 时间: "
+ simpleDateFormat.format(new Date()));
if (num % 2 == 0) {
System.out.println("num是偶数 - 不走远程访问 - 不会触发断路器逻辑");
return "num是偶数 - 不走远程访问 - 不会触发断路器逻辑";
}
System.out.println("num是奇数 - 访问远程 - 订单信息服务器8082 - 如果调不通就走断路器");
return restTemplate.getForObject("http://127.0.0.1:8082/getOrderTimeOut", String.class);
}
/**
* 请求超时触发降级 - 回退方案(降级处理方案)
*
* @param num 请求参数
* @return 结果
*/
public String zswTimeoutFallback(Integer num) {
// 可以在此做兜底的数据处理,例如从广告服务器上查询最新的广告失败了,可以返回固定的广告信息
System.out.println("TestHystrixController zswTimeoutFallback ------ num: " + num + " 时间: "
+ simpleDateFormat.format(new Date()));
return "请求超时触发降级 - zswTimeoutFallback - 回退方案 - 请求失败!!!";
}
/**
* 信号量隔离实现<br/>
* 不会使用Hystrix管理的线程池处理请求。使用容器(Tomcat)的线程处理请求逻辑。<br/>
* 不涉及线程切换,资源调度,上下文的转换等,相对效率高。<br/>
* 信号量隔离也会启动熔断机制。如果请求并发数超标,则触发熔断,返回fallback数据。<br/>
* commandProperties - 命令配置,HystrixPropertiesManager中的常量或字符串来配置。<br/>
* execution.isolation.strategy - 隔离的种类,<br/>
* 隔离的种类可选值只有THREAD(线程池隔离)和 SEMAPHORE(信号量隔离)。默认是THREAD线程池隔离。 <br/>
* 设置信号量隔离后,线程池相关配置失效。<br/>
* execution.isolation.semaphore.maxConcurrentRequests - 信号量最大并发数。<br/>
* 信号量最大并发数默认值是10; 常见配置500~1000。 <br/>
* 如果并发请求超过配置,其他请求进入fallback逻辑。
*/
@HystrixCommand(fallbackMethod = "semaphoreQuarantineFallback", commandProperties = {
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"), // 信号量隔离
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "100") }) // 信号量最大并发数
@RequestMapping("/queryOrderBySemaphore/{num}")
public String queryOrderSemaphore(@PathVariable Integer num) {
System.out.println("TestHystrixController queryOrderSemaphore ------ num: " + num + ", 时间: "
+ simpleDateFormat.format(new Date()));
if (num % 2 == 0) {
System.out.println("num是偶数 - 不走远程访问 - 不会触发断路器逻辑");
return "num是偶数 - 不走远程访问 - 不会触发断路器逻辑";
}
System.out.println("num是奇数 - 访问远程 - 订单信息服务器8082 - 如果调不通就走断路器");
return restTemplate.getForObject("http://127.0.0.1:8082/getOrder", String.class);
}
/**
* 信号量隔离触发降级 - 回退方案(降级处理方案)
*
* @param num 请求参数
* @return 结果
*/
public String semaphoreQuarantineFallback(Integer num) {
// 可以在此做兜底的数据处理,例如从广告服务器上查询最新的广告失败了,可以返回固定的广告信息
System.out.println("TestHystrixController semaphoreQuarantineFallback ------ num: " + num + " 时间: "
+ simpleDateFormat.format(new Date()));
return "信号量隔离触发降级 - semaphoreQuarantineFallback - 回退方案 - 请求失败!!!";
}
/**
* 线程池隔离 - 触发降级
*
* @param num 个数
* @return 结果
*/
@HystrixCommand(groupKey = "order-service", commandKey = "queryOrderByThreadPool", threadPoolKey = "order-service", threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"), //线程池大小
@HystrixProperty(name = "maxQueueSize", value = "100"), //最大队列长度
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"), //线程存活时间
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15")//拒绝请求
}, fallbackMethod = "fallbackByThreadPool")
@RequestMapping("/queryOrderByThreadPool/{num}")
public String queryOrderByThreadPool(@PathVariable Integer num) {
System.out.println("TestHystrixController queryOrderByThreadPool ------ num: " + num + ", 时间: "
+ simpleDateFormat.format(new Date()));
if (num % 2 == 0) {
System.out.println("num是偶数 - 不走远程访问 - 不会触发断路器逻辑");
return "num是偶数 - 不走远程访问 - 不会触发断路器逻辑";
}
System.out.println("num是奇数 - 访问远程 - 订单信息服务器8082 - 如果调不通就走断路器");
return restTemplate.getForObject("http://127.0.0.1:8082/getOrder", String.class);
}
/**
* 线程池隔离触发降级 - 回退方案(降级处理方案)
*
* @param num 请求参数
* @return 结果
*/
public String fallbackByThreadPool(Integer num) {
// 可以在此做兜底的数据处理,例如从广告服务器上查询最新的广告失败了,可以返回固定的广告信息
System.out.println("TestHystrixController fallbackByThreadPool ------ num: " + num + " 时间: "
+ simpleDateFormat.format(new Date()));
return "信号量隔离触发降级 - fallbackByThreadPool - 回退方案 - 请求失败!!!";
}
}
5. 启动注册中心项目,启动配置中心项目,启动user-service-8081项目,不启动order-service-8082项目,测试结果
6. 熔断状态: Closed(默认关闭), Open(失败超过阈值开启), Half-Open(自重试)
请求发起时:默认10s内20次请求,失败率超过errorThresholdPercentage设置的50%,熔断触发降级,熔断状态改为Open,
熔断开启不再走远程访问,熔断时间为sleepWindowInMilliseconds设置的5s,
从熔断开启的5s内不再发起远程连接,5s之后将熔断开关置于Half-Open状态,
再次进来请求:
若请求成功则熔断开关恢复为Closed;
若请求失败则熔断开关恢复为Open,熔断开关未Open就不能发起远程访问;
5s之后,熔断开关自动恢复为Closed,开始新一轮的统计计算;
二. Hystrix + Feign的使用
1. order-api项目修改OrderServiceFeignClient并重新打包发布到本地仓库
@FeignClient(value = "order-service", fallback = OrderServiceFeignClient.OrderServiceFeignClientFallback.class)
public interface OrderServiceFeignClient extends OrderService {
@Component
class OrderServiceFeignClientFallback implements OrderServiceFeignClient {
public final static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public String findAllList() {
System.out.println("OrderServiceFeignClient OrderServiceFeignClientFallback findAllList , 时间: "
+ simpleDateFormat.format(new Date()));
return "OrderServiceFeignClientFallback - findAllList - 熔断失败";
}
@Override
public String findById() {
System.out.println("OrderServiceFeignClient OrderServiceFeignClientFallback findById , 时间: "
+ simpleDateFormat.format(new Date()));
return "OrderServiceFeignClientFallback - findById - 熔断失败";
}
@Override
public int insertMessage(OrderDto orderDto) {
System.out.println("OrderServiceFeignClient OrderServiceFeignClientFallback insertMessage , 时间: "
+ simpleDateFormat.format(new Date()));
return -1;
}
}
}
2. spring-cloud-user-service-8081项目添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.zsw</groupId>
<artifactId>order-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
3. spring-cloud-user-service-8081项目添加配置
# feign使用熔断机制保护
feign:
hystrix:
enabled: true
4. spring-cloud-user-service-8081项目启动类加配置
@ComponentScan(basePackages = { "com.zsw.userserviceprovider", "com.zsw.openfeign.client" }) // openFeign客户端放在了服务提供方,需要扫描到交给spring管理
@EnableFeignClients(basePackages = "com.zsw.openfeign.client") // openFeign客户端
5. spring-cloud-user-service-8081项目测试控制类
@RestController
public class HystrixOpenFeignController {
@Autowired
private OrderServiceFeignClient orderServiceFeignClient;
public final static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@RequestMapping("/findAllOrderList/{num}")
public String queryOrderSemaphore(@PathVariable Integer num) {
System.out.println("HystrixOpenFeignController queryOrderSemaphore ------ num: " + num + ", 时间: "
+ simpleDateFormat.format(new Date()));
if (num % 2 == 0) {
System.out.println("num是偶数 - 不走远程访问 - 不会触发断路器逻辑");
return "num是偶数 - 不走远程访问 - 不会触发断路器逻辑";
}
System.out.println("num是奇数 - 访问远程 - 订单信息服务器8082 - 如果调不通就走断路器");
return orderServiceFeignClient.findAllList();
}
}
6. 测试
三. Hystrix + DashBoard 使用
1. 新建项目spring-cloud-hystrix-dashboard-9092
<?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>com.zsw</groupId>
<artifactId>spring-cloud-1002</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-hystrix-dashboard-9092</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-hystrix-dashboard-9092</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring:
application:
# 应用名称,
name: hystrix-dashboard-9092
# 应用服务 WEB 访问端口
server:
port: 9092
@EnableHystrixDashboard // 开启hystrix监控平台
@SpringBootApplication
public class SpringCloudHystrixDashboard9092Application {
public static void main(String[] args) {
SpringApplication.run(SpringCloudHystrixDashboard9092Application.class, args);
}
}
2. spring-cloud-user-service-8081项目配置新增(hystrix.stream)
# actuator配置
management:
endpoints:
# 开启actuator
enabled-by-default: true
web:
exposure:
# 开启: 提供refresh端点,可以使用接口主动刷新配置中心的配置文件内容
# 开启refresh刷新配置中心的配置信息, 开启hystrix监控面板
include: refresh,hystrix.stream
3. dashboard项目启动测试
4. 测试监控接口http://127.0.0.1:8081/findAllOrderList
5. 结果可能报错,原因: springboot版本问题 (我的没有报错哈)
访问http://127.0.0.1:8081/actuator/hystrix.stream正常,可以看见user-service项目不断的ping获取信息
6. 解决方法: spring-cloud-user-service-8081项目加个配置类
@Configuration
public class ZswHystrixConfiguration {
@Bean
public ServletRegistrationBean getBean() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet, "/hystrix.stream");
registrationBean.setLoadOnStartup(1);
// registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}