Hystrix 单词意为“豪猪”,浑身有刺来保护自己-
1-Hystrix 是一个延迟和容错库,可以隔离远程系统、服务和第三方库,阻止级联故障能力。
= 使用Hystrix组件,它可以解决什么问题 ?=
-1- 场景一:当服务被高并发的请求时,导致CUP、内存压力过大,
-2–其部署的软件服务会出现-延迟、响应过慢情况-,压力持续增大导致服务承受不了,
-3–资源耗尽/请求堆积/彻底宕机故障,分布下依赖的服务也会因请求不到该宕机的服务,
-4–导致成整个系统大面积的延迟或瘫痪。
-5–Hystrix组件,的机制就是来处理-延迟和故障-,并保护整个系统处于可用稳定的状态。-5-1-Hystrix组件,通过客户端库对延迟和故障进行保护和控制,
-5-2-Hystrix组件,可在分布式系统中停止级联故障;
-5-3-Hystrix组件,并在合理的情况下回退和降级操作,
-5-4-Hystrix组件,同时开启近实时监控、告警和操作控制。
-5-5-Hystrix组件底层大量使用RxJava(JVM响应式扩展技术库)。
1 > Hystrix 入门示例:
<!-- Hystrix-Server-Client 服务依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<!-- Hystrix-Server-Client 主程序Main类-->
@SpringBootApplication
@EnableHystrix <!--开启断路器-@EnableHystrix-注解-->
@EnableDiscoveryClient
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
-------------------------------------------------------------------------------------
<!-- Hystrix-Server-Client 服务-Service-层:增加@HystrixCommand和降级fallback-->
public interface IUserService {
public String getUser(String username) throws Exception;
}
@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";
}
}
--最后,浏览器URL请求访问地址路径即可。
-http://localhost:8888/getUser?username=spring //用户名正确时返回正确信息
-http://localhost:8888/getUser?username=hello //不正确时,则抛出异常,降级处理友好的提示
2 > Feign中使用Hystrix断路器 (Feign内整合的Hystrix默认是关闭了需要开始如下 ):
<!-- Hystrix-consumer-Server 项目配置依赖-->
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<!-- Hystrix-consumer-Server 项目 使用@FeignClient定义接口,配置降级回退类:
UserServiceFallback-->
@FeignClient(name = "r-provider-service", fallback = UserServiceFallback.class)
public interface IUserService {
@RequestMapping(value = "/getUser",method = RequestMethod.GET)
public String getUser(@RequestParam("username") String username);
}
-----------------------------------------------------------------------------------
<!-- Hystrix-consumer-Server 项目 创建降级Fallback类:UserServiceFallback-->
@Component
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";
}
}
server:
port: 8888
spring:
application:
name: r-consumer-service
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
<!-- Hystrix-provider-Server 项目 -->
server:
port: 7777
spring:
application:
name: r-provider-service
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/
instance:
prefer-ip-address: true
<!-- Hystrix-provider-Server 项目 -->
@RestController
public class TestController {
@RequestMapping(value = "/getUser",method = RequestMethod.GET)
public String getUser(@RequestParam("username") String username) throws Exception{
if(username.equals("spring")) {
return "This is real user";
}else {
throw new Exception();
}
}
}
--------------------------------------------------------------------------------------
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------
--最后结果运行✅
-- 1.当不启动 r-provider-service 工程,并feign.hystrix.enabled=false时,
访问 http://localhost:8888/geUser?username=hello,结果报500
-- 2.打开feign,hstrix的标识位为true,再重启访问,结果显示定义的友好提示。
这时发现Hystrix已经产生了作用了.
3 >Hystrix断路器 Dashboard 仪表盘 可视化面板
项目 | 端口 | 描述 |
---|---|---|
R-eureka-server | 8761 | 注册中心 |
R-hello-service | 9091 | 消费者 Consumer Client客户端 |
R-provider-service | 8099 | 提供者 Provider Client客户端,提供一个返回信息 |
R-hystrix-dashboard | 9000 | DashboardClient客户端,并增加Hystrix Dashboard依赖 |
<!-- 以上接口的父模块中 必须依赖 该Jar包,因为Hystrix的指标是需要端口进行支撑的,
所以要添加actuator-执行器依赖,并公开hystrix.stream端点以便能被顺利地访问到。-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 并公开hystrix.stream 端点以便能被顺利地访问到 -->
management:
security:
enabled: false
endpoints:
web:
exposure:
include: hystrix.stream
<!-- R-eureka-server 项目 所以代码-->
------------------------------------------------------------------------------------
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
------------------------------------------------------------------------------------
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://localhost:8761/eureka/
------------------------------------------------------------------------------------
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
------------------------------------------------------------------------------------
<!-- R-hello-service 项目 所有代码-->
------------------------------------------------------------------------------------
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
------------------------------------------------------------------------------------
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
feign:
hystrix:
enabled: true
ribbon:
ConnectTimeout: 6000
ReadTimeout: 6000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 0
hystrix:
command:
default:
execution:
timeout:
isolation:
thread:
timeoutInMilliseconds: 15000
Server:
port: 9091
spring:
application:
name: R-hello-service
------------------------------------------------------------------------------------
public interface IHelloService {
public List<String> getProviderData();
}
------------------------------------------------------------------------------------
@Component
public class HelloService implements IHelloService{
@Autowired
private ProviderService dataService;
@Override
public List<String> getProviderData() {
return dataService.getProviderData();
}
}
------------------------------------------------------------------------------------
/*** feign调用数据服务* 因为FeignClient绑定了Hystrix,
* 这里定义一个ProviderService的FeignClient负责调用接口:*/
@FeignClient(name = "sc-provider-service")
public interface ProviderService {
@RequestMapping(value = "/getDashboard", method = RequestMethod.GET)
public List<String> getProviderData();
}
------------------------------------------------------------------------------------
@RestController
public class HelloController {
@Autowired
private IHelloService userService;
@GetMapping("/getProviderData")
public List<String> getProviderData(){
return userService.getProviderData();
}
@RequestMapping(value = "/helloService", method = RequestMethod.GET)
public String getHelloServiceData() {
return "hello Service";
}
}
------------------------------------------------------------------------------------
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableFeignClients
public class HelloServiceApplication {
// @Bean
// @LoadBalanced
// public RestTemplate restTemplate() {
// RestTemplate restTemplate = new RestTemplate();
// return restTemplate;
// }
public static void main(String[] args) {
SpringApplication.run(HelloServiceApplication.class, args);
}
}
------------------------------------------------------------------------------------
<!-- R-provider-service 项目 所有代码-->
------------------------------------------------------------------------------------
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
------------------------------------------------------------------------------------
spring:
application:
name: sc-provider-service
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
management:
security:
enabled: false
endpoints:
web:
exposure:
include: hystrix.stream
server:
port: 8099
spring:
application:
name: R-provider-service
------------------------------------------------------------------------------------
@RestController
public class ProviderController {
@Autowired
private ConsumerService consumerService;
@GetMapping("/getDashboard")
public List<String> getProviderData(){
List<String> provider = new ArrayList<String>();
provider.add("hystrix dashboard");
return provider;
}
@GetMapping("/getHelloService")
public String getHelloService(){
return consumerService.getHelloServiceData();
}
}
------------------------------------------------------------------------------------
@FeignClient(name = "r-hello-service")
public interface ConsumerService {
@RequestMapping(value = "/helloService", method = RequestMethod.GET)
public String getHelloServiceData();
}
------------------------------------------------------------------------------------
/*** 数据服务*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableFeignClients
public class ProviderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderServiceApplication.class, args);
}
}
------------------------------------------------------------------------------------
<!-- R-hystrix-dashboard 项目 所有代码-->
------------------------------------------------------------------------------------
<dependencies>
<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-hystrix</artifactId>
</dependency> -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
------------------------------------------------------------------------------------
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
server:
port: 9000
spring:
application:
name: R-hystrix-dashboard
------------------------------------------------------------------------------------
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrixDashboard
public class HystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardApplication.class, args);
}
}
== 然后,启动Hystrix Dashboard 和 Hell-server 两个工程;
== 浏览器访问:http://localhost:9000/hystrix 即可看到Hystrix Dashboard 首页
== ==下面的步骤很关键,仔细看: == ==
3 > Turbine(风机)聚合Hystrix 断路器 -整合整个集群下的监控状态:
-1- 单个实例的Hystrix DashBoard 在整个系统和集群的情况下不是特别有用的,
-2- 需要使用/整合 Turbine 来聚合所有相关的hystrix.stream流的方案,然后在Hystrix DashBoard中显示。
-3- 搭建 Turbine :
<!-- R-turbine-dashboard 项目 所有代码-->
-- 沿用上面👆服务,并去provider-service里添加接口 -getHelloServiceData() 接口-
<dependencies>
<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-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
------------------------------------------------------------------------------------
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.app-Config 设置需要收集监控信息的服务名 -->
turbine:
appConfig: R-hello-service,R-provider-service
clusterNameExpression: "'default'"
<!-- 新增配置属性,turbine.clusterNameExpression 设置集群名称 -->
server:
port: 9088
spring:
application:
name: R-turbine-dashboard
------------------------------------------------------------------------------------
@SpringBootApplication
@EnableDiscoveryClient
@EnableTurbine<!-- 增加@EnableTurbine 注解 -->
@EnableHystrixDashboard
public class TurbineApplication {
public static void main(String[] args) {
SpringApplication.run(TurbineApplication.class, args);
}
}
4 > Hystrix 断路器 -异常机制和处理:
-1- Hystrix的异常处理中,有5种出错的情况下会被FallBack所截获,从而触发FallBack,这些情况是:
1-1-Falure:执行失败,抛出异常
1-2-Timcout:执行超时
1-3-Short_circuited:断路器打开
1-4-Thread-Pool-Rejected:线程池拒绝❌
1-5-Semaphore_Rejected:信号量拒绝❌
但 BAD_Request 这个异常不会被计入熔断,会抛出HystrixBadRequestException 异常
这中异常可以根据响应创建对应的异常进行异常封装或者直接处理,
下面👇给你同志们写个例子测试一下:
<!-- R-hystrix-exception-service 项目 所有代码-->
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
------------------------------------------------------------------------------------
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
feign:
hystrix:
enabled: true
client:
config:
sc-provider-service:
errorDecoder: cn.springcloud.book.ex.service.dataservice.FeignErrorDecoder
ribbon:
ConnectTimeout: 6000
ReadTimeout: 6000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 0
hystrix:
command:
default:
execution:
timeout:
isolation:
thread:
timeoutInMilliseconds: 15000
server:
port: 8088
spring:
application:
name: R-hystrix-exception-service
------------------------------------------------------------------------------------
<!-- R-hystrix-exception-service 项目 创建PSFallbackBadRequestExpcetion类
抛出 HystrixBadRequestException 不会被 FallBack->
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: ";
}
}
------------------------------------------------------------------------------------
<!-- R-hystrix-exception-service 项目 另外使用PSFallbackOtherExpcetion类
抛出Exception ,会被FallBack触发 -->
public class PSFallbackOtherExpcetion extends HystrixCommand<String>{
public PSFallbackOtherExpcetion() {
super(HystrixCommandGroupKey.Factory.asKey("GroupOE"));
}
@Override
protected String run() throws Exception {
throw new Exception("this command will trigger fallback");
}
<!-- 继承@HystrixCommand的命令可以通过方法来获取异常,如下:-->
@Override
protected String getFallback() {
return "invoke PSFallbackOtherExpcetion fallback method";
}
}
------------------------------------------------------------------------------------
<!-- R-hystrix-exception-service 项目 在@HystrixCommand里获取异常信息,参数里面带
Throwable 即可获得具体信息 -->
@RestController
public class ExceptionController {
private static Logger log = LoggerFactory.getLogger(ExceptionController.class);
@GetMapping("/getProviderServiceCommand")
public String providerServiceCommand(){
String result = new ProviderServiceCommand("World").execute();
return result;
}
@GetMapping("/getPSFallbackBadRequestExpcetion")
public String providerServiceFallback(){
String result = new PSFallbackBadRequestExpcetion().execute();
return result;
}
@GetMapping("/getPSFallbackOtherExpcetion")
public String pSFallbackOtherExpcetion(){
String result = new PSFallbackOtherExpcetion().execute();
return result;
}
@GetMapping("/getFallbackMethodTest")
@HystrixCommand
public String getFallbackMethodTest(String id){
throw new RuntimeException("getFallbackMethodTest failed");
}
public String fallback(String id, Throwable throwable) {
log.error(throwable.getMessage());
return "this is fallback message";
}
}
------------------------------------------------------------------------------------
<!-- 在Feign Client 中可以用ErrorDecoder实现对类异常的封装,实际使用中,很多时候调用接口会400——500之间的错误,此时可以通过封装-->
@Component
public class FeignErrorDecoder implements feign.codec.ErrorDecoder{
@Override
public Exception decode(String methodKey, Response response) {
try {
if (response.status() >= 400 && response.status() <= 499) {
String error = Util.toString(response.body().asReader());
return new HystrixBadRequestException(error);
}
} catch (IOException e) {
System.out.println(e);
}
return feign.FeignException.errorStatus(methodKey, response);
}
}
------------------------------------------------------------------------------------
public class ProviderServiceCommand extends HystrixCommand<String>{
private final String name;
public ProviderServiceCommand(String name) {
super(HystrixCommandGroupKey.Factory.asKey("GroupSC"));
this.name = name;
}
@Override
protected String run() {
return "Spring Cloud";
} @Override
protected String getFallback() {
return "Failure Spring Cloud";
}
}
------------------------------------------------------------------------------------
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableFeignClients
public class HystrixExceptionApplication{
public static void main(String[] args) {
SpringApplication.run(HystrixExceptionApplication.class, args);
}
}
5 > Hystrix 断路器 -配置说明:
Hystrix的配置比较多:
具体参考官方(https://github.com/Netflix/Hystrix/wiki/Confuguratuon).
<!-- == 奕小生,列出一些儿常见常用和可能改动的配置-(比较枯燥慢慢看喽~)== -->
'-1-隔离策略,HystrixCommandKey,如果不配置,则默认认为方法名:'
'默认值:Thread'
'默认属性:hystrix.command.default.execution.isolation.strategey'
'实例属性:hystrix.command.HystrixCommandKey.execution.islation.strategy'
------------------------------------------------------------------------------------
'-2-配置HystrixCommand 命令执行超时时间,以毫秒为单位:'
'默认值:hystrix.command.default.execution.isolation.thread.timeoutln.timeoutln.Milliseconds'
'实例属性:hystrix.command.HystrixCommandKey.execution.isolation.thread.timeoutlnMilliseconds'
------------------------------------------------------------------------------------
'-3-hystrixcommand命令执行是否开启超时:'
'默认值:true'
'默认属性:hystrix.command.default.execution.timeout.enabled'
'实例属性:hystrix.command.HystrixCommandKey.execution.timeout.enabled'
------------------------------------------------------------------------------------
'-4-超时时是否应中断执行操作:'
'默认值:true'
'默认属性:hystrix.command.default.execution.isolation.thread.interruptOnTimeout'
'实例属性:hystrix.command.HystrixCommandKey.execution.isolation.thread.interruptOnTimeout'
------------------------------------------------------------------------------------
'-5-📶信号量请求数,当设置为信号量隔离策略时,设置最大允许✅的请求数:'
'默认值:10'
'默认属性:hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests'
'实例属性:hystrix.command.HystrixCommandKey.execution.isolation.semaphore.maxConcurrentRequests'
------------------------------------------------------------------------------------
'-6-Circuit BreaKer 设置打开FallBack并启动FallBack逻辑的错误比率:'
'默认值:50'
'默认属性:hystrix.command.default.circuitBreaker.errorThresholdPercentage'
'实例属性:hystrix.command.HystrixCommandKey.circuitBreaker.errorThresholdPercentage'
------------------------------------------------------------------------------------
'-7-强制打开断路器,拒绝所有请求:'
'默认值:false'
'默认属性:hystrix.command.default.circuitBreaker.forceOpen'
'实例属性:hystrix.command.HystrixCommandKey.circutBreaker.forceOpen'
------------------------------------------------------------------------------------
'-8-当为线程隔离时,线程池核心大小:'
'默认值:10'
'默认属性:hystrix.threadpool.default.coreSize'
'实例属性:hystrix.threadpool.HystrixThreadPoolKehy.coreSize'
------------------------------------------------------------------------------------
'-9-当Htstruxge隔离策略为线程池隔离模式时,最大线程池大小的配置如下:*'
'默认值:10'
'默认属性:hystrix.threadpool.default.maximumSize'
'实例属性:hystrix.threadpool.HystrixThreadPoolKey.maximumSize'
------------------------------------------------------------------------------------
'-10-好了,👌刚好十条,在实际开发中,一般对超时时间、线程池大小、信号量等进行修改,具体要结合业务
进行分析,默认Hystrix的超时时间为1秒,可设置5~10秒左右,当然对于一个需要同步文件上传等业务则会更长,
如果配置量Ribbon的时间,器超时时间也需要和Ribbon的时间配合实用,一般情况下Ribbon的时间应
关于Hystrix超时时间。'
6 > Hystrix 断路器 -线程Thread调整和计算Compute:
-1- 在实际使用过程中会涉及很多服务,可以有些服务使用的线程池大,有些小些的,
-2- 有些服务超时时间长,有些又短,的场景/情况下,Hystrix 官方也提供类一些方法,
-3-供使用者来计算和调整这些配置,通过自我预判的配置先发布到生产或测试,
-4-然后查看它具体的运行情况,再调整为更符合业务的配置,
-5-通常的做法是:-5-1-超时时间默认认为1000ms(毫秒),如果业务明显超过1000ms,则根据自己的业务进行修改。
-5-2-线程池默认为10,如果你知道确实要使用更多时可以调整。
-5-3-金丝雀发布,如果成功则保持。
-5-4-在生产环境中运行超时24小时。
-5-5-如果系统又警告⚠️和监控,那么可以依靠它们捉问题。
-5-6-运行24小时之后,通过延迟百分位和流量来计算有意义的最低满足值。
-5-7-在生产或者测试环境中实时修改值,然后用仪表盘监控。
-5-8-如果断路器产生变化和影响,则需再次确定这个配置。
==上面👆列出的都以是一些儿比较实用的方法论,可以用来探测符合系统的配置。
7 > Hystrix 断路器 -请求缓存RequestCache:
Hystrix请求缓存是Hystrix在同一个上下文请求中缓存请求结果,它与传统理解的缓存有一定区别,
Hystrix请求缓存是在同一个请求中进行,在进行第一次调用结束后对结果缓存,然后接下来同参数的请求
将使用第一次的结果,缓存的生命周期只是在这一次请求中有效。
--使用HystrixCommand('豪猪命令')有两种方式,
--第一种是继承,第二种是直接注解。缓存也同时支持这两种,具体做法如下:
-- 再次提醒,Hystrix 的缓存在一次请求中有效,这个请求要在一个Hystrix上下文里,不然在使用缓存的时候
--Hystrix会报一个没有初始化上下文的错误,可以使用Filter过滤器和Interceptor拦截器进行初始化init。
-接下来,奕小生会使用一个拦截器来举例,实现HandlerInterceptor,分别在perHandle和 afterCompletion
-执行两段代码,另外还需要在启动上添加开启Hystrix的注解@EnableHystrix:初始化上下文。
<!-- -->
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
------------------------------------------------------------------------------------
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
feign:
hystrix:
enabled: true
ribbon:
ConnectTimeout: 6000
ReadTimeout: 6000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 0
hystrix:
command:
default:
execution:
timeout:
isolation:
thread:
timeoutInMilliseconds: 15000
server:
port: 5566
spring:
application:
name: R-hello-service
------------------------------------------------------------------------------------
<!--初始化上下文-->
public class CacheContextInterceptor 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(HttpServletRequest request, HttpServletResponse respone,
Object arg2, ModelAndView arg3)
throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse respone,
Object arg2, Exception arg3)
throws Exception {
context.shutdown();
}
}
------------------------------------------------------------------------------------
<!--关闭上下文-->
@Configuration
public class CacheConfiguration {
@Bean
@ConditionalOnClass(Controller.class)
public CacheContextInterceptor userContextInterceptor() {
return new CacheContextInterceptor();
}
@Configuration
@ConditionalOnClass(Controller.class)
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Autowired
CacheContextInterceptor userContextInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userContextInterceptor);
}
}
}
------------------------------------------------------------------------------------
<!--使用类来开启缓存-->
/**
* 使用类开启,需要继承HystrixCommand,然后重写它的getCacheKey方法即可,
* 以保证对于同一个请求返回同样的键=值,对于清除缓存
* 则调用HystrixRequestCache类的Clean方法即可:
* 代码👇下面👇
*/
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://R-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));
}
}
------------------------------------------------------------------------------------
@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";
}
}
== 然后,访问http://localhost:5566/getUserIdByExtendCOmmand/1,调用两次execute,
结果发现控制台只打印一次,并且可以用Hystrix的默认方法IsReponseFromCache来看是否来自缓存,
从打印结果来看,第二次的请求确实来自缓存数据,缓存已经成功。
> 使用注解来开启缓存-使用@CacheResult和CacheRemove即可缓存和清除缓存 <
<!-- 上面👆CacheController类里有实例代码-->
public interface IHelloService {
public String hello(Integer id);
public String getUserToCommandKey(@CacheKey Integer id);
public String updateUser(@CacheKey Integer id);
}
------------------------------------------------------------------------------------
@Component
public class HelloService implements IHelloService{
@Autowired
private RestTemplate restTemplate;
@CacheResult
@HystrixCommand
public String hello(Integer id) {
String json = restTemplate.getForObject("http://R-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://R-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";
}
}
<!-- 上面👆CacheController类里有实例代码-->
<!-- 使用注解开启缓存->
--然后,访问http://localhost:5566/getUser/1,调用两次hello结果,发现只打印出一条数据,
第二次的请求从缓存中读取了,缓存效果成功:
{"username":"Toy"."password":"123456","age":10}
<!-- 使用注解关闭缓存->
--运行结果,在没有缓存的情况下,打印一次,第二次取得缓存数据,
--然后清除缓存后又打印一次,最后一次又有了缓存。
{"username":"Toy"."password":"123456","age":10}
--删除getUser缓存
{"username":"Toy"."password":"123456","age":10}
=================================================================================
小结:
1)常用的三个注解
1-1:@CacheRestlt:使用该注解后结果会被缓存,同时,
它要和@HystrixCommand注解一起使用,注解参数为cacheKeyMethod。
1-2:@CacheRemove:清除缓存,需要指定commandKey,注解参数为commandkey、caheKeyMethod.
1-3:@CacheKey:指定请求命令参数,默认使用方法所有参数作为key,注解属性为value.
2) 一般在查询接口上使用@CaheResult,在更新接口中加上@CacheRemove删除缓存。
3) 注意事项:
3-1:在一些请求量大或者重复调用接口的,情况下-可以利用缓存机制有效地减轻请求的-压力,
-但在利用Hystrix缓存时有几个方面需要注意:
3-1-1:需要开启@EnableHystrix.
3-1-2:需要初始化HystrixRequestContext.
3-1-3:在指定HystrixCommand的CommandKey后,在@CacheRemove也要指定Commandkey.
8 > Hystrix 断路器 -Request Collapser-请求 崩溃:
Request Collapser是Hystrix 针对多个请求调用单个后端依赖做的一种优化和节约网络开销的方法。
>========================= =👆上图是引用官方请求对比🆚图================================
-如图所示:当发起5个请求时,在请求没有聚合和并合的情况下,是每个请求单独开启一个线程,并开启
-一个网络链接进行调用,这都会加重应用程序的负担和网络开销,并占用Hystrix的线程连接池,当使用
-Collapser把请求都合并起来时,则只需要一个线程和一个连接的开销,这大大减少了并发和请求执行所需要
-的线程数和网络连接数,尤其咋i一个时间段内有非常多的请求情况下能极大地提高资源利用率.
=== 下面👇使用注解方式进行请求合并 ===
<!-- 1)和缓存类似,用拦截器实现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(HttpServletRequest request, HttpServletResponse respone,
Object arg2, ModelAndView arg3)
throws Exception {}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse respone,
Object arg2, Exception arg3)
throws Exception {context.shutdown();}
}
------------------------------------------------------------------------------------
<!-- 2)实现一个Future异步返回值的方法,在这个方法上配置请求合并的注解,之后外部通过调用
--这个方法来实现请求的合并,注意,这个方法必须是Future异步返回值的,否则无法合并请求:-->
public interface ICollapsingService {
public Future<Animal> collapsing(Integer id);
public Animal collapsingSyn(Integer id);
public Future<Animal> collapsingGlobal(Integer id);}
------------------------------------------------------
@Component
public class CollapsingService implements ICollapsingService{
/**
* - @HystrixCollaper注解代表开启请求合并,调用该方法时,实际上运行的是CollapsingList方法,
* - 且利用HystrixProperty指定timerDelayInMillisecodes,这属性代表合并多少Ms内的请求,
* - 暂时配置为1000,代表1000ms,也就是1s内的请求,如果不配置的化,默认是10ms,
* - collapsingList方法如下,返回一个批量的Anaimal对象,
* - 这里需要注意的是,Feign调用的话还不支持Collapser,如下:
*/
@HystrixCollapser(batchMethod = "collapsingList", collapserProperties = {
@HystrixProperty(name="timerDelayInMilliseconds", value = "1000")})
public Future<Animal> collapsing(Integer id) {return null;}
/**
* -还有场景 在同一次请求中,如果请求两次接口都不通过线程运行的,那么合并整个应用中的请求
* 及对所以线程请求中的多次服务进行合并,需要修改属性配置:
* @HystrixCollapser注解的Scope属性,配置成GLOBAL 去实现。
* Scope(范围)属性有两个值:一个Request,也是默认的,一个是Global
*/
@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 stubreturn 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;}
}
------------------------------------------------------------------------------------
<!-- 3)写一个接口测试类,连续调用两次collapsing方法:-->
@RestController
public class CollapsingController {
@Autowired
private ICollapsingService collapsingService;
/**-
* 然后,启动工程,访问http://localhost:5555/getAnimal.
* -可以看到实际调用了CollapsingList ,输出当前线程名、请求的参数和结果,
* -一共合并了两个请求,达到预期目的。
*/
/** * 请求聚合/合并* @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";}
/**
* 码完下面👇这个方法后:
* 先瞅瞅变化,打开浏览器连续调用两次http://localhost:5555/getAnimalGolbal,
* 运行结果就出来会把,所以请求合并到一次线程中,
* 但 Request,作用域会运行两次线程来分别运行两次请求。
* /
/** * 请求聚合/合并,整个应用的 * @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";}
}
public class Animal {
private String name;
private String sex;
private int age;
public Animal() {}
public Animal(String name,String sex,int age) {
this.name = name;
this.sex = sex;
this.age = age;}}
==============================================================================
代码有些混乱总结一下:
Hystrix Request Collapser主要用于请求合并的场景,所以对于请求合并,我们一般使用的场景是:
-当在某个时间内有大量或并发的相同请求时,则适合用于请求合并,而如果在某个时间内只有很少的请求,
且延迟也不高,则使用请求合并反而增加复杂度和延时,因为对于Collapser本身Hystrix也是需要时间
--进行批处理的.
9 > Hystrix 断路器 -ThreadTransmit线程传递及Concurrency Strategy 并发策略:
- Hystrix 会对请求进行封装,然后管理请求的调用,从而实现断路器等多功能。
- Hystrix 提供来两种隔离模式来进行请求的操作,一种是信号量📶,一种是线程隔离。
- 如果是信号量,则Hystrix在请求的时候会获取到一个信号量,如果拿到,则继续进行请求,请求在一个线程中执行完毕。
- 如果是线程隔离,Hystrix 会把请求放入线程池中执行,这时就有可能产生线程的变化,从而导致线程1的上下文数据在线程2里能不能正确拿到。
*举个例子吧:
<!---->
server:
port: 3333
spring:
application:
name: R-hystrix-thread-service
------------------------------------------------------------------------------------
/**
* 新建请求接口和本地线程持有对象
* 建立一个TrheadLocal 来保存用户的信息,当作当前请求的上下文数据放入本地线程变量,
* 便于方便使用与销毁
*/
public class HystrixThreadLocal {
public static ThreadLocal<String> threadLocal = new ThreadLocal<>();
}
------------------------------------------------------------------------------------
/**
* 这里定义一个接口,做两件事:
* 1)请求入口打印当前线程ID,并利用上面的ThredLocal放入用户信息。
* 2)为了兼容其他情况,在使用Feign调用的时候,会使用RequestContextHolder拿到上下文属性。
* 下面👇测试代码:
*/
@RestController
public class ThreadContextController {
private static final Logger log =
LoggerFactory.getLogger(ThreadContextController.class);
@Autowired
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);
log.info("ThreadContextController, Current thread: " +
Thread.currentThread().getId());
log.info("ThreadContextController, Thread local: " +
HystrixThreadLocal.threadLocal.get());
log.info("ThreadContextController, RequestContextHolder: " +
RequestContextHolder.currentRequestAttributes().getAttribute("userId",
RequestAttributes.SCOPE_REQUEST));
//调用
String user = threadContextService.getUser(id);
return user;
}
}
<!-- 启动程序,运行http://localhost:3333/getUser/5555
-- 可以看到线程的id都是一样的,线程变量也是传入5555,请求上下文的持有对象也可以顺利拿到。-->
------------------------------------------------------------------------------------
/**
* 测试没有线程池隔离模式下,获取用户信息
* 定义一个后台服务获取之前放入的用户ID,获取用户信息
*/
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());
log.info("ThreadContextService, RequestContextHolder : " +
RequestContextHolder.currentRequestAttributes().getAttribute("userId",
RequestAttributes.SCOPE_REQUEST).toString());
return "Success";
}
}
<!-- 测试有线程池隔离模式下,获取用户信息,在上面👆的getUser接口加上@HystrixCommand 利用Hystrix接管。
启动程序,运行http://localhost:3333/getUser/5555
-- 会发现,进入的线程ID是**,当达到后台服务的时候,线程id变成**,说明线程池的隔离效果已经生效了,
--重新启动的线程进行请求的,然后线程的变量也都丢了,RequestContextHolder中也报错了,
-大概的意思是没有线程变量绑定,成功地重现了父子线程数据传递的问题。-->
----------------------------------------------------------------------------------
*解决方案:
根据上面👆测试出来的问题,解决此问题的两种方法:
1)修改Hystrix隔离策略,使用信号量,直接修改配置文件,但是Hystrix默认是线程池隔离。
–属性:hystrix.command.defalult.execution.isolation.strategy.
2) Hystrix 官方推荐了一种方式,就是使用 HystrixConcurrencyStrategy(断路器并发策略).
–官方描述说:使用HystrixConcurrencyStratergy实现wrapCallable(可调用的包)方法,对于依赖–ThreadLocal状态以
–实现应用程序功能的系统至关重要,也是就是说使用HystrixConcurrencyStrategy(断路器并发策略)
–覆盖wrapCallable(可调用的包)方法即可。
官方地址:https://github.com/Netflix/Hystrix/wiki/Plugins#concurrency-strategy
–wrapCallable(可调用的包)方法,上面的注解的意思,大概说明,它提供了一个在请求执行前包装的机会,
–可以注入自己定义的动作,比如复制线程状态。
–这样,可以通过重写这个方法来实现想要的封装线程参数的方法。
–可以参考一个已经实现过的这样方式的接口,从接口的继承关系来看有:
– HystrixConcurrencyStrategyDefault、(断路器并发策略默认)
– SecurityContextConcurrencyStrategy、(安全性上下文并发策略)
– SleuthHystrixConcurrencyStrategy(侦察性断路器并发策略)
/**
* 自己参考源码后,自己写的策略,代码如下:
* 这里包装来一个HystrixThreadCallable类,在执行请求前包HystrixThreadCallable对象,
* 该对象的构造函数是希望传递的RequestContextHolder和自定义的HystrixThreadLocal,
* 然后重写wrapCallable方法,在调用前把需要的对象设置进去,这样写一个新线程中就可以拿到了
* 如下如下如下:
*/
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());
}
-----------------------------------------------------------------------------------
/**
* 启动程序,查看运行结果,访问http://localhost:3333/getUser/5555,
* 不同的线程也能顺利地拿到上一个线程传递信息.
* 运行结果发现使用了Hystrix的线程池隔离,然后也能顺利实现即定的目标.
*/
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();
}
}
}
----------------------------------------------------------------------------------
/** 并发策略
* 上面👆代码,并没有完全-完全解决问题,
* 查看HystrixPlugins的registerConurrencyStrategy方法,只能被调用一次,
* 不然就会报错,导致无法与-其它的并发策略一起使用,
* 修改代码,把其它并发策略-注入进去,达到并存的目的
* */
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);
}
}
10 > Hystrix 断路器 -命令注解
奕小生,‘码’累了~呵呵呵~ 这里就分享HystrixCommand和HystrixObservableCommand注解啦!
1.HystrixCommand('豪猪命令'):
'代码:com.netflix.hystrix.HystrixCommand'
主要的意思就是封装执行的代码,然后具有故障延迟容错,断路器和统计等功能,
但它是阻塞命令,另外也是可以和Observable(可观察的)共用的。
2.HystrixObservableCommand('豪猪可观察的命令'):
'代码:com.netflix.hystrix.HystrixObservableCommand'
主要的意思和上面👆差不多,但主要区别是,它是一个非阻塞的调用模式。
-上面👆两个命令:
共同点:都支持故障和延迟容错、断路器、指标统计。
不同点:
1)HystrixCommand默认是阻塞式的,可以提供同步和异步两种方式,
但HystrixObserva-BleConnand是非阻塞的,默认只能是异步的。
2)HystrixCommand的方法是run,HystrixObservableCommand执行的是Construct。
3)HystrixCommand一个实例一次只能发一条数据出去,HystrixObservableCommand可以发送多条数据。
--HystrixCommand命令的属性值的描述:
--CommandKey:'全局唯一的标识符,如果不配则默认是方法名.'
--DefaultFallback:默认的fallback方法,该函数不能有入参,返回值和方法保持一致,但
fallbackMethood优先级最高.
--fallbackMethod:'指定的处理回退逻辑的方法,必须和HystrixCommand在同一个类里,方法的参数要保持一致.'
--ignoreExceptions:HystrixBadRequestException不会触发fallback,这里定义的就是不希望哪些
异常被fallback而是直接抛出.
--commandProperties:'配置一些命名的属性,如执行的隔离策略等.'
--threadPoolProperties:用来配置线程池相关的属性.
--groupKey:'全局唯一标识服务分组的名称,内部会根据这个兼职来展示统计数、仪表盘等信息,默认的线程划分'
'是根据这命令组的名称来进行的,一般会在创建HystrixCommond时指定命令来实现默认的线程池划分.'
--threadPoolKey:对服务的线程池信息进行设置,用于hystrixThreadPool监控、metrics、缓存等用途.
小结一下:奕小生,‘码’出来血汗,希望能给‘码’同志带来帮助。
我的卖点是什么? 努力 !努力!努力💪! 改变了的,只有我自己的心态,而从来不会是这个世界。
我的卖点是什么? 努力 !努力!努力💪! 希望是让人想要变的更好的动力,哪有什么有谓无谓的。