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; } }
} |