三、Hystrix信号量隔离技术的应用
3.1 线程池隔离技术与信号量隔离技术的区别
hystrix里面,核心的一项功能,其实就是所谓的资源隔离,要解决的最最核心的问题,就是将多个依赖服务的调用分别隔离到各自自己的资源池内避免说对某一个依赖服务的调用,因为依赖服务的接口调用的延迟或者失败,导致服务所有的线程资源全部耗费在这个服务的接口调用上一旦说某个服务的线程资源全部耗尽的话,可能就导致服务就会崩溃,甚至说这种故障会不断蔓延
Hystrix,资源隔离,两种技术,线程池的资源隔离,信号量的资源隔离
信号量,semaphore
信号量跟线程池,两种资源隔离的技术,区别到底在哪儿呢?
线程池隔离:
将Tomcat的线程,转化为ThreadPool的线程。Hystrix不会使用Web容器Tomcat的线程,对应Tomcat请求的线程超过线程池的线程数之后,会调用fallback降级机制,很快返回结果,确保Tomcat其它的线程不会卡死。
信号量隔离:
信号量隔离限定指定的请求,Tomcat请求的线程超过线程池的线程数之后,会调用fallback降级机制,很快返回结果
线程池、信号量限流:
限制最大的流量访问,线程池隔离技术是在Hystrix使用自己的线程完成调用,信号量隔离技术是使用Tomcat的线程完成调用。线程池隔离执行超时会发出TimeOut异常。信号量隔离会使用Tomcat线程执行调用,所以没法执行TimeOut管理。
3.2 线程池隔离技术和信号量隔离技术,分别在什么样的场景下去使用呢?
线程池:适合绝大多数的场景,99%的,线程池,对依赖服务的网络请求的调用和访问,timeout这种问题
信号量:适合你的访问不是对外部依赖的访问,而是对内部的一些比较复杂的业务逻辑的访问,但是像这种访问,系统内部的代码,其实不涉及任何的网络请求,那么只要做信号量的普通限流就可以了,因为不需要去捕获timeout类似的问题,算法+数据结构的效率不是太高,并发量突然太高,因为这里稍微耗时一些,导致很多线程卡在这里的话,不太好,所以进行一个基本的资源隔离和访问,避免内部复杂的低效率的代码,导致大量的线程被hang住
3.3 从本地内存获取数据的逻辑
业务背景里面, 比较适合信号量的是什么场景呢?比如说,我们一般来说,缓存服务,可能会将部分量特别少,访问又特别频繁的一些数据,放在自己的纯内存中,一般我们在获取到商品数据之后,都要去获取商品是属于哪个地理位置,省,市,卖家的,可能在自己的纯内存中,比如就一个Map去获取,对于这种直接访问本地内存的逻辑,比较适合用信号量做一下简单的隔离
优点在于:不用自己管理线程池拉,不用care timeout超时了,信号量做隔离的话,性能会相对来说高一些。
3.4 采用信号量技术获取逻辑进行资源隔离与限流
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)));
https://springcloud.cc/spring-cloud-dalston.html#_circuit_breaker_hystrix_clients
http://projects.spring.io/spring-cloud/spring-cloud.html
/** * 资源隔离,两种策略:[线程池隔离][信号量隔离] * execution.isolation.strategy * 线程池机制:command运行在一个线程中,限流是通过线程池的大小来控制的,其实最大的好处就是对于网络访问请求,如果有超时的话,可以避免调用线程阻塞住 * 信号量机制:command是运行在调用线程中,通过信号量的容量来进行限流,通常是针对超大并发量的场景下,每个服务实例每秒都几百的QPS,那么此时你用线程池的话,线程一般不会太多,可能撑不住那么高的并发,如果要撑住,可能要耗费大量的线程资源,那么就是用信号量, * 来进行限流保护线程池其实最大的好处就是对于网络访问请求,如果有超时的话,可以避免调用线程阻塞住 * * command线程池 * CommondKey:代表了一类command,一般来说代表了底层的依赖服务的一个接口 * CommondGroup:默认情况下,因为就是通过command group来定义一个线程池的,统计信息,成功次数,[timeout(thread)]超时次数,失败次数,可以看到某一个服务整体的一些访问情况 * CommondThreadpool:对于thread pool资源隔离来说,可能是希望能够拆分的更加一致一些,比如在一个功能模块内,对不同的请求可以使用不同的thread pool * * coreSize与queueSizeRejectionThreshold * coreSize:设置线程池的大小,默认是10 * queueSizeRejectionThreshold:控制queue满后reject的threshold,因为maxQueueSize不允许热修改,因此提供这个参数可以热修改,控制队列的最大大小 * * execution.isolation.semaphore.maxConcurrentRequests * 设置使用SEMAPHORE隔离策略的时候,允许访问的最大并发量,超过这个最大并发量,请求直接被reject,这个并发量的设置,跟线程池大小的设置,应该是类似的,但是基于信号量的话,性能会好很多,而且hystrix框架本身的开销会小很多 * @author mawenbo * */ public class JointSchoolStatisticsContrastAnalysisSemaphoreCommand extends HystrixCommand<String>{ public static Logger log = LoggerFactory.getLogger(JointSchoolStatisticsContrastAnalysisSemaphoreCommand.class);
private String associatedId; private String schoolIds; private String courseId; private String url;
public JointSchoolStatisticsContrastAnalysisSemaphoreCommand(String associatedId,String schoolIds,String courseId,String url) { super(Setter .withGroupKey(HystrixCommandGroupKey.Factory.asKey("AssociatedExamAllCourses")) .andCommandKey(HystrixCommandKey.Factory.asKey("jointSchoolStatisticsContrastAnalysis")) // .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("AssociatedExamAllCoursesThreadPool")) .andCommandPropertiesDefaults( HystrixCommandProperties.Setter() .withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE) //设置使用SEMAPHORE隔离策略的时候,允许访问的最大并发量,超过这个最大并发量,请求直接被reject,默认是10 .withExecutionIsolationSemaphoreMaxConcurrentRequests(10) ) // .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(10).withQueueSizeRejectionThreshold(10)) ); this.associatedId = associatedId; this.schoolIds = schoolIds; this.courseId = courseId; this.url = url; }
@Override protected String run() throws Exception { log.info(Thread.currentThread().getName()+"is running......"); Map <String, String> param = Maps.newLinkedHashMap(); param.put("associatedId", associatedId); param.put("schoolIds", schoolIds); param.put("courseId", courseId); try { String result = HttpClientUtils.simpleGetInvoke(url, param); log.info("联合考试 学校对比 获取成功:" + url + "?associatedId=" + associatedId + "&schoolIds=" + schoolIds + "&courseId=" + courseId); return result; } catch (Exception e) { log.error("联合考试 总分分析 获取成功:" + url + "?associatedId=" + associatedId + "&schoolIds=" + schoolIds + "&courseId=" + courseId, e); return null; } }
} |