五、解决方案五:线程池隔离
1、示意图
2、结局
接口A请求10万次,接口B请求10次,由于他们在同一个线程池中,接口A会把接口B撑爆。A接口瘫痪必然导致B接口瘫痪
接口没有隔离
在同一个springboot,同一个tomcat,同一个线程池里面
3、线程池隔离
给每个接口单独分配一个线程,预计好有哪些接口并发量大
接口A不会影响接口B
4、service类添加方法
package com.example.mycloud.service;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Future;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
@CacheConfig(cacheNames = {"com.example.mycloud"})
@Service
public class TestHystrixService {
/********************
* 服务降级案例
********************/
@HystrixCommand(fallbackMethod = "fallback", //降级方法
//配置参数
commandProperties = {
//默认10秒内,如果并发数达到该设置值,请求会被拒绝和抛出异常并且fallback不会被调用
@HystrixProperty(name=HystrixPropertiesManager.FALLBACK_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value="15")
})
public String testHystrix1() {
throw new RuntimeException("服务异常");
//return "正常请求......";
}
/**
* 返回托底数据
* @return
*/
public String fallback() {
return "fallback method......";
}
/********************
* 请求缓存案例
********************/
@Cacheable(key = "'get' + #id")
public String get(Integer id) {
System.out.println("==========get==========");
return "测试缓存..." + id;
}
@CacheEvict(key = "'get' + #id")
public void del(Integer id) {
System.out.println("==========del==========");
}
/********************
* 请求合并案例
********************/
@HystrixCollapser(batchMethod = "batchIdInfo",
scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
collapserProperties = {
//请求时间间隔在50ms之内的请求会被合并为一个请求
@HystrixProperty(name = "timerDelayInMilliseconds", value = "20"),
//设置触发批处理执行之前,在批处理中允许的最大请求数
@HystrixProperty(name = "maxRequestsInBatch", value = "200")
}
)
public Future<String> getIdInfo(Integer id) {
//没有打印这里,因为请求合并了
System.out.println("==========" + id + "==========");
return null;
}
@HystrixCommand
public List<String> batchIdInfo(List<Integer> id) {
List<String> list = new ArrayList<>();
for(Integer i : id) {
list.add("id: " + i);
}
return list;
}
/********************
* 服务熔断案例
********************/
@HystrixCommand(fallbackMethod = "fallback2",
commandProperties = {
//默认20个;10s内请求数大于20个时就启动熔断器,当请求符合熔断条件时将触发getFallback()
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10"),
//请求错误率大于50%时就熔断,然后for循环发起请求,当请求符合熔断条件时将触发getFallback()
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
//默认5秒;熔断多少秒后去尝试请求
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000")
}
)
public String testHystrix4() {
Random random = new Random();
int i = random.nextInt();
if (i % 2 == 1) {
throw new RuntimeException("服务异常");
}
return "正常请求......";
}
public String fallback2() {
return "fallback2 method......";
}
/********************
* 线程池隔离案例
********************/
@HystrixCommand(groupKey = "service-provider1", commandKey = "testHystrix5",
threadPoolKey = "testHystrix5",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"), //线程池大小
@HystrixProperty(name = "maxQueueSize", value = "100"), //最大队列长度
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"), //线程存活时间
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15") //拒绝请求
},
fallbackMethod = "falback3"
)
public String testHystrix5() {
return "正常请求......";
}
public String fallback3() {
return "fallback3 method......";
}
}
resource添加:
@RequestMapping("/testHystrix5")
public String testHystrix5() {
return testHystrixService.testHystrix5();
}
5、线程池隔离参数配置
(1)
参数:groupKey
作用:服务名(相同服务用一个名称,如商品、用户等等)
默认值:getClass().getSimpleName();
备注:在consumer里面为每个provider服务,设置group标识,一个group使用一个线程池
(2)
参数:commandKey
作用:接口(服务下面的接口,如购买商品)
默认值:当前执行方法名
备注:consumer的接口名称
(3)
参数:threadPoolKey
作用:线程池的名称,配置全局唯一标识线程池的名称,相同线程池名称的线程池是同一个
默认值:默认是分组名groupKey
备注:
(4)
参数:coreSize
作用:线程池大小,这是最大的并发执行数量
默认值:10
备注:
每秒最大支撑的请求数(99%平均响应时间 + 一个换充值)
比如,每秒能处理1000个请求,99%的请求响应时间是60ms,那么公式是:1000 * (0.060 + 0.012)
(5)
参数:maxQueueSize
作用:最大队列长度,设置BlockingQueue的最大长度
默认值:-1
备注:默认值-1,如果使用正数,队列将从同步队列(SynchronousQueue)改为阻塞队列(LinkedBlockingQueue)
(6)
参数:keepAliveTimeMinutes
作用:线程存活时间,设置存活时间,单位分钟
默认值:1
备注:控制一个线程从使用完成到被释放的时间
(7)
参数:queueSizeRejectionThreshold
作用:拒绝请求,设置拒绝请求的临界值
默认值:5
备注:
此属性不适用于maxQueueSize=-1时
设置这个值的原因是maxQueueSize值运行时不能改变,我们可以通过修改这个变量动态修改允许排队的长度
六、解决方案六:信号量隔离
1、示意图
2、service类添加方法
package com.example.mycloud.service;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Future;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
@CacheConfig(cacheNames = {"com.example.mycloud"})
@Service
public class TestHystrixService {
/********************
* 服务降级案例
********************/
@HystrixCommand(fallbackMethod = "fallback", //降级方法
//配置参数
commandProperties = {
//默认10秒内,如果并发数达到该设置值,请求会被拒绝和抛出异常并且fallback不会被调用
@HystrixProperty(name=HystrixPropertiesManager.FALLBACK_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value="15")
})
public String testHystrix1() {
throw new RuntimeException("服务异常");
//return "正常请求......";
}
/**
* 返回托底数据
* @return
*/
public String fallback() {
return "fallback method......";
}
/********************
* 请求缓存案例
********************/
@Cacheable(key = "'get' + #id")
public String get(Integer id) {
System.out.println("==========get==========");
return "测试缓存..." + id;
}
@CacheEvict(key = "'get' + #id")
public void del(Integer id) {
System.out.println("==========del==========");
}
/********************
* 请求合并案例
********************/
@HystrixCollapser(batchMethod = "batchIdInfo",
scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
collapserProperties = {
//请求时间间隔在50ms之内的请求会被合并为一个请求
@HystrixProperty(name = "timerDelayInMilliseconds", value = "20"),
//设置触发批处理执行之前,在批处理中允许的最大请求数
@HystrixProperty(name = "maxRequestsInBatch", value = "200")
}
)
public Future<String> getIdInfo(Integer id) {
//没有打印这里,因为请求合并了
System.out.println("==========" + id + "==========");
return null;
}
@HystrixCommand
public List<String> batchIdInfo(List<Integer> id) {
List<String> list = new ArrayList<>();
for(Integer i : id) {
list.add("id: " + i);
}
return list;
}
/********************
* 服务熔断案例
********************/
@HystrixCommand(fallbackMethod = "fallback2",
commandProperties = {
//默认20个;10s内请求数大于20个时就启动熔断器,当请求符合熔断条件时将触发getFallback()
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10"),
//请求错误率大于50%时就熔断,然后for循环发起请求,当请求符合熔断条件时将触发getFallback()
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "50"),
//默认5秒;熔断多少秒后去尝试请求
@HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "5000")
}
)
public String testHystrix4() {
Random random = new Random();
int i = random.nextInt();
if (i % 2 == 1) {
throw new RuntimeException("服务异常");
}
return "正常请求......";
}
public String fallback2() {
return "fallback2 method......";
}
/********************
* 线程池隔离案例
********************/
@HystrixCommand(groupKey = "service-provider1", commandKey = "testHystrix5",
threadPoolKey = "testHystrix5",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"), //线程池大小
@HystrixProperty(name = "maxQueueSize", value = "100"), //最大队列长度
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"), //线程存活时间
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15") //拒绝请求
},
fallbackMethod = "falback3"
)
public String testHystrix5() {
return "正常请求......";
}
public String fallback3() {
return "fallback3 method......";
}
/********************
* 信号量隔离案例
********************/
@HystrixCommand(fallbackMethod = "fallback4",
commandProperties = {
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"), //信号量隔离
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "100") //信号量最大并发数
}
)
public String testHystrix6() {
return "正常请求......";
}
public String fallback4() {
return "fallback4 method......";
}
}
resource添加:
@RequestMapping("/testHystrix6")
public String testHystrix6() {
return testHystrixService.testHystrix6();
}
3、信号量隔离参数配置
(1)
参数:execution.isolation.strategy
作用:隔离策略配置
默认值:THREAD
备注:只有2种,THREAD和SEMAPHORE
(2)
参数:execution.isolation.thread.timeoutInMilliseconds
作用:超时时间
默认值:1000ms
备注:
在THREAD模式下,达到超时时间,自动中断
在SEMAPHORE模式中下,会等待执行完成后,再去判断是否超时
(3)
参数:execution.isolation.thread.interruptOnTimeout
作用:是否打开超时线程中断
默认值:TRUE
备注:THREAD模式有效
(4)
参数:execution.isolation.semaphore.maxConcurrentRequests
作用:信号量最大并发数
默认值:10
备注:SEMAPHORE模式有效
(5)
参数:fallback.isolation.semaphore.maxConcurrentRequests
作用:fallback最大并发数
默认值:10
备注:SEMAPHORE模式有效
4、信号量隔离其实就是队列里面最大并发量的数字
七、线程池隔离和信号量隔离的区别
线程池隔离 | 信号量隔离 | |
线程 | 请求线程和调用provider线程不是同一条线程 | 请求线程和调用provider线程是同一条线程 |
开销 | 排队、调度、上下文开销等 | 无线程切换,开销低 |
异步 | 支持 | 不支持 |
并发支持 | 支持(最大线程池大小) | 支持(最大信号量上限) |
传递Header | 无法传递Http Header | 可以传递Http Header |
支持超时 | 能支持超时 | 不支持超时 |
1、什么情况下用线程池隔离
请求并发量大,并且耗时长(请求耗时长,一般是计算量大,或者读数据库)。采用线程隔离策略,这样的话,可以保证大量的容器(tomcat)线程可用,不会犹豫服务原因,一直处于阻塞或等待状态,快速失败返回
2、什么情况下用信号量隔离
请求并发量大,并且耗时短(请求耗时短,可能是计算量小,或者读缓存)。采用信号量隔离策略,因为这类服务的返回通常会非常的快,不会占用容器线程太长的时间,而且也减少了线程切换的一些开销,提高了缓存服务的效率
八、feign服务降级处理
1、修改配置文件
#feign默认是不开启Hystrix
feign.hystrix.enabled=true
2、增加fallback类
FeignClientServiceFallback.java
package com.example.mycloud.hystrix;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import com.example.mycloud.feign.FeignClientService;
@Component
public class FeignClientServiceFallback implements FeignClientService {
@Override
public String remoteApi(@PathVariable String id) {
return "fallback......";
}
}
3、关联feign服务类和fallback类
package com.example.mycloud.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.example.mycloud.hystrix.FeignClientServiceFallback;
@FeignClient(name = "inside-app1", fallback = FeignClientServiceFallback.class)
public interface FeignClientService {
@GetMapping("/insideApp1/remoteApi/{id}")
public String remoteApi(@PathVariable String id);
}
九、feign服务降级后的异常记录
1、增加fallback factory类
FeignClientServiceFallbackFactory.java
package com.example.mycloud.hystrix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.example.mycloud.feign.FeignClientService;
import feign.hystrix.FallbackFactory;
public class FeignClientServiceFallbackFactory implements FallbackFactory<FeignClientService> {
private Logger logger = LoggerFactory.getLogger(FeignClientServiceFallbackFactory.class);
@Override
public FeignClientService create(final Throwable cause) {
return new FeignClientService() {
@Override
public String remoteApi(String id) {
logger.warn("fallback exception: ", cause);
return "fallback......";
}
};
}
}
2、关联feign服务类和fallback factory类
package com.example.mycloud.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.example.mycloud.hystrix.FeignClientServiceFallback;
import com.example.mycloud.hystrix.FeignClientServiceFallbackFactory;
@FeignClient(name = "inside-app1", fallbackFactory = FeignClientServiceFallbackFactory.class) //fallback和fallbackFactory配置一个
public interface FeignClientService {
@GetMapping("/insideApp1/remoteApi/{id}")
public String remoteApi(@PathVariable String id);
}