01深度解析Spring Cloud Hystrix---入門搭建以及流程分析

總結

本章 主要搭建Hystrix 的簡單demo, 再從註解着手,大致梳理如何通過一個@EnableHystrix 和 @HystrixCommand就能生效的原理。

一.背景

1.1 介紹

Spring Cloud Hystrix 基於 Netflix Hystrix 實現,具備服務降級、服務熔斷、線程與信號隔離、請求緩存、請求合併以及服務監控等強大功能。

1.2 demo

先走一個簡單的例子,然後我們再繼續深入解析,我這裏 用的springBoot 的版本是
2.0.6.RELEASE,spring Cloud 的版本 是Finchley.RELEASE,這裏的 版本要匹配,可以自行搜索一下.
Pom 文件 配置:

    <dependencies>
        <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>
    </dependencies>

新建一個Application.class
在 啓動類上面開啓Hystrix(@EnableHystrix)@EnableHystrixDashboard 這個只是一個 數據面板 ,是查看接口的情況的,可配可不配

@SpringBootApplication
@EnableHystrix
@EnableHystrixDashboard
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

再建一個 TestController.class
在接口上面 加上 熔斷註解@HystrixCommand,並需要指定一個異常時調用的 方法名稱,這裏定義了一個 getDefaultMethod方法,入參和出參 要和原先的方法一致,不然會報錯。

@RestController
public class TestController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("user/{id}")
    @HystrixCommand(fallbackMethod = "getDefaultMethod")
    public String getClientById(@PathVariable Long id) throws InterruptedException {
        Thread.sleep(2000);
        String client = restTemplate.getForObject("http://localhost:2223/user/" + 100, String.class);
        System.out.println(client);
        return client;
    }

    public String getDefaultMethod(Long id) {
        System.out.println("熔斷,默認回調函數");
        return "this is hystrix  call back function !";
    }
}

啓動然後測試
我這裏的端口號是 2225,結果如下:
可以看到 代碼裏面去請求2223 端口號的服務器,然後走的 默認的接口,這樣就起到了一個 在 調用的服務 出現異常時,可以走默認接口,達到一個 服務降級的目的.
在這裏插入圖片描述

二. 分析

從上面的例子,先引入jar ,然後在啓動類上面加上註解@EnableHystrix 開啓Hystrix ,接着在 需要熔斷的方法上面加上@HystrixCommand 並指定對應的出現異常時運行的方法即可. 內部是如何運轉的呢,我們就從 @EnableHystrix 開始分析

2.1 @EnableHystrix

在這裏插入圖片描述
@EnableHystrix 註解裏面什麼都沒有,引入了@EnableCircuitBreaker ,所以在 啓動類上面 用 @EnableHystrix 或者@EnableCircuitBreaker 是一個效果

我們繼續看一下 @EnableCircuitBreaker,裏面也沒有啥,引入了 EnableCircuitBreakerImportSelector.class,此類繼承了SpringFactoryImportSelector.class,這個類從名字 就能看出來 實現了 接口 ImportSelector //TODO, 作用 應該是手動注入特定的Bean ,直接看 selectImports 的具體邏輯

	@Override
	public String[] selectImports(AnnotationMetadata metadata) {
	    //判斷是否開啓Hystrix
		if (!isEnabled()) {
			return new String[0];
		}
		AnnotationAttributes attributes = AnnotationAttributes.fromMap(
				metadata.getAnnotationAttributes(this.annotationClass.getName(), true));

		Assert.notNull(attributes, "No " + getSimpleName() + " attributes found. Is "
				+ metadata.getClassName() + " annotated with @" + getSimpleName() + "?");
		// 獲取所有的 可能的配置,過濾掉重複的
		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) {
			log.warn("More than one implementation " + "of @" + getSimpleName()
					+ " (now relying on @Conditionals to pick one): " + factories);
		}
		return factories.toArray(new String[factories.size()]);
	}

大致的流程如下:

  1. 判斷是否開啓Hystrix,如果沒有開啓 直接返回
  2. 獲取到所有的 可能的配置,SpringFactoryImportSelector 類 實現了 BeanClassLoaderAware,獲取類加載器, 這裏用到了 類似 SPI的技術,是SpringBoot 從將 需要注入的Bean 放在 "META-INF/spring.factories"文件裏面,這樣使用時就不必再進行注入了. 加載註解 @EnableCircuitBreaker具體需要注入的配置實現類,爲:org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration在這裏插入圖片描述
  3. 接下來就是 注入 HystrixCircuitBreakerConfiguration 這個Bean了

2.2 HystrixCircuitBreakerConfiguration

@Configuration
public class HystrixCircuitBreakerConfiguration {
    // 注入了一個攔截器
	@Bean
	public HystrixCommandAspect hystrixCommandAspect() {
		return new HystrixCommandAspect();
	}
	// 注入了Hystrix 關閉的Hook
	@Bean
	public HystrixShutdownHook hystrixShutdownHook() {
		return new HystrixShutdownHook();
	}
	
	@Bean
	public HasFeatures hystrixFeature() {
		return HasFeatures.namedFeatures(new NamedFeature("Hystrix", HystrixCommandAspect.class));
	}
	private class HystrixShutdownHook implements DisposableBean {
		@Override
		public void destroy() throws Exception {
			// Just call Hystrix to reset thread pool etc.
			Hystrix.reset();
		}
	}
}
//Hystrix 類的 reset 方法, 將線程池關閉,將一些統計數據重置
   public static void reset() {
        // shutdown thread-pools
        HystrixThreadPool.Factory.shutdown();
        _reset();
    }

從這個Bean 裏面可以看出 ,主要是 注入了一個攔截器HystrixCommandAspect ,注入了一個HystrixShutdownHook 用於關閉線程池以及重置數據,重點就在hystrixCommandAspect 裏面了

2.2 hystrixCommandAspect

首先看一下 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 {
       ...
    }
    ...
}

這裏就是入口的核心所在了, 從代碼可以看出 對 @HystrixCommand 和 @HystrixCollapser 進行了 @Around的攔截 , 這樣便實現了 只要在方法上面 加上一個 @HystrixCommand 註解,便可以實現服務的熔斷或者降級操作.具體的 實現邏輯請查看下一章.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章