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