图解服务隔离
-
假设一个抢购下单场景,先需要调用订单系统的接口保存订单信息,然后调用支付接口进行支付,最后调用物流系统的接口进行发货,并且后两个接口依赖相邻前一个接口的处理结果
-
假设第一个接口的三台机器都是正常的,第二个接口三台机器中有一台机器调用失败了,然后会一直卡在这里,而抢课下单是一个高并发接口,所有打到这个机器的调用全部都会失败,导致tomcat连接池占满,其余要调用这台机器其余接口的调用也都会失败
-
一个点影响了全局
-
-
思考:公司的微服务的并发量为什么不高?吞吐量为什么不高?
-
hystrix就是服务隔离
-
上述问题怎么解决?
- 比如一个接口只允许占用10个tomcat连接
hystrix服务隔离机制
- 接口请求到实际接口前,都会先经过hystrix这层所拦截,来确定是否让它继续调用相应的接口
- 服务隔离就是一种技术,隔离了服务调用方和服务提供方,确定服务调用方是否能调用服务提供方
- 比如这个接口可以提供10个连接请求,超过10个之后就不能调用,这样别的接口照样可以请求,就可以避免服务雪崩的现象
pom的jar包依赖
-
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
开启hystrix功能
-
package com.xiangxue.jack; import com.xiangxue.jack.service.feign.StudentService; import com.xiangxue.jack.service.feign.TeacherServiceFeign; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.retry.annotation.EnableRetry; import org.springframework.web.client.RestTemplate; @SpringBootApplication(scanBasePackages = { "com.xiangxue.jack"}) //注册到eureka @EnableEurekaClient //开启断路器功能 @EnableCircuitBreaker //开启feign支持,clients指定哪个类开启feign @EnableFeignClients(clients = { StudentService.class,TeacherServiceFeign.class}) //开启重试功能 @EnableRetry public class MicroWebApplication { @Bean @LoadBalanced RestTemplate restTemplate() { // HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); // factory.setConnectionRequestTimeout(2000); // factory.setReadTimeout(4000); return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(MicroWebApplication.class,args); } }
- @EnableCircuitBreaker:开启熔断功能
实例使用
-
package com.xiangxue.jack.service; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.stereotype.Service; @Service public class TicketServiceImpl implements TicketService { @HystrixCommand @Override public String queryTicket() { return "queryTicket"; } @HystrixCommand @Override public String saveTicket() { return "saveTicket"; } }
hystrix服务削峰
隔离策略
- 1.信号量的隔离策略
- 对全局变量加锁,但是会损耗性能
- 2.线程池的隔离策略
- 为这个接口分配若干个线程,当线程耗光,再有请求会被拒绝
实例
-
/** * Command属性 * execution.isolation.strategy 执行的隔离策略 * THREAD 线程池隔离策略 独立线程接收请求 * SEMAPHORE 信号量隔离策略 在调用线程上执行 * <p> * execution.isolation.thread.timeoutInMilliseconds 设置HystrixCommand执行的超时时间,单位毫秒 * execution.timeout.enabled 是否启动超时时间,true,false * execution.isolation.semaphore.maxConcurrentRequests 隔离策略为信号量的时候,该属性来配置信号量的大小,最大并发达到信号量时,后续请求被拒绝 * <p> * circuitBreaker.enabled 是否开启断路器功能 * circuitBreaker.requestVolumeThreshold 该属性设置在滚动时间窗口中,断路器的最小请求数。默认20,如果在窗口时间内请求次数19,即使19个全部失败,断路器也不会打开 * circuitBreaker.sleepWindowInMilliseconds 改属性用来设置当断路器打开之后的休眠时间,休眠时间结束后断路器为半开状态,断路器能接受请求,如果请求失败又重新回到打开状态,如果请求成功又回到关闭状态 * circuitBreaker.errorThresholdPercentage 该属性设置断路器打开的错误百分比。在滚动时间内,在请求数量超过circuitBreaker.requestVolumeThreshold,如果错误请求数的百分比超过这个比例,断路器就为打开状态 * circuitBreaker.forceOpen true表示强制打开断路器,拒绝所有请求 * circuitBreaker.forceClosed true表示强制进入关闭状态,接收所有请求 * <p> * metrics.rollingStats.timeInMilliseconds 设置滚动时间窗的长度,单位毫秒。这个时间窗口就是断路器收集信息的持续时间。断路器在收集指标信息的时会根据这个时间窗口把这个窗口拆分成多个桶,每个桶代表一段时间的指标,默认10000 * metrics.rollingStats.numBuckets 滚动时间窗统计指标信息划分的桶的数量,但是滚动时间必须能够整除这个桶的个数,要不然抛异常 * <p> * requestCache.enabled 是否开启请求缓存,默认为true * requestLog.enabled 是否打印日志到HystrixRequestLog中,默认true * * @HystrixCollapser 请求合并 * maxRequestsInBatch 设置一次请求合并批处理中允许的最大请求数 * timerDelayInMilliseconds 设置批处理过程中每个命令延迟时间 * requestCache.enabled 批处理过程中是否开启请求缓存,默认true * <p> * threadPoolProperties * threadPoolProperties 属性 * coreSize 执行命令线程池的最大线程数,也就是命令执行的最大并发数,默认10 */ @HystrixCommand(fallbackMethod = "queryContentsFallback", commandKey = "queryContents", groupKey = "querygroup-one", commandProperties = { @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests",value = "100"), @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"), @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000000000") }, threadPoolKey = "queryContentshystrixJackpool", threadPoolProperties = { // @HystrixProperty(name = "coreSize", value = "100") }) @Override public List<ConsultContent> queryContents() { log.info(Thread.currentThread().getName() + "========queryContents========="); s.incrementAndGet(); List<ConsultContent> results = restTemplate.getForObject("http://" + SERVIER_NAME + "/user/queryContent", List.class); return results; }
-
commandProperties是基于Command的属性
- @HystrixProperty(name=“xx.xx”,value=“xx”)
- execution.isolation.strategy:隔离策略,如果配置的是线程池策略,需要在threadPoolKey中的threadPoolProperties配置线程池的数量,如果没有配,默认线程数是10
- @HystrixProperty(name=“xx.xx”,value=“xx”)
-
groupKey和threadPoolKey是用来和其他加了HystrixCommand注解的接口调用进行合并处理的,如果groupkey和threadpoolkey都加了,以threadpoolkey为准,如果只加了groupkey,两个接口用相同的线程池
模拟测试hystrix注解
-
@Test @PerfTest(invocations = 11,threads = 11) public void hystrixTest2() { logger.info(Thread.currentThread().getName() + "==>" + userService.queryContents()); }
-
@PerfTest(invocations = 11,threads = 11):模拟并发数是11
-
这个注解需要引入jar包
-
<dependency> <groupId>org.databene</groupId> <artifactId>contiperf</artifactId> <version>2.3.4</version> </dependency>
-
-
信号量和线程池两种隔离策略的区别
- 线程池策略中有两种线程,一种是用户的请求线程,一种是hystrix分配的线程,并且线程名是hystrix- + commandKey + threadPoolKey,跟用户线程一一匹配,而信号量策略hystrix没有重新分配线程
- 如果想请求速度快一点,用线程池策略,如果想节约资源一些,就用信号量策略
- 默认是线程池策略,在threadPoolProperties = {@HystrixProperty(name = “coreSize”, value = “100”)配置线程池的数量
- 如果使用信号量策略,可以通过并发量超过线程数量,可以加入参数@HystrixProperty(name = “execution.isolation.semaphore.maxConcurrentRequests”,value = “100”),同样可以实现100个任务正常执行
hystrix服务降级
- 对应削峰测试中,假如使用线程池策略,当进行并发调用时,超过线程数量的请求会请求失败,此时可以进行服务降级
- 比如淘宝秒杀抢购时,会出现"系统繁忙,请稍后再试"的提示,这是为了改善用户体验,给一个友好提示,而服务降级就是对异常的友好封装,在hystrix的注解使用中有一个参数fallbackMethod = “queryContentsFallback”,就是当服务调用失败了,就会去调用queryContentsFallback方法,而具体的降级方法怎么写,需要根据业务进行设计了,比如可以记录日志
- 服务降级也可以多次降级,在第一次降级的方法中,同样加上@HystrixCommand的注解,并配置fallbackMethod = "queryContentsFallback"的降级参数,实际使用使用可以业务去做相应的设计
hystrix数据监控
- hystrix接口的调用是有一个监控界面的,当发生高并发调用时,hystrix会对调用情况进行监控,调用失败有多少,服务降级有多少,服务熔断有多少,具体需要搭建springcloud-dashboard的项目
搭建springcloud-dashboard
jar包导入
-
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
开启监控功能
-
package com.xiangxue.jack; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; /* * 监控界面:http://localhost:9990/hystrix * 需要监控的端点(使用了hystrix组件的端点):http://localhost:8083/actuator/hystrix.stream * * */ @SpringBootApplication @EnableHystrixDashboard public class HystrixDashboardApplication { public static void main(String[] args) { SpringApplication.run(HystrixDashboardApplication.class,args); // new SpringApplicationBuilder(HystrixDashboardApplication.class).web(true).run(args); } }
-
在启动类中加入@EnableHystrixDashboard的注解
-
启动项目后访问http://localhost:9990/hystrix,输入监控的端点是http://localhost:8083/actuator/hystrix.stream
-
直接在浏览器访问http://localhost:8083/actuator/hystrix.stream,可以看到很多统计数据,而hystrix控制面板就是根据这些统计数据来描绘的,而hystrix的控制面板就是每隔一段时间请求一下这个监控网址,这样画出来就是一条连续的曲线了
监控面板的监控数据
- hystrix注解中的commandKey会显示在控制面板上
hystrix的配置类HystrixCommandProperties
-
从这个类中可以看到更多的hystrix的配置方法
-
private static HystrixProperty<Boolean> getProperty(String propertyPrefix, HystrixCommandKey key, String instanceProperty, Boolean builderOverrideValue, Boolean defaultValue) { return forBoolean() .add(propertyPrefix + ".command." + key.name() + "." + instanceProperty, builderOverrideValue) .add(propertyPrefix + ".command.default." + instanceProperty, defaultValue) .build(); }
- propertyPrefix默认就是hystrix
- key.name()就是commandKey
- 同样会把默认的加进来.command.default
-
-
同样的,Eureka的配置类是EurekaServerConfigBean和EurekaClientConfigBean,Ribbon的配置类是RibbonClientConfiguration,从这里配置类中可以看到默认配置
jmeter压测
-
Http请求
- 接口:/user/queryUser
- 端口:8083
- Method:GET
-
线程组
- Number of Threads:3000
- Ramp-up periods:1
- Loop Count:1
- 勾选Same user on each iteration
-
点击播放按钮
- hystrix的控制面板出现红圈,同时有一个红色的百分比数字,并且Circuit显示为open