文章目录
七、Netflix Hystrix 服务熔断
7.1 Hystrix 简介
7.1.1 雪崩效应
在微服务架构中通常会有多个服务级联的调用,在级联调用过程中,某个下游服务如果出现故障或阻塞,很可能造成服务的消费者也处于阻塞状态,造成线程资源占用,如果此时有大量请求访问,线程资源很容易被销毁完毕,导致整个系统的瘫痪;这种现象被称为服务雪崩效应。服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。
如果上图所示:积分服务的不可用导致了订单服务的不可用,订单服务的不可用导致了商品服务的不可用;最终就跟滚雪球一样越放越大,造成雪崩效应;
7.1.2 Hystrix 简介
Hystrix的中文含义是豪猪,因其背上长满了刺,而拥有自我保护能力。
在微服务架构下,很多服务都相互依赖,如果不能对依赖的服务进行隔离,那么服务本身也有可能发生故障,Hystrix 通过 HystrixCommand 对调用进行隔离,这样可以阻止故障的连锁效应,能够让接口调用快速失败并迅速恢复正常,或者回退并优雅降级。
Hystrix 是 Netflix 针对微服务分布式系统采用的熔断保护中间件,用于防止服务的雪崩效应;
Hystrix提供的功能:
-
1)对远程调用产生的延迟和故障进行控制和保护
-
2)防止系统的服务级联瘫痪
-
3)提供回退、限流、降级等操作保护下游微服务
-
4)提供服务监控、警报等操作
7.2 Hystrix 快速入门
7.2.1 测试方法降级
1)在服务提供者提供接口(Order服务):
package com.cloud.order.controller;
import com.cloud.order.entity.Order;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@RestController
@RequestMapping("/order")
public class OrderController {
/**
* 测试hystrix远程调用
*
* @param flag
* @return
*/
@GetMapping("/testHystrix/{flag}")
public Map testHystrix(@PathVariable String flag) throws InterruptedException {
if ("0".equals(flag)) {
// 模拟异常
int i = 1 / 0;
}
if ("1".equals(flag)) {
// 模拟阻塞
Thread.sleep(1500);
}
return new HashMap() {{
put("flag", true);
put("message", "请求成功");
put("statusCode", "200");
}};
}
}
2)在服务消费者(Item服务)引入Hystrix相关依赖:
<!--hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
3)在引导类中开启Hystrix:
@EnableCircuitBreaker // 开启微服务熔断
4)在服务消费者提供降级方法:
package com.cloud.item.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.ribbon.proxy.annotation.Hystrix;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@RestController
@RequestMapping("/item")
public class ItemController {
@Autowired
private RestTemplate restTemplate;
/**
* 测试Hystrix接口
*
* @param flag
* @return
*/
@HystrixCommand(fallbackMethod = "fallBack") // 降级方法
@GetMapping("/testHystrix/{flag}")
public Map findOrderById(@PathVariable String flag) {
Map resultMap = restTemplate.getForObject("http://order-service/order/testHystrix/" + flag, Map.class);
return resultMap;
}
// 降级方法
public Map fallBack(String flag) {
Map fallBackMap = new HashMap() {{
put("flag", true);
put("message", "触发降级方法!");
put("statusCode", "400");
put("flag", flag);
}};
return fallBackMap;
}
}
访问:http://localhost:9000/item/testHystrix/0、http://localhost:9000/item/testHystrix/1
注意:降级方法的参数列表、返回值等必须和业务方法保持一致;
7.2.2 超时降级
我们在配置方法降级后,不仅是Order服务出现异常之后会触发降级方法,如果Order服务出现超时响应Feign也会触发降级方法;默认情况下,使用Feign在远程调用时,默认1秒钟,如果访问未响应则触发降级方法,在实际开发中,我们可以针对于不同的情况酌情调整;
修改Feign的超时时间:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 2000 # 修改超时时间为2s
再次访问:http://localhost:9000/item/testHystrix/1
发现请求能够正常响应;
7.3 Hystrix处理高并发策略
7.3.1 准备测试环境
- 1)修改OrderController:
package com.cloud.order.controller;
import com.cloud.order.entity.Order;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@RestController
@RequestMapping("/order")
public class OrderController {
@Value("${spring.cloud.client.ip-address}")
private String ip;
@Value("${server.port}")
private String port;
/**
* 根据id查询
*
* @param id
* @return
*/
@GetMapping("{id}")
public Map findById(@PathVariable Integer id) {
return new HashMap() {{
put("flag", true);
put("message", "查询成功" + ip + ":" + port);
put("statusCode", "200");
put("id", id);
}};
}
/**
* 查询全部
*
* @return
*/
@GetMapping
public Map findAll() throws InterruptedException {
// 模拟线程阻塞
Thread.sleep(3000);
return new HashMap() {{
put("flag", true);
put("message", "查询成功全部成功;" + ip + ":" + port);
put("statusCode", "200");
}};
}
}
- 2)修改ItemController:
@RestController
@RequestMapping("/item")
public class ItemController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/findOrderById/{orderId}")
public Map findOrderById(@PathVariable Integer orderId) {
Map resultMap = restTemplate.getForObject("http://order-service/order/" + orderId, Map.class);
return resultMap;
}
@GetMapping("/findOrderAll")
public Map findOrderAll() {
Map resultMap = restTemplate.getForObject("http://order-service/order/", Map.class);
return resultMap;
}
}
7.3.2 JMeter测试工具
7.3.2.1 JMeter简介
Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试, 它可以用于测试静态和动态资源,例如静态文件、Java 小服务程序、CGI 脚本、Java 对象、数据库、FTP 服务器, 等等。JMeter 可以用于对服务器、网络或对象模拟巨大的负载,来自不同压力类别下测试它们的强度和分析整体性能。
JMeter官网:https://jmeter.apache.org/
7.3.2.2 JMeter的使用
1)添加线程组
2)配置并发参数:
3)添加HTTP请求:
4)配置HTTP请求参数:
5)配置结果取样器:
6)启动并发请求:
与此同时,在浏览器发送一个http://localhsot:9000/item/findOrderById/1的请求(正常请求),打开F12,查看服务器的响应时间:
我们发现在高并发情况下,如果有太多的连接被其他不可用(超时)的服务占用会导致正常的服务访问也有会有问题;
7.4 服务隔离
我们刚刚使用JMeter演示了高并发场景下的雪崩效应;当有某个微服务不能够立即响应时,那么会占用当前连接资源,总的线程连接资源是有限的,如果在微服务调用链路中,有非常多的超时连接无法立即响应时,那么势必会造成其他正常服务的调用超时(因为连接都被超时调用的服务占用);为此Hystrix提供有两种的服务隔离策略,分别为线程池隔离和信号量隔离;
7.4.1 线程池隔离
没有进行线程池隔离前,项目的所有接口都运行在一个线程池中,当某个接口压力过大时容易耗尽整个线程池中的所有线程,如果这个接口出现超时或故障时,那么其他正常的接口将会一直处于等待状态;
线程池隔离将某几个接口(访问量大的)分配一个独立的线程池,如果系统流量大时,那么最多只会耗尽该线程池中的资源,对整体的项目不会有很大的影响;
- 修改ItemController:
package com.cloud.item.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.ribbon.proxy.annotation.Hystrix;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@RestController
@RequestMapping("/item")
public class ItemController {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(
// 服务组名称,用于一个服务组保护多个接口(多个接口使用的服务组名称一致即可)
groupKey = "item-orderService",
// 接口名称,默认为方法名(名称任意)
commandKey = "abc-findOrderById",
// 线程池名称
threadPoolKey = "item-orderService-findOrderByIdPool",
commandProperties = {
@HystrixProperty(
// 超时时间,默认为1000ms
name = "execution.isolation.thread.timeoutInMilliseconds",
value = "10000"
),
// 线程池隔离策略(默认值)
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "THREAD"),
},
threadPoolProperties = {
// 线程池大小
@HystrixProperty(name = "coreSize", value = "2"),
// 队列等待阈值
@HystrixProperty(name = "maxQueueSize", value = "2"),
},
fallbackMethod = "findOrderByIdFallBack" // 降级方法
)
@GetMapping("/findOrderById/{orderId}")
public Map findOrderById(@PathVariable Integer orderId) {
System.out.println(Thread.currentThread().getName());
Map resultMap = restTemplate.getForObject("http://order-service/order/" + orderId, Map.class);
return resultMap;
}
/**
* findOrderById降级方法
* @param flag
* @return
*/
public Map findOrderByIdFallBack(Integer flag) {
Map fallBackMap = new HashMap() {{
put("flag", true);
put("message", "触发降级方法!");
put("statusCode", "400");
put("flag", flag);
}};
System.out.println("触发降级方法....findOrderByIdFallBack");
return fallBackMap;
}
@HystrixCommand(
// 服务组名称(名称任意)
groupKey = "item-orderService",
// 接口名称,默认为方法名(名称任意)
commandKey = "abc-findOrderAll",
// 线程池名称
threadPoolKey = "item-orderService-findAllPool",
commandProperties = {
@HystrixProperty(
// 超时时间,默认为1000ms
name = "execution.isolation.thread.timeoutInMilliseconds",
value = "10000"
),
// 线程池隔离策略(默认值)
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "THREAD"),
},
threadPoolProperties = {
// 线程池大小
@HystrixProperty(name = "coreSize", value = "3"),
// 队列等待阈值
@HystrixProperty(name = "maxQueueSize", value = "1"),
},
fallbackMethod = "findOrderAllFallBack" // 降级方法
)
@GetMapping("/findOrderAll")
public Map findOrderAll() {
System.out.println(Thread.currentThread().getName());
Map resultMap = restTemplate.getForObject("http://order-service/order/", Map.class);
return resultMap;
}
/**
* findOrderAll 降级方法
* @return
*/
public Map findOrderAllFallBack() {
System.out.println("触发降级方法....findOrderAllFallBack");
Map fallBackMap = new HashMap() {{
put("flag", true);
put("message", "触发降级方法!");
put("statusCode", "400");
}};
return fallBackMap;
}
}
coreSize为3代表最多只能处理三个并发,maxQueueSize为1,代表请求队列中最多只能等待1个请求,也就是说这个接口最多能够处理4个并发,如果第5个并发请求来了,那么会直接触发降级方法,立即响应释放连接资源;
使用JMeter发起高并发请求findOrderAll接口,在浏览器访问findOrderById接口,查看是否快速响应;
7.4.2 信号量隔离
信号量隔离为某个接口分配具体的并发量,超出了则触发降级立即响应;
package com.cloud.item.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import com.netflix.ribbon.proxy.annotation.Hystrix;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.netflix.hystrix.HystrixProperties;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@RestController
@RequestMapping("/item")
public class ItemController {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(
commandProperties = {
@HystrixProperty(
// 超时时间,默认为1000ms
name = "execution.isolation.thread.timeoutInMilliseconds",
value = "10000"
),
// 隔离策略 默认为Thread(线程池隔离) SEMAPHORE:信号量隔离
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),
// 信号量最大并发
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "5"),
},
fallbackMethod = "findOrderByIdFallBack" // 降级方法
)
@GetMapping("/findOrderById/{orderId}")
public Map findOrderById(@PathVariable Integer orderId) {
System.out.println(Thread.currentThread().getName());
Map resultMap = restTemplate.getForObject("http://order-service/order/" + orderId, Map.class);
return resultMap;
}
/**
* findOrderById降级方法
* @param flag
* @return
*/
public Map findOrderByIdFallBack(Integer flag) {
System.out.println("触发降级方法....findOrderByIdFallBack");
Map fallBackMap = new HashMap() {{
put("flag", true);
put("message", "触发降级方法!");
put("statusCode", "400");
put("flag", flag);
}};
return fallBackMap;
}
@HystrixCommand(
commandProperties = {
@HystrixProperty(
// 超时时间,默认为1000ms
name = "execution.isolation.thread.timeoutInMilliseconds",
value = "10000"
),
// 隔离策略 默认为Thread(线程池隔离) SEMAPHORE:信号量隔离
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),
// 信号量最大并发
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "5"),
},
fallbackMethod = "findOrderAllFallBack" // 降级方法
)
@GetMapping("/findOrderAll")
public Map findOrderAll() {
System.out.println(Thread.currentThread().getName());
Map resultMap = restTemplate.getForObject("http://order-service/order/", Map.class);
return resultMap;
}
/**
* findOrderAll 降级方法
* @return
*/
public Map findOrderAllFallBack() {
System.out.println("触发降级方法....findOrderAllFallBack");
Map fallBackMap = new HashMap() {{
put("flag", true);
put("message", "触发降级方法!");
put("statusCode", "400");
}};
return fallBackMap;
}
}
7.4.3 小结
-
线程池隔离:
- 请求线程和调用线程Provider不是同一条线程;(消耗资源)
- 支持熔断,当线程池到达最大线程并且阈值已经满了时,在请求触发
fallback
降级方法; - 采用单独的线程池进行隔离
- 支持同步和异步两种方式;
- 资源消耗大,大量的线程进行上下文切换、排队、调度等
- 无法传递Http Header信息
-
信号量隔离:
- 请求线程和调用Provider线程是同一条线程
- 支持熔断,信号量达到
maxConcurrentRequests
后再请求触发fallback
降级方法; - 采用信号量令牌进行隔离
- 同步调用,不支持异步调用
- 资源消耗小
- 可以传递Http Header
-
何时采用线程池隔离?
请求并发量大,并且==调用服务耗时长==,可采用线程池隔离,保证大量容器的线程处于可用状态,不会由于服务本身原因一直处于等待或阻塞状态,快速失败返回;
- 何时采用信号量隔离?
请求并发量大,并且==调用服务耗时短==(通常是命中缓存等情况),可采用信号量隔离,这类服务的调用通常返回都较快,不会占用容器线程太长时间,而且也减少了线程切换的开销;
7.5 Hystrix Dashboard
Hystrix 仪表盘(Hystrix Dashboard),Hystrix 仪表盘主要用来监控Hystrix 的实时运行状态,通过它我们可以看到 Hystrix 的各项指标信息,如每秒请求数量,成功/失败/熔断次数等,Hystrix 仪表盘通过很好的可视化工具帮我们展示这些指标,从而快速发现系统中存在的问题进而解决它。
7.5.1 搭建Hystrix DashBoard
1)引入依赖
<dependencies>
<!--dashboard监控依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
2)编写配置
server:
port: 10102
hystrix:
dashboard:
proxy-stream-allow-list: "localhost" # 允许本机访问
3)启动类
package com.cloud.dashboard;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@EnableHystrixDashboard // 开启Hystrix仪表盘
@SpringBootApplication
public class DashBoardApplication {
public static void main(String[] args) {
SpringApplication.run(DashBoardApplication.class);
}
}
访问http://localhost:10102/hystrix
7.5.2 配置Hystrix监控
1)在服务消费者引入依赖:
<!--hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--SpringBoot监控依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2)编写配置
management:
endpoints:
web:
exposure:
include: '*' # 开放所有监控端点
访问http://localhost:9000/actuator/hystrix.stream(需要开启@EnableCircuitBreaker并提供降级方法)
我们需要访问服务下的任意一个接口之后才可以获取具体的ping信息:http://localhost:9000/item/findOrderById/1
7.5.3 使用DashBoard监控
访问http://localhost:10102/hystrix
监控面板:
修改ItemController:
package com.cloud.item.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@RestController
@RequestMapping("/item")
public class ItemController {
@Autowired
private RestTemplate restTemplate;
/**
* 测试Hystrix接口
*
* @param flag
* @return
*/
@HystrixCommand(fallbackMethod = "fallBack") // 降级方法
@GetMapping("/testHystrix/{flag}")
public Map findOrderById(@PathVariable String flag) {
Map resultMap = restTemplate.getForObject("http://order-service/order/testHystrix/" + flag, Map.class);
return resultMap;
}
// 降级方法
public Map fallBack(String flag) {
Map fallBackMap = new HashMap() {{
put("flag", true);
put("message", "触发降级方法!");
put("statusCode", "400");
put("flag", flag);
}};
return fallBackMap;
}
@HystrixCommand(fallbackMethod = "findOrderByIdFallBack")
@GetMapping("/findOrderById/{orderId}")
public Map findOrderById(@PathVariable Integer orderId) {
Map resultMap = restTemplate.getForObject("http://order-service/order/" + orderId, Map.class);
return resultMap;
}
/**
* findOrderById降级方法
* @param flag
* @return
*/
public Map findOrderByIdFallBack(Integer flag) {
Map fallBackMap = new HashMap() {{
put("flag", true);
put("message", "触发降级方法!");
put("statusCode", "400");
put("flag", flag);
}};
System.out.println("触发降级方法....findOrderByIdFallBack");
return fallBackMap;
}
@HystrixCommand(fallbackMethod = "findOrderAllFallBack")
@GetMapping("/findOrderAll")
public Map findOrderAll() {
Map resultMap = restTemplate.getForObject("http://order-service/order/", Map.class);
return resultMap;
}
/**
* findOrderAll 降级方法
* @return
*/
public Map findOrderAllFallBack() {
System.out.println("触发降级方法....findOrderAllFallBack");
Map fallBackMap = new HashMap() {{
put("flag", true);
put("message", "触发降级方法!");
put("statusCode", "400");
}};
return fallBackMap;
}
}
7.5.4 DashBoard面板参数
7.7 Hystrix断路器
7.7.1 断路器的工作原理
Hystrix 能使你的系统在出现依赖服务失效的时候,通过隔离系统所依赖的服务,防止服务级联失败,同时提供失败回退机制,更优雅地应对失效,并使你的系统能更快地从异常中恢复。了解熔断器模式请看下图:
熔断器有三个状态 CLOSED
、 OPEN
、 HALF_OPEN
熔断器默认关闭状态,当触发熔断后状态变更为OPEN
,在等待到指定的时间,Hystrix会放请求检测服务是否开启,这期间熔断器会变为 HALF_OPEN
半开启状态,熔断探测服务可用则继续变更为 CLOSED
关闭熔断器。
- 1)
Closed
:断路器处于关闭状态,closed是断路器的默认状态,,表示当前所有的请求都可以正常访问; - 2)
Open
:断路器属于打开状态,当失败请求百分比达到50%,并且请求次数不小于20次,则触发熔断,断路器会开启。 - 3)
Half Open
:断路器属于半开状态,当断路器属于open状态时会进入休眠(默认5s),之后断路器进入半开状态。此时若请求成功1次,则断路器自动关闭,否则断路器再次进入打开状态,再次进行5s的休眠;
我们发送请求:http://localhost:9000/item/testHystrix/0 20次触发熔断器
在5s内(Open状态)发送http://localhost:9000/item/testHystrix/1 请求,发现依旧被降级;
7.7.2 断路器相关参数
断路器开启添加默认为请求失败比率为50%,并且需要20次请求,当断路器打开时休眠5s后进入半开状态;我们可以通过一系列参数来修改断路器的配置;
关于断路器的配置有全局配置和方法配置两种,全局配置针对于所有的服务调用,方法配置只针对于本方法的配置,两种都配置了采取就近原则(方法配置)
全局配置:在服务消费者(Item服务)中配置:
hystrix:
command:
default:
circuitBreaker:
requestVolumeThreshold: 5 # 熔断触发的错误次数的阈值
sleepWindowInMilliseconds: 10000 # 断路器开启后多少毫秒进入半开状态
errorThresholdPercentage: 50 # 断路器统计的请求失败比率阈值
方法配置:修改testHystrix接口:
/**
* 测试Hystrix接口
*
* @param flag
* @return
*/
@HystrixCommand(
// 降级方法
fallbackMethod = "fallBack",
commandProperties = {
@HystrixProperty(
// 超时时间,默认为1000ms
name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS,
value = "10000"
),
// 熔断触发的错误次数的阈值为5次
@HystrixProperty(
name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,
value = "5"
),
// 断路器开启后3s进入半开状态
@HystrixProperty(
name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,
value = "3000"
),
// 断路器统计的请求失败比率阈值为50
@HystrixProperty(
name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,
value = "100"
)
})
@GetMapping("/testHystrix/{flag}")
public Map testHystrix(@PathVariable String flag) {
Map resultMap = restTemplate.getForObject("http://order-service/order/testHystrix/" + flag, Map.class);
return resultMap;
}
Tips:当同时配置注解和yml配置时,以注解为准
注意:断路器除了达到上面的条件会开启时,线程池隔离和信号量隔离时也会触发断路器
1)线程池隔离:超出了等待线程也会触发断路器,当线程队列中的线程正常消费完毕时,断路器关闭;
2)信号量隔离:信号量令牌全部耗尽时,如果还有新的请求来临,那么触发断路器,当请求归还信号量令牌并且令牌能完全分配给请求线程时,断路器关闭;
7.8 Feign 集成 Hystrix
7.8.1 Feign 降级
Spring Cloud Fegin中默认已为Feign整合了hystrix,所以添加Feign依赖后就不用在添加hystrix,Feign与Hystrix集成也非常的方便,我们只需要在配置文件中激活Feign即可:
- 引入feign依赖:
<!--Feign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 开启feign支持:
feign:
hystrix:
enabled: true # 开启hystrix熔断
编写远程调用接口:
package com.cloud.item.client;
import com.cloud.item.client.fallback.OrderClientFallBack;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.Map;
/**
* 远程调用接口
* name: 服务名
* fallback: 降级处理类
*/
@FeignClient(name = "order-service", fallback = OrderClientFallBack.class)
public interface OrderClient {
/**
* 测试hystrix远程调用
*
* @param flag
* @return
*/
@GetMapping("/order/testHystrix/{flag}")
public Map testHystrix(@PathVariable("flag") String flag);
/**
* 根据id查询
*
* @param id
* @return
*/
@GetMapping("/order/{id}")
public Map findById(@PathVariable Integer id);
/**
* 查询全部
*
* @return
*/
@GetMapping("/order/")
public Map findAll();
}
编写降级类:
package com.cloud.item.client.fallback;
import com.cloud.item.client.OrderClient;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @author lscl
* @version 1.0
* @intro: Order服务远程调用失败的降级处理
*/
@Component
public class OrderClientFallBack implements OrderClient {
@Override
public Map testHystrix(String flag) {
return new HashMap() {{
put("flag", true);
put("message", "触发testHystrix降级方法!");
put("statusCode", "400");
put("flag", flag);
}};
}
@Override
public Map findById(Integer id) {
return new HashMap() {{
put("flag", true);
put("message", "触发findById降级方法!");
put("statusCode", "400");
put("id", id);
}};
}
@Override
public Map findAll() {
return new HashMap() {{
put("flag", true);
put("message", "触发findAll降级方法!");
put("statusCode", "400");
}};
}
}
- 编写ItemController:
package com.cloud.item.controller;
import com.cloud.item.client.OrderClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@RestController
@RequestMapping("/item")
public class ItemController {
@Autowired
private OrderClient orderClient;
/**
* 测试Hystrix接口
*
* @param flag
* @return
*/
@GetMapping("/testHystrix/{flag}")
public Map testHystrix(@PathVariable String flag) {
Map resultMap = orderClient.testHystrix(flag);
return resultMap;
}
@GetMapping("/findOrderById/{orderId}")
public Map findOrderById(@PathVariable Integer orderId) {
Map resultMap = orderClient.findById(orderId);
return resultMap;
}
@GetMapping("/findOrderAll")
public Map findOrderAll() {
Map resultMap = orderClient.findAll();
return resultMap;
}
}
访问:http://localhost:9000/demo/testHystrix/2
7.8.2 Feign的超时配置
Feign集成了Ribbon,Feign的降级超时由Ribbon的超时时间与Hystrix的超时时间一起控制,取时间短的为准;
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000 # 修改超时时间为2s
ribbon:
ReadTimeout: 1500 # 请求处理的超时时间为1.5s
Tips:两个需要一起配置,根据取时间短的为准原则,服务调用的超时时间为:1.5s
7.8.3 Feign配置线程池隔离
1)修改OrderClient,配置降级属性
@FeignClient(
name = "order-service",
fallback = OrderClientFallBack.class,
fallbackFactory = OrderThreadIsolationConfig.class // 降级配置
)
2)编写降级配置类
package com.cloud.item.client.isolation;
import com.netflix.hystrix.*;
import feign.Feign;
import feign.Target;
import feign.hystrix.SetterFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;
/**
* @author lscl
* @version 1.0
* @intro: 调用Order服务的线程池隔离配置
*/
@Configuration
public class OrderThreadIsolationConfig {
@Bean
public SetterFactory setterFactory(){
SetterFactory setterFactory =new SetterFactory() {
@Override
public HystrixCommand.Setter create(Target<?> target, Method method) {
// 服务分组名称
String groupKey = target.name();
// 接口名称
String commandKey = Feign.configKey(target.type(), method);
// Hystrix配置
HystrixCommandProperties.Setter commandProp = HystrixCommandProperties.Setter()
// 设置线程隔离
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
// 线程池配置
HystrixThreadPoolProperties.Setter threadPoolProp = HystrixThreadPoolProperties.Setter()
// 线程池大小
.withCoreSize(3)
// 线程池最大等待阈值
.withMaxQueueSize(1);
return HystrixCommand.Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))
.andCommandPropertiesDefaults(commandProp).
andThreadPoolPropertiesDefaults(threadPoolProp);
}
};
return setterFactory;
}
}
7.8.4 Feign配置信号量隔离
1)修改OrderClient降级处理配置类
@FeignClient(
name = "order-service",
fallback = OrderClientFallBack.class,
fallbackFactory = OrderSemaphoreIsolationConfig.class // 降级配置
)
2)编写信号量配置:
package com.cloud.item.client.isolation;
import com.netflix.hystrix.*;
import feign.Feign;
import feign.Target;
import feign.hystrix.SetterFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;
/**
* @author lscl
* @version 1.0
* @intro: 调用Order服务的线程池隔离配置
*/
@Configuration
public class OrderSemaphoreIsolationConfig {
@Bean
public SetterFactory setterFactory() {
SetterFactory setterFactory = new SetterFactory() {
@Override
public HystrixCommand.Setter create(Target<?> target, Method method) {
// 服务分组名称
String groupKey = target.name();
// 接口名称
String commandKey = Feign.configKey(target.type(), method);
// Hystrix配置
HystrixCommandProperties.Setter commandProp = HystrixCommandProperties.Setter()
// 设置信号量隔离
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
// 信号量令牌个数
.withExecutionIsolationSemaphoreMaxConcurrentRequests(3);
return HystrixCommand.Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))
.andCommandPropertiesDefaults(commandProp);
}
};
return setterFactory;
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/131746.html