开启
@EnableDiscoveryClient
@SpringBootApplication
//@EnableHystrix 开启 Hystrix
@EnableCircuitBreaker // 开启熔断器
public class AutodeliverApplication8091 {
配置方法上
//HystrixCommand进行熔断控制 HystrixCommandProperties
@HystrixCommand(
commandProperties ={
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value ="3000" )
}
,fallbackMethod ="myFallBack" //回退方法
)
@GetMapping("/ribbonTimeout/{userId}")
public Integer findRibbonResumeOpenStateTimeout(@PathVariable Long userId) {
String requestUrl = "http://edu-service-resume/resume/openstate/"+userId;
System.out.println("获取服务实现 拼接的url : "+requestUrl);
Integer forObject = restTemplate.getForObject(requestUrl , Integer.class);
return forObject;
}
public Integer myFallBack(Long userId){
return -1;
}
HystrixProperty中的name值都在HystrixCommandProperties类中
// known that we're using deprecated HystrixPropertiesChainedServoProperty until ChainedDynamicProperty exists in Archaius
protected HystrixCommandProperties(HystrixCommandKey key, HystrixCommandProperties.Setter builder, String propertyPrefix) {
this.key = key;
this.circuitBreakerEnabled = getProperty(propertyPrefix, key, "circuitBreaker.enabled", builder.getCircuitBreakerEnabled(), default_circuitBreakerEnabled);
this.circuitBreakerRequestVolumeThreshold = getProperty(propertyPrefix, key, "circuitBreaker.requestVolumeThreshold", builder.getCircuitBreakerRequestVolumeThreshold(), default_circuitBreakerRequestVolumeThreshold);
this.circuitBreakerSleepWindowInMilliseconds = getProperty(propertyPrefix, key, "circuitBreaker.sleepWindowInMilliseconds", builder.getCircuitBreakerSleepWindowInMilliseconds(), default_circuitBreakerSleepWindowInMilliseconds);
this.circuitBreakerErrorThresholdPercentage = getProperty(propertyPrefix, key, "circuitBreaker.errorThresholdPercentage", builder.getCircuitBreakerErrorThresholdPercentage(), default_circuitBreakerErrorThresholdPercentage);
this.circuitBreakerForceOpen = getProperty(propertyPrefix, key, "circuitBreaker.forceOpen", builder.getCircuitBreakerForceOpen(), default_circuitBreakerForceOpen);
this.circuitBreakerForceClosed = getProperty(propertyPrefix, key, "circuitBreaker.forceClosed", builder.getCircuitBreakerForceClosed(), default_circuitBreakerForceClosed);
this.executionIsolationStrategy = getProperty(propertyPrefix, key, "execution.isolation.strategy", builder.getExecutionIsolationStrategy(), default_executionIsolationStrategy);
//this property name is now misleading. //TODO figure out a good way to deprecate this property name
this.executionTimeoutInMilliseconds = getProperty(propertyPrefix, key, "execution.isolation.thread.timeoutInMilliseconds", builder.getExecutionIsolationThreadTimeoutInMilliseconds(), default_executionTimeoutInMilliseconds);
this.executionTimeoutEnabled = getProperty(propertyPrefix, key, "execution.timeout.enabled", builder.getExecutionTimeoutEnabled(), default_executionTimeoutEnabled);
this.executionIsolationThreadInterruptOnTimeout = getProperty(propertyPrefix, key, "execution.isolation.thread.interruptOnTimeout", builder.getExecutionIsolationThreadInterruptOnTimeout(), default_executionIsolationThreadInterruptOnTimeout);
this.executionIsolationThreadInterruptOnFutureCancel = getProperty(propertyPrefix, key, "execution.isolation.thread.interruptOnFutureCancel", builder.getExecutionIsolationThreadInterruptOnFutureCancel(), default_executionIsolationThreadInterruptOnFutureCancel);
this.executionIsolationSemaphoreMaxConcurrentRequests = getProperty(propertyPrefix, key, "execution.isolation.semaphore.maxConcurrentRequests", builder.getExecutionIsolationSemaphoreMaxConcurrentRequests(), default_executionIsolationSemaphoreMaxConcurrentRequests);
this.fallbackIsolationSemaphoreMaxConcurrentRequests = getProperty(propertyPrefix, key, "fallback.isolation.semaphore.maxConcurrentRequests", builder.getFallbackIsolationSemaphoreMaxConcurrentRequests(), default_fallbackIsolationSemaphoreMaxConcurrentRequests);
this.fallbackEnabled = getProperty(propertyPrefix, key, "fallback.enabled", builder.getFallbackEnabled(), default_fallbackEnabled);
this.metricsRollingStatisticalWindowInMilliseconds = getProperty(propertyPrefix, key, "metrics.rollingStats.timeInMilliseconds", builder.getMetricsRollingStatisticalWindowInMilliseconds(), default_metricsRollingStatisticalWindow);
this.metricsRollingStatisticalWindowBuckets = getProperty(propertyPrefix, key, "metrics.rollingStats.numBuckets", builder.getMetricsRollingStatisticalWindowBuckets(), default_metricsRollingStatisticalWindowBuckets);
this.metricsRollingPercentileEnabled = getProperty(propertyPrefix, key, "metrics.rollingPercentile.enabled", builder.getMetricsRollingPercentileEnabled(), default_metricsRollingPercentileEnabled);
this.metricsRollingPercentileWindowInMilliseconds = getProperty(propertyPrefix, key, "metrics.rollingPercentile.timeInMilliseconds", builder.getMetricsRollingPercentileWindowInMilliseconds(), default_metricsRollingPercentileWindow);
this.metricsRollingPercentileWindowBuckets = getProperty(propertyPrefix, key, "metrics.rollingPercentile.numBuckets", builder.getMetricsRollingPercentileWindowBuckets(), default_metricsRollingPercentileWindowBuckets);
this.metricsRollingPercentileBucketSize = getProperty(propertyPrefix, key, "metrics.rollingPercentile.bucketSize", builder.getMetricsRollingPercentileBucketSize(), default_metricsRollingPercentileBucketSize);
this.metricsHealthSnapshotIntervalInMilliseconds = getProperty(propertyPrefix, key, "metrics.healthSnapshot.intervalInMilliseconds", builder.getMetricsHealthSnapshotIntervalInMilliseconds(), default_metricsHealthSnapshotIntervalInMilliseconds);
this.requestCacheEnabled = getProperty(propertyPrefix, key, "requestCache.enabled", builder.getRequestCacheEnabled(), default_requestCacheEnabled);
this.requestLogEnabled = getProperty(propertyPrefix, key, "requestLog.enabled", builder.getRequestLogEnabled(), default_requestLogEnabled);
// threadpool doesn't have a global override, only instance level makes sense
this.executionIsolationThreadPoolKeyOverride = forString().add(propertyPrefix + ".command." + key.name() + ".threadPoolKeyOverride", null).build();
}
Hystrix舱壁模式(线程池隔离策略)
@HystrixCommand
如果不进⾏任何设置,所有熔断⽅法使⽤⼀个Hystrix线程池(10个线程),那么这样的话会导致问
题,这个问题并不是扇出链路微服务不可⽤导致的,⽽是我们的线程机制导致的,如果⽅法A的请求把
10个线程都⽤了,⽅法2请求处理的时候压根都没法去访问B,因为没有线程可⽤,并不是B服务不可
⽤。
@HystrixCommand(
commandProperties ={
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value ="3000" )
}
,fallbackMethod ="myFallBack" //回退方法
//线程池标识,要标识为唯一
,threadPoolKey = "findRibbonResumeOpenStateTimeout"
//线程池属性
,threadPoolProperties = {
//核心纯种
@HystrixProperty(name = "coreSize",value ="1" ),
//最大等待队列长度
@HystrixProperty(name = "maxQueueSize",value ="20" )
}
)
一些配置
@HystrixCommand(
commandProperties ={
//请求超时时间
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value ="3000" ),
//统计窗口内的定义
@HystrixProperty(name =
"metrics.rollingStats.timeInMilliseconds",value = "8000"),
//最小请求数
@HystrixProperty(name =
"circuitBreaker.requestVolumeThreshold",value = "2"),
//错误的请求比例
@HystrixProperty(name =
"circuitBreaker.errorThresholdPercentage",value = "50"),
//自我修复的活窗口时长
@HystrixProperty(name =
"circuitBreaker.sleepWindowInMilliseconds",value = "3000")
}
,fallbackMethod ="myFallBack" //回退方法
//线程池标识,要标识为唯一
,threadPoolKey = "findRibbonResumeOpenStateTimeout"
//线程池属性
,threadPoolProperties = {
//核心纯种
@HystrixProperty(name = "coreSize",value ="1" ),
//最大等待队列长度
@HystrixProperty(name = "maxQueueSize",value ="20" )
}
)
- 配置文件
# 配置熔断策略:
hystrix:
command:
default:
circuitBreaker:
# 强制打开熔断器,如果该属性设置为true,强制断路器进⼊打开状态,将会拒绝所有的请
求。 默认false关闭的
forceOpen: false
# 触发熔断错误⽐例阈值,默认值50%
errorThresholdPercentage: 50
# 熔断后休眠时⻓,默认值5秒
sleepWindowInMilliseconds: 3000
# 熔断触发最⼩请求次数,默认值是20
requestVolumeThreshold: 2
execution:
isolation:
thread:
# 熔断超时设置,默认为1秒
timeoutInMilliseconds: 2000
开启健康检查
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
- 健康检查
跳闸状态
"hystrix": {
"status": "CIRCUIT_OPEN",
"details": {
"openCircuitBreakers": [
"AutodeliverController::findRibbonResumeOpenStateTimeout"
]
}
}
正常
@EnableHystrixDashboard
启用仪表盘
<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-hystrix-dashboard</artifactId>
</dependency>
聚合监控
<!--hystrix turbine聚合监控-->
<dependencies>
<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-eurekaclient</artifactId>
</dependency>
</dependencies>
- 配置
server:
port: 9001
spring:
application:
name: edu-service-hystrix-turbine
# 注册到eureka注册中
eureka:
client:
service-url:
# 使用逗号分隔 多个实例,
defaultZone: http://EduCloudEurekaServerB:8762/eureka,http://EduCloudEurekaServerA:8761/eureka
instance:
prefer-ip-address: true #服务实例中显示ip,而不是显示主机名(兼容老的eureka版本)
# 实例名称: 192.168.1.103:lagou-service-resume:8080,我们可以自定义它
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
# 自定义元数据
# metadata-map:
# cluster: cl1
# region: rn1
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
turbine:
appConfig: edu-service-autodeliver
clusterNameExpression: "'default'"
动类上添加注解@EnableTurbine
@EnableDiscoveryClient
@EnableTurbine
@SpringBootApplication
public class TurbineApplication9001 {
public static void main(String[] args) {
SpringApplication.run(TurbineApplication9001.class,args);
}
}
访问
http://localhost:9001/turbine.stream
再访问仪表盘项目
http://localhost:9000/hystrix
Hystrix核⼼源码
分析⼊⼝: @EnableCircuitBreaker注解激活了熔断功能
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//1 导入了一个选择器
@Import(EnableCircuitBreakerImportSelector.class)
public @interface EnableCircuitBreaker {
}
-
- EnableCircuitBreakerImportSelector
/**
* Imports a single circuit breaker implementation configuration.
* @author Spencer Gibb
*/
@Order(Ordered.LOWEST_PRECEDENCE - 100)
//2 .这里没什么要看 父类SpringFactoryImportSelector
public class EnableCircuitBreakerImportSelector extends
SpringFactoryImportSelector<EnableCircuitBreaker> {
@Override
protected boolean isEnabled() {
//获取断路由开关,默认true
return getEnvironment().getProperty(
"spring.cloud.circuit.breaker.enabled", Boolean.class, Boolean.TRUE);
}
}
- 2.SpringFactoryImportSelector
/**
* Selects configurations to load, defined by the generic type T. Loads implementations
* using {@link SpringFactoriesLoader}.
*
* @author Spencer Gibb
* @author Dave Syer
*/
public abstract class SpringFactoryImportSelector<T>
implements DeferredImportSelector, BeanClassLoaderAware, EnvironmentAware {
private ClassLoader beanClassLoader;
private Class<T> annotationClass;
private Environment environment;
private final Log log = LogFactory.getLog(SpringFactoryImportSelector.class);
@SuppressWarnings("unchecked")
protected SpringFactoryImportSelector() {
//获取到子类传到父类的泛型
this.annotationClass = (Class<T>) GenericTypeResolver
.resolveTypeArgument(this.getClass(), SpringFactoryImportSelector.class);
}
@Override
public String[] selectImports(AnnotationMetadata metadata) {
if (!isEnabled()) {
return new String[0];
}
//根据传过来的类的合路径去spring.factories文件中获取自动装配
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(this.annotationClass.getName(), true));
Assert.notNull(attributes, "No " + getSimpleName() + " attributes found. Is "
+ metadata.getClassName() + " annotated with @" + getSimpleName() + "?");
// Find all possible auto configuration classes, filtering duplicates
List<String> factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader
.loadFactoryNames(this.annotationClass, this.beanClassLoader)));
if (factories.isEmpty() && !hasDefaultFactory()) {
throw new IllegalStateException("Annotation @" + getSimpleName()
+ " found, but there are no implementations. Did you forget to include a starter?");
}
if (factories.size() > 1) {
// there should only ever be one DiscoveryClient, but there might be more than
// one factory
log.warn("More than one implementation " + "of @" + getSimpleName()
+ " (now relying on @Conditionals to pick one): " + factories);
}
return factories.toArray(new String[factories.size()]);
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.hystrix.HystrixAutoConfiguration,\
org.springframework.cloud.netflix.hystrix.security.HystrixSecurityAutoConfiguration
org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration
会注⼊org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration
-
- HystrixCircuitBreakerConfiguration
@Configuration
public class HystrixCircuitBreakerConfiguration {
//切面
@Bean
public HystrixCommandAspect hystrixCommandAspect() {
return new HystrixCommandAspect();
}
//关闭时的钩子
@Bean
public HystrixShutdownHook hystrixShutdownHook() {
return new HystrixShutdownHook();
}
//特性
@Bean
public HasFeatures hystrixFeature() {
return HasFeatures.namedFeatures(new NamedFeature("Hystrix", HystrixCommandAspect.class));
}
/**
* {@link DisposableBean} that makes sure that Hystrix internal state is cleared when
* {@link ApplicationContext} shuts down.
*/
private class HystrixShutdownHook implements DisposableBean {
@Override
public void destroy() throws Exception {
// Just call Hystrix to reset thread pool etc.
Hystrix.reset();
}
}
}
-
- HystrixCommandAspect
//切面
@Aspect
public class HystrixCommandAspect {
private static final Map<HystrixPointcutType, MetaHolderFactory> META_HOLDER_FACTORY_MAP;
static {
META_HOLDER_FACTORY_MAP = ImmutableMap.<HystrixPointcutType, MetaHolderFactory>builder()
.put(HystrixPointcutType.COMMAND, new CommandMetaHolderFactory())
.put(HystrixPointcutType.COLLAPSER, new CollapserMetaHolderFactory())
.build();
}
//切入点 @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)")
public void hystrixCommandAnnotationPointcut() {
}
//切入点 @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)")
public void hystrixCollapserAnnotationPointcut() {
}
//增强
@Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()")
public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable {
//根据切入点获取原始的方法
Method method = getMethodFromTarget(joinPoint);
Validate.notNull(method, "failed to get method from joinPoint: %s", joinPoint);
if (method.isAnnotationPresent(HystrixCommand.class) && method.isAnnotationPresent(HystrixCollapser.class)) {
throw new IllegalStateException("method cannot be annotated with HystrixCommand and HystrixCollapser " +
"annotations at the same time");
}
MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method));
//封装元数据
MetaHolder metaHolder = metaHolderFactory.create(joinPoint);
HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder);
ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ?
metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType();
Object result;
try {
if (!metaHolder.isObservable()) {
result = CommandExecutor.execute(invokable, executionType, metaHolder);
} else {
result = executeObservable(invokable, executionType, metaHolder);
}
} catch (HystrixBadRequestException e) {
throw e.getCause();
} catch (HystrixRuntimeException e) {
throw hystrixRuntimeExceptionToThrowable(metaHolder, e);
}
return result;
}
...
GenericCommand中根据元数据信息重写了两个很核⼼的⽅法,⼀个是run⽅法封装了对原始⽬标⽅法
的调⽤,另外⼀个是getFallBack⽅法
它封装了对回退⽅法的调⽤
另外,在GenericCommand的上层类构造函数中会完成资源的初始化,⽐如线程池
GenericCommand —>AbstractHystrixCommand—>HystrixCommand—>AbstractCommand
进⼊execute执⾏这⾥
public R execute() {
try {
return queue().get();
} catch (Exception e) {
throw Exceptions.sneakyThrow(decomposeException(e));
}
}
GenericCommand⽅法中根据元数据信息等重写了run⽅法(对⽬标⽅法的调⽤),
getFallback⽅法(对回退⽅法的调⽤),在RxJava处理过程中会完成对这两个⽅法的调⽤。