hystrix 概述
-
是由 Netflix 开源的 分布式系统 容错 处理的开源框架
-
hystrix : 豪猪 , 浑身有刺来保护自己。
-
是一个延迟和容错库,旨在 隔离 远程系统,服务 和 第三方框架,阻止级联故障,
-
在复杂的分布式 系统中实现 恢复能力。
-
服务1 承受不住压力或 发生其他内部错误,导致机器内部资源耗尽,
-
其他服务 对1 有依赖,导致其他服务 也同样出现 请求堆积,资源占用等问题
-
整个系统出现大面积的 延迟 和 瘫痪
-
雪崩效应
-
需要一种机制 来处理 延迟和故障
-
保证 整个系统 处于 可用的状态。
设计目标:(官方)
-
通过客户端库 对延迟和故障 进行保护和控制
-
停止 级联故障
-
快速失败 和 迅速 恢复
-
回退 和 优雅地 降级
-
实时监控,告警, 和 操作控制
-
hystrix 底层 大量使用 Rxjava
Hystrix 入门案例
引入和启用
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
@EnableDiscoveryClient //服务发现的客户端
@SpringBootApplication //boot的启动类
@EnableHystrix //开启hystrix
@EnableFeignClients // 开启feignClients
public class ClientApplication {}
使用
- fallback降级:方法执行错误,也会降级
@Component
public class UserService implements IUserService{
@Override
@HystrixCommand(fallbackMethod="defaultUser")
public String getUser(String username) throws Exception {
if(username.equals("spring")) {
return "This is real user";
}else {
throw new Exception();
}
}
/**
* 出错则调用该方法返回友好错误
* @param username
* @return
*/
public String defaultUser(String username) {
return "The user does not exist in this system";
}
}
@RestController
public class TestController {
@Autowired
private IUserService userService;
@GetMapping("/getUser")
public String getUser(@RequestParam String username) throws Exception{
return userService.getUser(username);
}
}
测试
http://localhost:8888/getUser?username=spring
This is real user
http://localhost:8888/getUser?username=aaa
The user does not exist in this system
feign中使用断路器
- feign中 老版本是 默认打开的
- 新版本默认是关闭的
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>//网飞 hystrix开始组件
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId> //开始 feign组件
</dependency>
使用
@FeignClient(name = "sc-provider-service", fallback = UserServiceFallback.class)
public interface IUserService {
@RequestMapping(value = "/getUser",method = RequestMethod.GET)
public String getUser(@RequestParam("username") String username);
}
@Component //交给spring管理
public class UserServiceFallback implements IUserService{
/**
* 出错则调用该方法返回友好错误
* @param username
* @return
*/
public String getUser(String username){
return "The user does not exist in this system, please confirm username";
}
}
yaml配置和测试
applicaton.yml 开启配置
feign:
hystrix:
enabled: true
http://localhost:8888/getUser?username=spring
未开启hystrix:将会报错
ClientException: Load balancer does not have available server for client: sc-provider-service
开启hystrix,将会进入回退方法:
The user does not exist in this system, please confirm username
- 如果 feign的对象 报错(如扔个异常),也会进入 回退的方法
Hystrix Dashboard
- 仪表盘
- 一段时间内 发生的请求情况 来展示的 可视化面板
搭建hystrix
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency> -->
feign:
hystrix:
enabled: true #开启feign
management:
security:
enabled: false
endpoints:
web:
exposure:
include: hystrix.stream #打开端点
@FeignClient(name = "sc-provider-service")
public interface ProviderService {
@RequestMapping(value = "/getDashboard", method = RequestMethod.GET)
public List<String> getProviderData();
}
搭建Hystrix dashboard 工程
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrixDashboard //开启dashboard
public class HystrixDashboardApplication {
}
management:
security:
enabled: false
endpoints:
web:
exposure:
include: hystrix.stream
测试:
http://192.168.20.92:9000/hystrix #能看到Dashboard界面
访问:
http://192.168.20.92:9091/actuator/hystrix.stream #可以看到 ping data,data里是个对象
在dashboard 输入这个流。
随便访问一个网址,如:http://localhost:9091/getProviderData ,在dashboard界面即可看到这个断路器的状态。
默认集群监控: http://turbine-hostname:port/turbine.stream
指定集群监控: http://turbine-hostname:port/turbine.stream?cluster=[clusterName]
单个应用: http://hystrix-app:port/hystrix.stream
- http://192.168.20.92:9091/actuator/hystrix.stream F版的spring boot 版本 2.0 ,所有端点前面都加了actuator ,不然会访问不到。
dashboard 监控图
- 圆圈:
- 流量的大小 和 流量的健康。 绿色,黄色,橙色,红色
- 曲线
- 2分钟内流量的变化,发现流量的浮动趋势。
- 右边的数字
- 左边第一列 代表请求的成功,success
- 熔断数 short-circuited
- 错误的请求 bad request
- 超时的请求 timeout
- 线程池拒绝数 rejected
- 失败的请求 failure
- 最近10秒内错误的比率 error %
- 其他:
- host cluster 机器和集群的请求频率
- circuit 熔断器的状态 open/closed
- hosts median mean 集群下的报告,百分位延迟数
- thread pools ,线程池的指标,核心线程池指标,队列大小。
提供者配置
xml
<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-netflix-hystrix</artifactId>
</dependency> //hystrix
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency> //openfeign
</dependencies>
yaml
bootstrap.yml
server:
port: 9091
spring:
application:
name: sc-hello-serviceserver:
port: 9091
spring:
application:
name: sc-hello-service
application.yml
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/
instance:
prefer-ip-address: true #eureka
management:
security:
enabled: false #关闭security
endpoints:
web:
exposure:
include: hystrix.stream #开启hystrix端点
feign:
hystrix:
enabled: true #开启hystrix
ribbon:
ConnectTimeout: 6000 #连接超时时间
ReadTimeout: 6000 #读取超时时间
MaxAutoRetries: 0 #最大重试
MaxAutoRetriesNextServer: 0 #自动重试 对于下个server
hystrix:
command: #命令
default: #默认
execution: #执行
timeout: #超时
isolation: #隔离
thread: #线程
timeoutInMilliseconds: 15000 #hystrix命令 默认 执行 超时 隔离 线程 毫秒
#超时 在 毫秒
isolation
英 /ˌaɪsəˈleɪʃn/ 美 /ˌaɪsəˈleɪʃn/ 全球(英国)
简明 牛津 新牛津 韦氏 柯林斯 例句 百科
n. 隔离;孤立;[电] 绝缘;[化学] 离析
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableFeignClients
public class HelloServiceApplication {}
turbine 聚合 hystrix
- 聚合整个集群下的 监控状况,然后在 hystrix Dashboard中显示
xml引入
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency> #dashboard
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency> #hystrix
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency> #turbine
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency> #eureka-client
</dependencies>
main方法开启
@SpringBootApplication
@EnableDiscoveryClient
@EnableTurbine
@EnableHystrixDashboard
public class TurbineApplication {
public static void main(String[] args) {
SpringApplication.run(TurbineApplication.class, args);
}
}
yaml配置
#bootstrap.yml
server:
port: 9088
spring:
application:
name: sc-turbine-dashboard
#application.yml
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/
instance:
prefer-ip-address: true
management:
security:
enabled: false
endpoints:
web:
exposure:
include: hystrix.stream
turbine:
appConfig: sc-hello-service,sc-provider-service #需要收集 监控信息的服务名
clusterNameExpression: "'default'" #设置集群名称
测试
http://localhost:9088/hystrix (dashboard 和 turbine一个项目)
输入:http://localhost:9088/turbine.stream
之后:访问一下请求:
http://localhost:8099/getHelloService
http://localhost:9091/getProviderData
hystrix异常处理机制
-
hystrix 异常处理,有5种出错 会被 fallback所截获
- failure 执行失败,抛出异常
- timeout 超时
- short_circuited 断路器 打开
- thread_pool_rejected 线程池拒绝
- semaphore_rejected 信号量拒绝
-
bad_request 会抛出 hystrix BadRequest Exception ,非法参数
-
通过例子测试:
public class PSFallbackBadRequestExpcetion extends HystrixCommand<String>{ private static Logger log = LoggerFactory.getLogger(PSFallbackBadRequestExpcetion.class); public PSFallbackBadRequestExpcetion() { super(HystrixCommandGroupKey.Factory.asKey("GroupBRE")); } @Override protected String run() throws Exception { throw new HystrixBadRequestException("HystrixBadRequestException error"); } @Override protected String getFallback() { //通过方法 来获取异常 System.out.println(getFailedExecutionException().getMessage()); return "invoke HystrixBadRequestException fallback method: "; } } //如果抛出:throw new Exception("this command will trigger fallback"); //会被 getFallback() 触发
hystrix的配置说明
- https://github.com/Netflix/Hystrix/wiki/Configuration 官方的配置文档
hystrixCommand 命令的超时时间
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 15000
隔离策略,默认为方法名
hystrix:
command:
default:
execution:
isolation:
strategy: thread
hystrix command 是否开启超时
hystrix:
command:
default:
execution:
timeout:
enabled: true
超时是否中断 执行的操作
hystrix:
command:
default:
execution:
isolation:
thread:
interruptOnTimeout: true
设置信号量 隔离策略时,设置最大允许的请求数
hystrix:
command:
default:
execution:
isolation:
semaphore:
maxConcurrentRequests: 10
circuit breaker设置 打开 fallback 并 启动 fallback 逻辑的 错误比率
hystrix:
command:
default:
circuitBreaker:
errorThresholdPercentage: 50
当为线程隔离时,线程池 核心大小
hystrix:
threadpool:
default:
coreSize: 10
隔离模式为线程池 隔离模式时,最大线程池 大小的配置如下,
hystrix:
threadpool:
default:
maximumSize: 10
hystrix:
threadpool:
default:
allowMaximumSizeToDivergeFromCoreSize: true #(默认为 false,上面的生效,1.5.9需要配置)
- 真实环境:一般对 超时时间,线程池大小,信号量 修改
- 超时时间 为 5-10秒
- 如果配置了ribbon的超时,Ribbon的时间 应该短于 Hystrix超时时间。
- feign配置15秒,ribbon配置到10秒。
hystrix其他设置
- Hystrix 线程调整 和 计算
- 每秒请求的峰值 * 99% 的延迟百分比(请求的相应时间)+预留缓冲的值
- 30 * 0.2s=6+预留缓冲的值=10 ,预留了4个线程数
hystrix缓存
-
初始化请求上下文
public class CacheContextInterceptor implements HandlerInterceptor { //注入 hystrix上下文 private HystrixRequestContext context; @Override public boolean preHandle() {//执行前 context = HystrixRequestContext.initializeContext(); return true; } @Override public void postHandle() { } @Override //方法成功执行后 public void afterCompletion() { context.shutdown(); } }
使用类 来开启缓存
public class HelloCommand extends HystrixCommand<String>{
private RestTemplate restTemplate;
private Integer id;
public HelloCommand(RestTemplate restTemplate, Integer id) {
super(HystrixCommandGroupKey.Factory.asKey("springCloudCacheGroup"));
this.id = id;
this.restTemplate = restTemplate;
}
@Override
protected String getFallback() {
return "fallback";
}
@Override
protected String run() throws Exception {
String json = restTemplate.getForObject("http://sc-provider-service/getUser/{1}", String.class, id);
System.out.println(json);
return json;
}
@Override //同一个请求,返回同样的 键值
protected String getCacheKey() {
return String.valueOf(id);
}
//清除缓存
public static void cleanCache(Long id){
HystrixRequestCache.getInstance(
HystrixCommandKey.Factory.asKey("springCloudCacheGroup"), HystrixConcurrencyStrategyDefault.getInstance())
.clear(String.valueOf(id));
}
}
使用注解来开启缓存
-
@CacheResult
-
@CacheRemove
@Component public class HelloService implements IHelloService{ @Autowired private RestTemplate restTemplate; @CacheResult //加入缓存 @HystrixCommand public String hello(Integer id) { String json = restTemplate.getForObject("http://sc-provider-service/getUser/{1}", String.class, id); System.out.println(json); return json; } @CacheResult //使用缓存 @HystrixCommand(commandKey = "getUser") public String getUserToCommandKey(@CacheKey Integer id) { String json = restTemplate.getForObject("http://sc-provider-service/getUser/{1}", String.class, id); System.out.println(json); return json; } @CacheRemove(commandKey="getUser") //删除缓存 @HystrixCommand public String updateUser(@CacheKey Integer id) { System.out.println("删除getUser缓存"); return "update success"; } }
controller
@RestController
public class CacheController {
private static final Logger logger = LoggerFactory.getLogger(CacheController.class);
@Autowired
private IHelloService helloService;
@Autowired
private RestTemplate restTemplate;
/**
* 缓存
* @param id
* @return
*/
@RequestMapping(value = "/getUser/{id}", method = RequestMethod.GET)
public String getUserId(@PathVariable("id") Integer id) {
helloService.hello(id);
helloService.hello(id);
return "getUser success";
}
/**
* 缓存更新测试
* @param id
* @return
*/
@RequestMapping(value = "/getUserIdUpdate/{id}", method = RequestMethod.GET)
public String getUserIdUpdate(@PathVariable("id") Integer id) {
helloService.hello(id);
helloService.hello(id);
helloService.hello(5);
return "getUserIdUpdate success";
}
/**
* 继承类方式
* @param id
* @return
*/
@RequestMapping(value = "/getUserIdByExtendCommand/{id}", method = RequestMethod.GET)
public String getUserIdByExtendCommand(@PathVariable("id") Integer id) {
HelloCommand one = new HelloCommand(restTemplate,id);
one.execute();
logger.info("from cache: " + one.isResponseFromCache());
HelloCommand two = new HelloCommand(restTemplate,id);
two.execute();
logger.info("from cache: " + two.isResponseFromCache());
return "getUserIdByExtendCommand success";
}
/**
* 缓存和清除缓存
* @param id
* @return
*/
@RequestMapping(value = "/getAndUpdateUser/{id}", method = RequestMethod.GET)
public String getAndUpdateUser(@PathVariable("id") Integer id) {
//调用接口并缓存数据
helloService.getUserToCommandKey(id);
helloService.getUserToCommandKey(id);
//清除缓存
helloService.updateUser(id);
//再调用接口
helloService.getUserToCommandKey(id);
helloService.getUserToCommandKey(id);
return "getAndUpdateUser success";
}
}
- 大量重复的接口,可以利用缓存机制 有效地 减轻 请求的压力
- 开启@EnableHystrix
- 初始化 HystrixRequestContext
- 注意:要用@HystrixCommand
Hystrix Request Collapser
-
多个请求 调用 单个后端依赖做的 一种优化和节约 网络开销的方法
-
只 需要一个 线程 和 一个连接的 开销
-
实现 Hystrix 上下文的初始化 和 关闭
public class HystrixContextInterceptor implements HandlerInterceptor { private HystrixRequestContext context; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse respone, Object arg2) throws Exception { context = HystrixRequestContext.initializeContext(); return true; } @Override public void postHandle() throws Exception { } @Override public void afterCompletion(){ context.shutdown(); } }
使用
@Component
public class CollapsingService implements ICollapsingService{
@HystrixCollapser(batchMethod = "collapsingList", collapserProperties = {
@HystrixProperty(name="timerDelayInMilliseconds", value = "1000")
})
public Future<Animal> collapsing(Integer id) {
return null;
}
@HystrixCollapser(batchMethod = "collapsingListGlobal",scope = Scope.GLOBAL, collapserProperties = {
@HystrixProperty(name="timerDelayInMilliseconds", value = "10000")
})
public Future<Animal> collapsingGlobal(Integer id) {
return null;
}
@HystrixCollapser(batchMethod = "collapsingList", collapserProperties = {
@HystrixProperty(name="timerDelayInMilliseconds", value = "1000")
})
public Animal collapsingSyn(Integer id) {
// TODO Auto-generated method stub
return null;
}
@HystrixCommand
public List<Animal> collapsingList(List<Integer> animalParam) {
System.out.println("collapsingList当前线程" + Thread.currentThread().getName());
System.out.println("当前请求参数个数:" + animalParam.size());
List<Animal> animalList = new ArrayList<Animal>();
for (Integer animalNumber : animalParam) {
Animal animal = new Animal();
animal.setName("Cat - " + animalNumber);
animal.setSex("male");
animal.setAge(animalNumber);
animalList.add(animal);
}
return animalList;
}
@HystrixCommand
public List<Animal> collapsingListGlobal(List<Integer> animalParam) {
System.out.println("collapsingListGlobal当前线程" + Thread.currentThread().getName());
System.out.println("当前请求参数个数:" + animalParam.size());
List<Animal> animalList = new ArrayList<Animal>();
for (Integer animalNumber : animalParam) {
Animal animal = new Animal();
animal.setName("Dog - " + animalNumber);
animal.setSex("male");
animal.setAge(animalNumber);
animalList.add(animal);
}
return animalList;
}
}
-
@HystrixCollapser(batchMethod = "collapsingList", collapserProperties = { @HystrixProperty(name="timerDelayInMilliseconds", value = "1000") }) public Future<Animal> collapsing(Integer id) { return null; }
-
实现 Future异步返回值 的方法,配置请求合并的注解,外部通过调用这 方法 来实现 请求的 合并。
-
这个方法必须是Future异步返回值的
-
HystrixCollapser 开启请求合并,实际运行collapsingList
-
timerDelayInMilliseconds 合并 1s 内的请求,默认10ms
-
public List collapsingList
-
feign 不支持 合并请求
-
如何对所有线程 请求中的多次服务 进行合并呢
- 修改 @HystrixCollapser注解的 Scope属性,配置成Global
- scope = Scope.GLOBAL 。默认是 Request
-
当 某个时间 内有大量 或 并发的相同请求时,适合 用请求合并。
action
@RestController
public class CollapsingController {
@Autowired
private ICollapsingService collapsingService;
/**
* 请求聚合/合并
* @return
* @throws ExecutionException
* @throws InterruptedException
*/
@RequestMapping("/getAnimal")
public String getAnimal() throws Exception {
Future<Animal> user = collapsingService.collapsing(1);
Future<Animal> user2 = collapsingService.collapsing(2);
System.out.println(user.get().getName());
System.out.println(user2.get().getName());
return "Success";
}
/**
* 返回值必须是Future,否则不会进行合并/聚合
* @return
* @throws ExecutionException
* @throws InterruptedException
*/
@RequestMapping("/getAnimalSyn")
public String getAnimalSyn() throws ExecutionException, InterruptedException {
Animal user = collapsingService.collapsingSyn(1);
Animal user2 = collapsingService.collapsingSyn(2);
System.out.println(user.getName());
System.out.println(user2.getName());
return "Success";
}
/**
* 请求聚合/合并,整个应用的
* @return
* @throws ExecutionException
* @throws InterruptedException
*/
@RequestMapping("/getAnimalGolbal")
public String getAnimalGolbal() throws Exception {
Future<Animal> user = collapsingService.collapsingGlobal(1);
Future<Animal> user2 = collapsingService.collapsingGlobal(2);
System.out.println(user.get().getName());
System.out.println(user2.get().getName());
return "Success";
}
}
Hystrix 线程传递及 并发隔离
- 提供了两种隔离方式
- 信号量
- 请求的时候 会获取到一个信号量,成功拿到,继续进行请求,请求在一个线程中执行完毕
- 线程隔离
- 把请求放入线程池中执行,有可能产生线程的变化,导致线程1的上下文,在线程2拿不到。
- 信号量
新建 请求接口 和 本地线程持有对象
-
建立 ThreadLocal ,把当前请求的上下文 数据放入 本地线程变量,方便使用 及 销毁
public class HystrixThreadLocal { public static ThreadLocal<String> threadLocal = new ThreadLocal<>(); }
定义controller
@RestController
public class ThreadContextController {
//得到日志对象。
private static final Logger log = LoggerFactory.getLogger(ThreadContextController.class);
@Autowired//注入service
private IThreadContextService threadContextService;
//接口的使用
@RequestMapping(value = "/getUser/{id}", method = RequestMethod.GET)
public String getUser(@PathVariable("id") Integer id) {
//第一种测试,放入上下文对象
HystrixThreadLocal.threadLocal.set("userId : "+ id);
//第二种测试,利用RequestContextHolder放入对象测试
RequestContextHolder.currentRequestAttributes().setAttribute("userId", "userId : "+ id, RequestAttributes.SCOPE_REQUEST);
//打印线程的ID
log.info("ThreadContextController, Current thread: " + Thread.currentThread().getId());
//打印 ThreadLocal 的get
log.info("ThreadContextController, Thread local: " + HystrixThreadLocal.threadLocal.get());
//打印 request域属性
log.info("ThreadContextController, RequestContextHolder: " + RequestContextHolder.currentRequestAttributes().getAttribute("userId", RequestAttributes.SCOPE_REQUEST));
//调用
String user = threadContextService.getUser(id);
return user;
}
}
定义service
public interface IThreadContextService {
public String getUser(Integer id);
}
@Component
public class ThreadContextService implements IThreadContextService{
private static final Logger log = LoggerFactory.getLogger(ThreadContextController.class);
@HystrixCommand
public String getUser(Integer id) {
log.info("ThreadContextService, Current thread : " + Thread.currentThread().getId());
log.info("ThreadContextService, ThreadContext object : " + HystrixThreadLocal.threadLocal.get());//获得到ThreadLocal
log.info("ThreadContextService, RequestContextHolder : " + RequestContextHolder.currentRequestAttributes().getAttribute("userId", RequestAttributes.SCOPE_REQUEST).toString());//得到请求域中的对象
return "Success";
}
}
- http://localhost:3333/getUser/55555
线程隔离模式下,获取用户信息
- 如果在 getUser接口 @HystrixCommand ,会发现 当达到后台服务的时候,线程ID变了。
- 线程的隔离已经生效,是重新启动的线程进行请求的,父子线程数据传递的问题
修改隔离策略
- 使用信号量隔离,hystrix默认是线程隔离,(不推荐)
hystrix:
command:
default:
execution:
isolation:
strategy: thread
使用 HystrixConcurrencyStrategy
- 实现 wrapCallable方法
- warpCallable提供在请求执行前 包装的机会,注入自己定义的动作,比如复制线程的状态。
public class SpringCloudHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private HystrixConcurrencyStrategy delegateHystrixConcurrencyStrategy;
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
return new HystrixThreadCallable<>(callable, RequestContextHolder.getRequestAttributes(),HystrixThreadLocal.threadLocal.get());
}//传递request域和 自定义的 HystrixThreadLocal
}
public class HystrixThreadCallable<S> implements Callable<S>{
private final RequestAttributes requestAttributes;
private final Callable<S> delegate;
private String params;
public HystrixThreadCallable(Callable<S> callable, RequestAttributes requestAttributes,String params) {
this.delegate = callable;
this.requestAttributes = requestAttributes;
this.params = params;
}
@Override
public S call() throws Exception {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
HystrixThreadLocal.threadLocal.set(params);
return delegate.call();
} finally {
RequestContextHolder.resetRequestAttributes();
HystrixThreadLocal.threadLocal.remove();
}
}
}
-
计算service上写 这个注解,也可传递
@HystrixCommand public String getUser(Integer id) {}
并发策略
-
上面 registerConcurrencyStrategy 方法只能被调用一次,不然会报错,无法与其他并发策略一起。
-
把其他并发策略注入进去,达到并存,
public class SpringCloudHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy { private HystrixConcurrencyStrategy delegateHystrixConcurrencyStrategy; @Override public <T> Callable<T> wrapCallable(Callable<T> callable) { return new HystrixThreadCallable<>(callable, RequestContextHolder.getRequestAttributes(),HystrixThreadLocal.threadLocal.get()); } public SpringCloudHystrixConcurrencyStrategy() { init(); } private void init() { try { this.delegateHystrixConcurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy(); if (this.delegateHystrixConcurrencyStrategy instanceof SpringCloudHystrixConcurrencyStrategy) { return; } HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook(); HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier(); HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher(); HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy(); HystrixPlugins.reset(); HystrixPlugins.getInstance().registerConcurrencyStrategy(this); HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook); HystrixPlugins.getInstance().registerEventNotifier(eventNotifier); HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher); HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy); } catch (Exception e) { throw e; } } @Override public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixProperty<Integer> corePoolSize, HystrixProperty<Integer> maximumPoolSize, HystrixProperty<Integer> keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { return this.delegateHystrixConcurrencyStrategy.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } @Override public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties threadPoolProperties) { return this.delegateHystrixConcurrencyStrategy.getThreadPool(threadPoolKey, threadPoolProperties); } @Override public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) { return this.delegateHystrixConcurrencyStrategy.getBlockingQueue(maxQueueSize); } @Override public <T> HystrixRequestVariable<T> getRequestVariable( HystrixRequestVariableLifecycle<T> rv) { return this.delegateHystrixConcurrencyStrategy.getRequestVariable(rv); } }
Hystrix 命令注解
-
HystrixCommand
- 封装执行的代码,具有故障延迟容错,断路器,统计,是阻塞命令。和 Observable共用的。
- 默认是阻塞式,提供同步和异步两种方式。
- 方法时run
- 一个实例只能发送一条数据出去
-
HystrixObservableCommand ,
- 是一个非阻塞的调用模式
- 方法时 construct
- 可以发送多条数据
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HystrixCommand {
String groupKey() default "";//唯一标识服务分组的名称,内部根据这个键值来统计,仪表盘等,
//默认的线程划分 是根据这命令组的名称来进行的。
String commandKey() default "";//全局唯一的标识符,默认是方法名
String threadPoolKey() default "";//对服务的线程池信息进行设置,监控,缓存等用途。
String defaultFallback() default ""; //默认的fallback方法
String fallbackMethod() default "";//指定的处理回退逻辑的方法
Class<? extends Throwable>[] ignoreExceptions() default {};//HystrixBadRequestException 不会触发 fallback ,不希望哪些异常被回退,而是直接抛出。
HystrixProperty[] commandProperties() default {};//配置一些命名的属性,如隔离策略
HystrixProperty[] threadPoolProperties() default {};//配置线程池相关的属性
ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
HystrixException[] raiseHystrixExceptions() default {};
}