SpringCloud系列(5)---Hystrix 容錯

最近在工作,本來沒有什麼心情寫Blog的。但是沒有面試也沒有什麼事情可以幹,所以繼續寫吧~ 

Hystrix 是作爲熔斷的技術,什麼叫熔斷呢?SpringCloud的各個微服務之間是採用通過網絡調用的,網絡充滿各種不穩定性,所以非常容易出現雪崩效應,爲什麼叫雪崩,當A調用了B微服務,B微服務又調用了C微服務,C微服務無法相應,C和B就會一直拉着鏈接等待超時。只要你訪問併發量夠大,很快的連接線程池就會被打滿,這個後果會怎麼樣估計充滿開發經驗的你們應該意識到嚴重性了。C拖住了B,導致B又拖住了A,併發住夠大的話,所有服務都得掛~。

所以本着你掛了別連累我的精神,C微服務掛就讓他掛,別讓B和A服務作爲陪葬品。所以我們的Hystrix主要就是解決這個大難臨頭各自飛的功能。

一、容錯方案

1、網絡請求超時。如果調用的服務的事件過長,最大的可能就可能是微服務的負載出現問題,再去調用將會得不償失,導致多個線程越累越多,最終等到JVM線程池打滿。

2、熔斷模式,這個就是你家裏那個總閘,以前不是有個保險絲嗎?超過複覈就熔斷那個保險絲,保證安全。同樣道理,如果你所調用的微服務在一段時間無法訪問,就正明你所調用的微服務已經跪舔了,所以你再去利用資源調用也是浪費。所以採取熔斷,將所有調用的鏈接的請求先停掉,直接返回錯誤,做到快速失敗。但是熔斷也需要在一段時間後開啓斷路器半開模式,允許部分請求不直接返回錯誤,並嘗試調用服務,從而發現服務是否恢復的檢測。


二、Hystrix實現了什麼?

1、包裹請求,使用HystrixCommand包裹對依賴的調用邏輯。(這個我還是不太理解,不着急等我看完後面2本關於微服務的書,再補充)

2、跳閘機制:調用服務超過錯誤率閾值,Hystrix可以實現手動或者自動跳閘,停止一段時間的服務請求。(這個好理解)

3、資源隔離:Hystrix爲每個依賴都維護了一個小型的線程。如果該線程池已經打滿,直接拒絕訪問。


廢話說完,搞起~


三、Hystrix整合

例牌環境貼出Maven:

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

目標容錯點,還是我們的商品服務,在調用的controller當中做容錯:

@HystrixCommand(fallbackMethod = "getProductFallback",commandProperties = {
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000"),
},threadPoolProperties = {
        @HystrixProperty(name = "coreSize" ,value = "1"),
        @HystrixProperty(name = "maxQueueSize",value = "10")
})
@GetMapping("/getProduct/{productId}/userId/{userId}")
public Map<String, Object> getProduct(@PathVariable int productId, @PathVariable int userId) {
    Map<String, Object> map = new HashMap<>();
    String url = "http://tony-mall-provider-user/user/getUserById/" + userId;
    User user = this.restTemplate.getForEntity(url, User.class).getBody();
    Product product = this.productRepository.getByProductId(productId);
    map.put("user", user);
    map.put("product", product);
    return map;
}


public Map<String,Object> getProductFallback(@PathVariable int productId, @PathVariable int userId){
    Map<String, Object> map = new HashMap<>();
    User user = new User();
    user.setUserId(-1);
    user.setUsername("ErrorUser");
    Product product = this.productRepository.getByProductId(productId);
    map.put("user", user);
    map.put("product", product);
    return map;
}
解釋一下:

1、 fallback method 當調用失敗(包括熔斷後),使用fallbackmethod指定的方法進行調用和返回(通常指向降級策略)

2、isolation.thread.timeoutInMilliseconds 調用超時限制

3、coresize 核心線程池大小 默認10

4、maxQueueSize  登錄隊列,這裏默認爲不等待直接報錯,這裏是採用BlockingQueue


還有其他的一堆配置,官方文檔有,但是我也沒有看,找到工作之後去看看:

https://github.com/Netflix/Hystrix/wiki/Configuration


四、整合Feign使用Hystrix

SpringCloud 已經幫我們準備最重要的東西 將Hystrix整合到Feign。

其實非常容易看懂,我也沒有介紹的必要了:

@FeignClient(name = "tony-mall-provider-user",fallback = UserFeignClientFallback.class)
public interface UserFeignClient {

    @GetMapping("/user/getUserById/{id}")
    User getUserById(@PathVariable("id") int id);

