全文概览
TOC
为什么需要hystrix
============
特色功能
====
Hystrix是一个低延迟和容错的第三方组件库。旨在隔离远程系统、服务和第三方库的访问点。官网上已经停止维护并推荐使用resilience4j。但是国内的话我们有springcloud alibaba。
Hystrix 通过隔离服务之间的访问来实现分布式系统中延迟及容错机制来解决服务雪崩场景并且基于hystrix可以提供备选方案(fallback)。
99.9930\=99.7%uptime0.3%of1billionrequests\=3,000,000failures2+hoursdowntime/monthevenifalldependencieshaveexcellentuptime.99.99^{30} = 99.7\% \quad uptime \ 0.3\% \quad of \quad 1 \quad billion \quad requests \quad = \quad 3,000,000 \quad failures \ 2+ \quad hours \quad downtime/month \quad even \quad if \quad all \quad dependencies \quad have \quad excellent \quad uptime.99.9930\=99.7%uptime0.3%of1billionrequests\=3,000,000failures2+hoursdowntime/monthevenifalldependencieshaveexcellentuptime.
项目准备
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
演示下传统企业没有备选方案的情况会发生什么灾难。
业务隔离
@HystrixCommand(
groupKey = "order-service-getPaymentInfo",
commandKey = "getPaymentInfo",
threadPoolKey = "orderServicePaymentInfo",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1000")
},
threadPoolProperties = {
@HystrixProperty(name = "coreSize" ,value = "6"),
@HystrixProperty(name = "maxQueueSize",value = "100"),
@HystrixProperty(name = "keepAliveTimeMinutes",value = "2"),
@HystrixProperty(name = "queueSizeRejectionThreshold",value = "100")
},
fallbackMethod = "getPaymentInfoFallback"
)
@RequestMapping(value = "/getpayment/{id}",method = RequestMethod.GET)
public ResultInfo getPaymentInfo(@PathVariable("id") Long id) {
log.info(Thread.currentThread().getName());
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id, ResultInfo.class);
}
public ResultInfo getPaymentInfoFallback(@PathVariable("id") Long id) {
log.info("已经进入备选方案了,下面交由自由线程执行"+Thread.currentThread().getName());
return new ResultInfo();
}
@HystrixCommand(
groupKey = "order-service-getpaymentTimeout",
commandKey = "getpaymentTimeout",
threadPoolKey = "orderServicegetpaymentTimeout",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "10000")
},
threadPoolProperties = {
@HystrixProperty(name = "coreSize" ,value = "3"),
@HystrixProperty(name = "maxQueueSize",value = "100"),
@HystrixProperty(name = "keepAliveTimeMinutes",value = "2"),
@HystrixProperty(name = "queueSizeRejectionThreshold",value = "100")
}
)
@RequestMapping(value = "/getpaymentTimeout/{id}",method = RequestMethod.GET)
public ResultInfo getpaymentTimeout(@PathVariable("id") Long id) {
log.info(Thread.currentThread().getName());
return orderPaymentService.getTimeOut(id);
}
并发量在getpaymentTimeout
getpaymentTimeout/{id}
/getpayment/{id}
20
三个线程打满后一段时间开始报错
可以正常响应;也会慢,cpu线程切换需要时间
30
同上
同上
50
同上
也会超时,因为order调用payment服务压力会受影响
@HystrixCommand(
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1000"),
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY,value = "SEMAPHORE"),
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS,value = "6")
},
fallbackMethod = "getPaymentInfoFallback"
)
措施
优点
缺点
超时
熔断
异步
线程隔离
一个调用一个线程池;互相不干扰;保证高可用
cpu线程切换开销
√
√
√
信号量隔离
避免CPU切换。高效
在高并发场景下需要存储信号量变大
×
√
×
服务降级
@DefaultProperties(defaultFallback = "globalFallback")
来实现全局的备选方案。一个方法满足触发降级的条件时如果该请求对应的HystrixCommand
注解中没有配置fallback则使用所在类的全局fallback。如果全局也没有则抛出异常。### 不足
* 虽然`DefaultProperties` 可以避免每个接口都配置fallback。但是这种的全局好像还不是全局的fallback。我们还是需要每个类上配置fallback。笔者查阅了资料好像也没有
* 但是在openfeign专题里我们说了openfeign结合hystrix实现的服务降级功能。还记的里面提到了一个`FallbackFactory`这个类吗。这个类可以理解成spring的`BeanFactory`。这个类是用来产生我们所需要的`FallBack`的。我们在这个工厂里可以生成一个通用类型的fallback的代理对象。代理对象可以根据代理方法的方法签名进行入参和出参。
* 这样我们可以在所有的openfeign地方配置这个工厂类。这样的话就避免的生成很多个fallback。 美中不足的还是需要每个地方都指定一下。关于`FallBackFactory`感兴趣的可以下载源码查看或者进主页查看openfeign专题。
服务熔断
@HystrixCommand(
commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //时间范围
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸
},
fallbackMethod = "getInfoFallback"
)
@RequestMapping(value = "/get", method = RequestMethod.GET)
public ResultInfo get(@RequestParam Long id) {
if (id < 0) {
int i = 1 / 0;
}
log.info(Thread.currentThread().getName());
return orderPaymentService.get(id);
}
public ResultInfo getInfoFallback(@RequestParam Long id) {
return new ResultInfo();
}
circuitBreaker.requestVolumeThreshold
设置统计请求次数circuitBreaker.sleepWindowInMilliseconds
设置时间滑动单位 , 在触发熔断后多久进行尝试开放,及俗称的半开状态circuitBreaker.errorThresholdPercentage
设置触发熔断开关的临界条件http://localhost/order/get?id=-1
进行20次测试。虽然这20次无一例额外都会报错。但是我们会发现一开始报错是因为我们代码里的错误。后面的错误就是hystrix熔断的错误了。一开始试by zero 错误、后面就是short-circuited and fallback failed 熔断错误了服务限流
请求合并
====
order/getId?id=1
突然有一万个请求过来。为了缓解压力我们集中一下请求每100个请求调用一次order/getIds?ids=xxxxx
。这样我们最终到payment模块则是10000/100=100个请求。下面我们通过代码配置实现下请求合并。HystrixCollapser
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HystrixCollapser {
String collapserKey() default "";
String batchMethod();
Scope scope() default Scope.REQUEST;
HystrixProperty[] collapserProperties() default {};
}
属性
含义
collapserKey
唯一标识
batchMethod
请求合并处理方法。即合并后需要调用的方法
scope
作用域;两种方式REQUEST, GLOBAL ;
REQUEST : 在同一个用户请求中达到条件将会合并
GLOBAL : 任何线程的请求都会加入到这个全局统计中
HystrixProperty[]
配置相关参数
@HystrixCollapser(
scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
batchMethod = "getIds",
collapserProperties = {
@HystrixProperty(name = HystrixPropertiesManager.MAX_REQUESTS_IN_BATCH , value = "3"),
@HystrixProperty(name = HystrixPropertiesManager.TIMER_DELAY_IN_MILLISECONDS, value = "10")
}
)
@RequestMapping(value = "/getId", method = RequestMethod.GET)
public ResultInfo getId(@RequestParam Long id) {
if (id < 0) {
int i = 1 / 0;
}
log.info(Thread.currentThread().getName());
return null;
}
@HystrixCommand
public List<ResultInfo> getIds(List<Long> ids) {
System.out.println(ids.size()+"@@@@@@@@@");
return orderPaymentService.getIds(ids);
}
工作流程
====
* HystrixCommand : 用在依赖单个服务上
* HystrixObservableCommand : 用在依赖多个服务上②、命令执行,hystrrixCommand 执行execute、queue ; hystrixObservableCommand执行observe、toObservable
方法
作用
execute
同步执行;返回结果对象或者异常抛出
queue
异步执行;返回Future对象
observe
返回Observable对象
toObservable
返回Observable对象
HystrixDashboard
================
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
就引入了dashboard了。 我们不需要进行任何开发。这个和eureka一样主需要简单的引包就可以了。@Component
public class HystrixConfig {
@Bean
public ServletRegistrationBean getServlet(){
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
//注意这里配置的/hystrix.stream 最终访问地址就是 localhost:port/hystrix.stream ; 如果在配置文件中配置在新版本中是需要
//加上actuator 即 localhost:port/actuator
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
localhost/hystrix.stream
就会出现ping的界面。表示我们order模块安装监控成功。当然order也需要actuator模块聚合监控
====
hystrix-dashboard
来对我们的order模块进行监控。但是实际应用中我们不可能只在order中配置hystrix的。新建hystrix-turbine
<!--新增hystrix dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
spring:
application:
name: cloud-hystrix-turbine
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
instance:
prefer-ip-address: true
# 聚合监控
turbine:
app-config: cloud-order-service,cloud-payment-service
cluster-name-expression: "'default'"
# 该处配置和url一样。如果/actuator/hystrix.stream 的则需要配置actuator
instanceUrlSuffix: hystrix.stream
启动类上添加EnableTurbine
注解
* * *
* * *
* * *
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。