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还有很多东西可以研究,有空继续挖~ 睡觉~






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