    @PutMapping("/user/add")
    User addUser(@RequestParam("username") String username, @RequestParam("password")String password,@RequestParam("balance") long balance);

}
我們在定義FeignClient的地方,聲明fallback並指定到UserFeignClient的一個實現類(注意:UserFeignClientFallback這個類必須要是Spring 的bean)

@Component
public class UserFeignClientFallback implements UserFeignClient {
    @Override
    public User getUserById(int id) {
        User user = new User();
        user.setBalance(0);
        user.setUserId(0);
        user.setUsername("Default");
        user.setUserpwd("NULL");
        return user;
    }

    @Override
    public User addUser(String username, String password, long balance) {
        User user = new User();
        user.setBalance(0);
        user.setUserId(0);
        user.setUsername("Default");
        user.setUserpwd("NULL");
        return user;
    }
}
最後我們還需要在配置類中添加@EnableCircuitBreaker annotation:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableCircuitBreaker
public class ProductsystemApplication {


除了使用fallback還有一個fallback factory 可以獲得錯誤的exception,根據不同的throwable去進行降級是個不錯的方案;

這裏通過實現FallbackFactory接口的create方法,編寫fallback的方法。

@Component
public class UserFeignClientFallbackFactory implements FallbackFactory<UserFeignClient> {

    @Override
    public UserFeignClient create(Throwable throwable) {
        throwable.printStackTrace();
        return new UserFeignClient() {
            @Override
            public User getUserById(int id) {
                User user = new User();
                user.setBalance(0);
                user.setUserId(0);
                user.setUsername("Default");
                user.setUserpwd("NULL");
                return user;
            }

            @Override
            public User addUser(String username, String password, long balance) {
                User user = new User();
                user.setBalance(0);
                user.setUserId(0);
                user.setUsername("Default");
                user.setUserpwd("NULL");
                return user;
            }
        };
    }
}

然後將FeignClient annotation的配置從fallback 改爲 fallbackFactory 然後指定上面創建的這個類:

@FeignClient(name = "tony-mall-provider-user",fallbackFactory = UserFeignClientFallbackFactory.class)
public interface UserFeignClient {

五、Hystrix Dashboard 

通過Dashbord 實現對沒有接口的監控

創建一個新的項目,已經添加如下的maven 依賴:

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
然後再配置類當中打上@EnbleHystrixDashboard 

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixdashboardApplication {

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

然後application.properties配置端口,這裏就不貼了。然後運行~

所有整合了hystrix的微服務,都會提供一個hystrix.stream 端點獲得當前hystrix的監控指標。相當全面:

ping: 

data: {"type":"HystrixCommand","name":"getProduct","group":"ProductController","currentTime":1500476379289,"isCircuitBreakerOpen":false,"errorPercentage":0,"errorCount":0,"requestCount":0,"rollingCountBadRequests":0,"rollingCountCollapsedRequests":0,"rollingCountEmit":0,"rollingCountExceptionsThrown":0,"rollingCountFailure":0,"rollingCountFallbackEmit":0,"rollingCountFallbackFailure":0,"rollingCountFallbackMissing":0,"rollingCountFallbackRejection":0,"rollingCountFallbackSuccess":0,"rollingCountResponsesFromCache":0,"rollingCountSemaphoreRejected":0,"rollingCountShortCircuited":0,"rollingCountSuccess":0,"rollingCountThreadPoolRejected":0,"rollingCountTimeout":0,"currentConcurrentExecutionCount":0,"rollingMaxConcurrentExecutionCount":0,"latencyExecute_mean":5006,"latencyExecute":{"0":5004,"25":5007,"50":5007,"75":5007,"90":5007,"95":5007,"99":5007,"99.5":5007,"100":5007},"latencyTotal_mean":5006,"latencyTotal":{"0":5004,"25":5007,"50":5007,"75":5007,"90":5007,"95":5007,"99":5007,"99.5":5007,"100":5007},"propertyValue_circuitBreakerRequestVolumeThreshold":20,"propertyValue_circuitBreakerSleepWindowInMilliseconds":5000,"propertyValue_circuitBreakerErrorThresholdPercentage":50,"propertyValue_circuitBreakerForceOpen":false,"propertyValue_circuitBreakerForceClosed":false,"propertyValue_circuitBreakerEnabled":true,"propertyValue_executionIsolationStrategy":"THREAD","propertyValue_executionIsolationThreadTimeoutInMilliseconds":5000,"propertyValue_executionTimeoutInMilliseconds":5000,"propertyValue_executionIsolationThreadInterruptOnTimeout":true,"propertyValue_executionIsolationThreadPoolKeyOverride":null,"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_metricsRollingStatisticalWindowInMilliseconds":10000,"propertyValue_requestCacheEnabled":true,"propertyValue_requestLogEnabled":true,"reportingHosts":1,"threadPool":"ProductController"}

data: {"type":"HystrixCommand","name":"UserFeignClient#getUserById(int)","group":"tony-mall-provider-user","currentTime":1500476379296,"isCircuitBreakerOpen":false,"errorPercentage":0,"errorCount":0,"requestCount":0,"rollingCountBadRequests":0,"rollingCountCollapsedRequests":0,"rollingCountEmit":0,"rollingCountExceptionsThrown":0,"rollingCountFailure":0,"rollingCountFallbackEmit":0,"rollingCountFallbackFailure":0,"rollingCountFallbackMissing":0,"rollingCountFallbackRejection":0,"rollingCountFallbackSuccess":0,"rollingCountResponsesFromCache":0,"rollingCountSemaphoreRejected":0,"rollingCountShortCircuited":0,"rollingCountSuccess":0,"rollingCountThreadPoolRejected":0,"rollingCountTimeout":0,"currentConcurrentExecutionCount":0,"rollingMaxConcurrentExecutionCount":0,"latencyExecute_mean":0,"latencyExecute":{"0":0,"25":0,"50":0,"75":0,"90":0,"95":0,"99":0,"99.5":0,"100":0},"latencyTotal_mean":0,"latencyTotal":{"0":0,"25":0,"50":0,"75":0,"90":0,"95":0,"99":0,"99.5":0,"100":0},"propertyValue_circuitBreakerRequestVolumeThreshold":20,"propertyValue_circuitBreakerSleepWindowInMilliseconds":5000,"propertyValue_circuitBreakerErrorThresholdPercentage":50,"propertyValue_circuitBreakerForceOpen":false,"propertyValue_circuitBreakerForceClosed":false,"propertyValue_circuitBreakerEnabled":true,"propertyValue_executionIsolationStrategy":"THREAD","propertyValue_executionIsolationThreadTimeoutInMilliseconds":1000,"propertyValue_executionTimeoutInMilliseconds":1000,"propertyValue_executionIsolationThreadInterruptOnTimeout":true,"propertyValue_executionIsolationThreadPoolKeyOverride":null,"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests":10,"propertyValue_metricsRollingStatisticalWindowInMilliseconds":10000,"propertyValue_requestCacheEnabled":true,"propertyValue_requestLogEnabled":true,"reportingHosts":1,"threadPool":"tony-mall-provider-user"}

data: {"type":"HystrixThreadPool","name":"tony-mall-provider-user","currentTime":1500476379296,"currentActiveCount":0,"currentCompletedTaskCount":6,"currentCorePoolSize":10,"currentLargestPoolSize":6,"currentMaximumPoolSize":10,"currentPoolSize":6,"currentQueueSize":0,"currentTaskCount":6,"rollingCountThreadsExecuted":0,"rollingMaxActiveThreads":0,"rollingCountCommandRejections":0,"propertyValue_queueSizeRejectionThreshold":5,"propertyValue_metricsRollingStatisticalWindowInMilliseconds":10000,"reportingHosts":1}

data: {"type":"HystrixThreadPool","name":"ProductController","currentTime":1500476379297,"currentActiveCount":0,"currentCompletedTaskCount":11,"currentCorePoolSize":1,"currentLargestPoolSize":1,"currentMaximumPoolSize":1,"currentPoolSize":1,"currentQueueSize":0,"currentTaskCount":11,"rollingCountThreadsExecuted":0,"rollingMaxActiveThreads":0,"rollingCountCommandRejections":0,"propertyValue_queueSizeRejectionThreshold":5,"propertyValue_metricsRollingStatisticalWindowInMilliseconds":10000,"reportingHosts":1}
當然看上去是的確有點費勁,這是後我們剛剛搭建好的dashboard就是提供友好的分析界面(其實都唔多友好噶啦~)

訪問我們搭好的dashboard的頁面:http://localhost:7701/hystrix


然後再URL上寫上我們商品服務的hystrix.stream端點->http://localhost:8802/hystrix.stream


好了,現在就有個問題,這麼多個微服務一個個輸入非常麻煩,有沒有一個將所有微服務的hystrix.stream整合起來的東西呢~?有turbine~ 但是我好睏我要睡了~  有空接着寫~

其實hystrix還有很多東西可以研究,有空繼續挖~ 睡覺~






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