Hystrix
核心特性和原理
-
熔断: 连续失败次数达到阈值, (有计数器)
- 恢复:
- 半请求: 时不时试试, 提供方是否恢复; 例如随机策略, 按时间间隔策略; 重试成功后, 重置标志位/计数器
- 相互之间不通知, 其实也不能通知, 例如: 会引发广播风暴
-
降级: 返回差一点的响应; 写mq; 备用方案;
-
隔离(线程隔离): 线程数/池: 控制到具体url 的线程个数(1:46); map<url, thread_count>;
- 这里的线程 和 tomcat线程池中的是一个list 吗? 还是单独 http请求服务的线程?
- Tomcat 线程数是整个应用的个数;
-
Hystrix实现: 代理
-
自我修复:断路器打开一段时间后,会自动进入“半开”状态,可以进行打开,关闭,半开状态的转换。
使用
- Hystrix 可以脱离spring-cloud
- 依赖: 2:00, 在consumer 中配置:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
-
使用:
- 继承 HystrixCommand;
- 重写 run, getFallback, 方法
- 还要重写一个 构造方法
-
hystrix有两种执行模式:
-
execute()
: 以同步阻塞方式执行run(),调用execute()后,hystrix先创建一个新线程运行run(),接着调用程序要在execute()调用处一直阻塞着,直到run()运行完成;
-
queue()
: 以同步非阻塞方式执行run(),调用queue()就直接返回一个Future对象,同时hystrix创建一个新线程运行run(),调用程序通过Future.get()拿到run()的返回结果;
单独使用
package com.go.cn.test;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class HystrixTest extends HystrixCommand {
private static Logger logger = LoggerFactory.getLogger(HystrixCommand.class);
protected HystrixTest(HystrixCommandGroupKey group) {
super(group);
}
public static void main(String[] args) {
// 阻塞方式
/* HystrixTest hystrixTest = new HystrixTest(HystrixCommandGroupKey.Factory.asKey("ext"));
logger.info("==>>: result={}", hystrixTest.execute());*/
// 非阻塞方式
Future<String> futureResult = new HystrixTest(HystrixCommandGroupKey.Factory.asKey("ext")).queue();
String result = "";
try {
result = futureResult.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
logger.info("==>>: result={}", result);
}
@Override
protected Object run() throws Exception {
System.out.println("执行逻辑");
int i = 1 / 0;
return String.valueOf(i);
}
@Override
protected Object getFallback() {
return "getFallback:getFallback";
}
}
--- 执行结果 ---
15:16:41.825 [main] INFO com.netflix.hystrix.HystrixCommand - result=getFallback:getFallback
整合 Feign
-
也有两种:1-指定 Fallback
类;2-指定 FallbackFactory
工厂类;
-
在调用方的 类上不能加 @RequestMapping(), 需要单独写道每个方法上; 就是路径前缀需要重复写;
-
配置:
feign:
hystrix:
enabled: true
- service 和 controller: 1-指定
Fallback
类;
---service---
package com.go.cn.service;
import com.go.cn.conf.HystrixFeignBack;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
//@RequestMapping("/hystrix") // feign 有 fallback 属性时这个注解要去掉
//@FeignClient(name = "provider", configuration = FeignAuthConfiguration.class)
@FeignClient(name = "provider", contextId = "hystrix", fallback = HystrixFeignBack.class)
public interface HystrixFeignService {
@GetMapping("/hystrix/feign_hystrix")
String getPort();
}
--- 降级类 需要实现对应的Service接口 ---
package com.go.cn.service;
import org.springframework.stereotype.Component;
@Component
public class HystrixFeignBack implements HystrixFeignService {
/**
* 方法名和 feign 调用时的方法名一致
*/
@Override
public String getPort() {
return "getPort:fail_back";
}
}
--- controller ---
package com.go.cn.controller;
import com.go.cn.service.HystrixFeignService;
import com.go.cn.service.HystrixXRestTemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HystrixController {
private HystrixXRestTemplateService hystrixService;
private HystrixFeignService hystrixFeignService;
@Autowired
public void setFeignApiService(HystrixXRestTemplateService hystrixService) {
this.hystrixService = hystrixService;
}
@Autowired
public void setHystrixFeignService(HystrixFeignService hystrixFeignService) {
this.hystrixFeignService = hystrixFeignService;
}
@GetMapping("/restTemplate_hystrix")
public String getPort(){
return hystrixService.runFun();
}
@GetMapping("/feign_hystrix")
public String feign_hystrix(){
return hystrixFeignService.getPort();
}
}
package com.go.cn.service;
import feign.FeignException;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
@Component
public class HystrixFeignFallbackFactory implements FallbackFactory<HystrixFeignService> {
@Override
public HystrixFeignService create(Throwable cause) {
return new HystrixFeignService() {
@Override
public String getPort() {
if(cause instanceof FeignException.InternalServerError) {
return "远程服务报错";
}else if(cause instanceof RuntimeException) {
return "请求时异常:" + cause;
}else {
return "其他情况";
}
}
};
}
}
--- service---
package com.go.cn.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
//@RequestMapping("/hystrix") // feign 有 fallback 属性时这个注解要去掉
//@FeignClient(name = "provider", configuration = FeignAuthConfiguration.class)
//@FeignClient(name = "provider", contextId = "hystrix", fallback = HystrixFeignBack.class, fallbackFactory=HystrixFeignFallbackFactory.class)
@FeignClient(name = "provider", contextId = "hystrix", fallbackFactory=HystrixFeignFallbackFactory.class)
public interface HystrixFeignService {
@GetMapping("/hystrix/feign_hystrix")
String getPort();
}
整合RestTemplate
- 加好依赖
- 启动类加:
@EnableCircuitBreaker
- service 和 controller:
---service---
package com.go.cn.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
@Service
public class HystrixXRestTemplateService {
@Autowired
@Qualifier("restTemplate")
RestTemplate template;
@HystrixCommand(defaultFallback = "fail")
public String runFun() {
String url ="http://provider/restTemplate_hystrix";
return template.getForObject(url, String.class);
}
public String fail() {
return "rest_template:fail";
}
}
--- controller ---
package com.go.cn.controller;
import com.go.cn.service.HystrixXRestTemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HystrixController {
private HystrixXRestTemplateService hystrixService;
@Autowired
public void setFeignApiService(HystrixXRestTemplateService hystrixService) {
this.hystrixService = hystrixService;
}
@GetMapping("/restTemplate_hystrix")
public String getPort(){
return hystrixService.runFun();
}
}
信号量与线程隔离
线程池隔离
- 针对每个不同的服务维护一个线程池;
- 不同服务互不影响; 不会由于某个服务调用导致当前服务卡死;
- 缺点就是, 开心稍微有点大;
- 官方推荐此方案:
- 可以做失败策略;
- 异常的隔离;
- 异步请求, 解放 worker 线程池的阻塞;
-
信号量隔离
- 根据 URI 设置信号量; 拿不到信号量 直接 fallback;
- 更加轻量级;
常用配置
hystrix:
command:
default:
execution:
isolation:
semaphore:
# 最大并发请求数,默认10,该参数当使用ExecutionIsolationStrategy.SEMAPHORE策略时才有效。
# 如果达到最大并发请求数,请求会被拒绝。理论上选择semaphore size的原则和选择thread size一致,
# 但选用semaphore时每次执行的代码量比较小且执行速度快(ms级别),否则的话应该用thread。
maxConcurrentRequests: 20
# 隔离策略,默认是Thread, 可选Thread|Semaphore
# - 通过semaphore count来限制并发请求数,适用于无网络的高并发请求,semaphore应该占整个容器(tomcat)的线程池的一小部分;
# 通过线程数量来限制并发请求数,可以提供额外的保护,但有一定的延迟。一般用于网络调用
strategy: Semaphore # SEMAPHORE
thread:
# 发生超时是是否中断,默认 true
interruptOnTimeout: true
# 命令执行超时时间,默认1000ms
timeoutInMilliseconds: 1000
# 执行是否启用超时,默认启用 true
timeout:
enabled: true
- 使用信号量, 配置:
- 配置文件:
hystrix.command.default.execution.isolation.strategy=SEMAPHORE
hystrix 使用dashboard
- 可以是一个独立项目,也可以在某个项目里;
- 添加依赖:
<!-- 独立服务中需要的依赖 -->
<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-actuator</artifactId>
</dependency>
- 主类:
@EnableHystrixDashboard
- 被监控的服务中增加配置类:
import com.netflix.hystrix.Hystrix;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OrderActuator {
@Bean
public ServletRegistrationBean getServletRegistrationBean(){
HystrixMetricsStreamServlet mss = new HystrixMetricsStreamServlet();
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(mss);
servletRegistrationBean.setName("servletRegistrationBean");
servletRegistrationBean.setLoadOnStartup(1);
servletRegistrationBean.addUrlMappings("/hystrix.stream");
return servletRegistrationBean;
}
}
- 图形化查看: http://service_ip:port/hystirx;在其中输入框中输入 http://被监控ip:port/hystrix.stream
- 查看 hystrix.stream: http://ip:port/actuator/hystrix.stream