Hystrix使用分析

Hystrix使用

使用Hystrix實現熔斷

要實現熔斷,首先需要在請求調用方pom文件中加入

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

在啓動類上加入@EnableCircuitBreaker註解,並在調用到另一個微服務的方法上加入一些配置

 @GetMapping("/checkHystrix/{userId}")
    @HystrixCommand(threadPoolKey = "checkHystrix1",
    threadPoolProperties = {
            @HystrixProperty(name = "coreSize",value = "1"),
            @HystrixProperty(name = "maxQueueSize",value = "20")
    },
    commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value="2000")
    })
    public Integer checkHystrix(@PathVariable Long userId) {
        String url = "http://zhao-service-resume/resume/openstate/"+userId;
        Integer forObject =
                restTemplate.getForObject(url, Integer.class);
        return forObject;
    }

將調用到的服務中加入線程休眠十秒。訪問上述服務,在界面上即可發現Hystrix的超時錯誤
file

服務降級

在配置中再增加一個 fallbackMethod = "customeFallback",
配置降級兜底方法的具體形式是

    public  Integer customeFallback(Long userId){
        return -1;
    }

當某個服務熔斷之後,服務器將不再被調⽤,此刻客戶端可以⾃⼰準備⼀個本地的fallback回調,返回⼀個缺省值,這樣做,雖然服務⽔平下降,但好⽍可⽤,⽐直接掛掉要強。但是在配置服務降級策略時,降級(兜底)⽅法必須和被降級⽅法相同的⽅法簽名(相同參數列表、相同返回值)
如果參數不同會出現com.netflix.hystrix.contrib.javanica.exception.FallbackDefinitionException: fallback method wasn't found: customeFallback([class java.lang.Long])
如果返回值不同會出現。且包裝類和基本類型不能共用
Hint: Fallback method 'public int com.zp.controller.AutoDeliverController.customeFallback(java.lang.Long)' must return: class java.lang.Integer or its subclass

倉壁模式(線程池隔離模式)

如果不進⾏任何線程池設置,所有熔斷⽅法使⽤⼀個Hystrix線程池(默認爲10個線程),那麼這樣的話會導致問題,這個問題並不是扇出鏈路微服務不可⽤導致的,⽽是我們的線程機制導致的,如果⽅法A的請求把10個線程都⽤了,⽅法2請求處理的時候壓根都、沒法去訪問B,因爲沒有線程可⽤,並不是B服務不可⽤。因此在配置Hystrix線程時,多個方法應該寫多個線程池。這樣能夠讓線程之間互不影響

  @GetMapping("/checkHystrix/{userId}")
    @HystrixCommand(threadPoolKey = "checkHystrix1",
    threadPoolProperties = {
            @HystrixProperty(name = "coreSize",value = "1"),
            @HystrixProperty(name = "maxQueueSize",value = "20")
    },
    commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value="2000")
    })
    public Integer checkHystrix(@PathVariable Long userId) {
        String url = "http://zhao-service-resume/resume/openstate/"+userId;
        Integer forObject =
                restTemplate.getForObject(url, Integer.class);
        return forObject;
    }

    @GetMapping("/checkHystrixFallback/{userId}")
    @HystrixCommand(threadPoolKey = "checkHystrix2",
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize",value = "2"),
                    @HystrixProperty(name = "maxQueueSize",value = "20")
            },
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value="2000")
            },
            fallbackMethod = "customeFallback"


    )

通過postman發送批量請求,並通過jstack命令可以看到兩個方法的線程池進行了隔離
file

其他屬性的含義

            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value="2000"),
                    @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")
            }

metrics.rollingStats.timeInMilliseconds該屬性是下面的熔斷的統計時間窗口定義
circuitBreaker.requestVolumeThreshold該屬性統計時間窗⼝內的失敗的次數,達到此次數之後開啓熔斷操作
circuitBreaker.errorThresholdPercentage 窗口內失敗的次數的百分比,達到這個百分比之後開啓熔斷操作
circuitBreaker.sleepWindowInMilliseconds 熔斷多久間隔多久以後開始嘗試是否恢復

源碼簡要分析

首先我們根據註解類@EnableCircuitBreaker可以找到SpringFactoryImportSelector類,該類通過泛型在spring.factories文件中找到註解了該泛型的配置類

@Override
	public String[] selectImports(AnnotationMetadata metadata) {
		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() + "?");

		// 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()]);
	}

隨後在spring.factories中找到了加載的配置類

org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration

配置類中配置了一個切面HystrixCommandAspect,Hystrix實現的主要功能都在這個切面中進行了執行

    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;
    }

使用同步調用會走executeObservable最終進入HystrixCommand進入最終的處理邏輯

    public R execute() {
        try {
            return queue().get();
        } catch (Exception e) {
            throw Exceptions.sneakyThrow(decomposeException(e));
        }
    }

進入執行隊列並異步獲取
最後執行 final Future delegate = toObservable().toBlocking().toFuture();具體Hystrix中的熔斷降級操作即在toObservable()完成

歡迎搜索關注本人與朋友共同開發的微信面經小程序【大廠面試助手】和公衆號【微瞰技術】,以及總結的分類面試題https://github.com/zhendiao/JavaInterview

file
file

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