2.1 集成Hystrix線程池依賴POM
pom.xml
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.12</version>
</dependency>
2.2 將Http接口調用的邏輯進行封裝Hystrix
hystrix進行資源隔離,其實是提供了一個抽象,叫做command,就是說,你如果要把對某一個依賴服務的所有調用請求,全部隔離在同一份資源池內,對這個依賴服務的所有調用請求,全部走這個資源池內的資源,不會去用其他的資源了,這個就叫做資源隔離.
對某一個依賴服務,商品服務,所有的調用請求,全部隔離到一個線程池內,對商品服務的每次調用請求都封裝在一個command裏面,每個command(每次服務調用請求)都是使用線程池內的一個線程去執行的,所以哪怕是對這個依賴服務,商品服務,現在同時發起的調用量已經到了1000了,但是線程池內就10個線程,最多就只會用這10個線程去執行,不會說,對Http的請求,因爲接口調用延遲,將tomcat內部所有的線程資源全部耗盡,不會出現了
2.3 使用HystrixCommand封裝Http請求獲取一條數據
/** * 資源隔離:command的創建和執行 * 請求緩存:request cache[getCacheKey][HystrixRequestContextFilter] * 優雅降級:fall back[getFallback] * 短路器:circuit breaker[circuit breaker短路器的配置] * 限流:線程池或者信號量的容量是否已滿[withFallbackIsolationSemaphoreMaxConcurrentRequests][withExecutionTimeoutInMilliseconds][withCoreSize][withMaxQueueSize][withQueueSizeRejectionThreshold] * 超時:[withExecutionTimeoutEnabled][withExecutionTimeoutInMilliseconds] * 彈性線程調度:[withCoreSize][withMaximumSize][withKeepAliveTimeMinutes] * @author mawenbo * */ public class JointExamWholeAnalysisCommand extends HystrixCommand<String>{ public static Logger log = LoggerFactory.getLogger(JointExamWholeAnalysisCommand.class);
private String associatedId; private String url;
private static final HystrixCommandKey COMMAND_KEY = HystrixCommandKey.Factory.asKey("jointExamWholeAnalysis");
public JointExamWholeAnalysisCommand(String associatedId,String url) { super(Setter //CommondGroup:默認情況下,因爲就是通過command group來定義一個線程池的,統計信息,成功次數,[timeout(thread)]超時次數,失敗次數,可以看到某一個服務整體的一些訪問情況 .withGroupKey(HystrixCommandGroupKey.Factory.asKey("AssociatedExamAllCourses")) //CommondKey:代表了一類command,一般來說代表了底層的依賴服務的一個接口,,多個command key屬於一個command group,在做統計的時候,會放在一起統計 .andCommandKey(COMMAND_KEY) //CommondThreadpool:對於thread pool資源隔離來說,可能是希望能夠拆分的更加一致一些,比如在一個功能模塊內,對不同的請求可以使用不同的thread pool //同一個服務的不同接口,做一個細粒度的資源隔離,每個command key有自己的線程池,每個接口有自己的線程池,去做資源隔離和限流 .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("associatedExamAllCoursesThreadPool")) .andCommandPropertiesDefaults( HystrixCommandProperties.Setter() //hystrix資源隔離,兩種技術:線程池的資源隔離;信號量的資源隔離 .withExecutionIsolationStrategy(ExecutionIsolationStrategy.THREAD) //這個參數設置了HystrixCommand.getFallback()最大允許的併發請求數量,默認值是10,也是通過semaphore信號量的機制去限流 //如果超出了這個最大值,那麼直接被reject ==>fallback.isolation.semaphore.maxConcurrentRequests .withFallbackIsolationSemaphoreMaxConcurrentRequests(10) //控制是否要打開timeout機制,默認是(true),讓一個command執行timeout,然後看是否會調用fallback降級 .withExecutionTimeoutEnabled(true) //execution.isolation.thread.timeoutInMilliseconds(默認1s):如果請求放等待隊列中時間太長了,直接就會timeout(fallback),等不到去線程池裏執行了 //手動設置timeout時長,一個command運行超出這個時間,就被認爲是timeout,然後將hystrix command標識爲timeout,同時執行fallback降級邏輯 .withExecutionTimeoutInMilliseconds(1000) //circuit breaker短路器的配置 //(1)circuitBreaker.enabled:控制短路器是否允許工作 .withCircuitBreakerEnabled(true) //(2)circuitBreaker.requestVolumeThreshold:經過短路器的流量超過了一定的閾值(默認10s 20個) .withCircuitBreakerRequestVolumeThreshold(20) //(3)circuitBreaker.errorThresholdPercentage:短路器統計到的異常調用的佔比超過了一定的閾值(50) .withCircuitBreakerErrorThresholdPercentage(50) //(4)circuitBreaker.sleepWindowInMilliseconds:短路器,會自動恢復的,half-open,半開狀態,默認5s .withCircuitBreakerSleepWindowInMilliseconds(5000) //(5)circuitBreaker.forceClosed:強迫關閉短路器 //.withCircuitBreakerForceClosed(true) //(6)circuitBreaker.forceOpen:直接強迫打開短路器 //.withCircuitBreakerForceOpen(true) ) .andThreadPoolPropertiesDefaults( HystrixThreadPoolProperties.Setter() //CoreSize:設置線程池的大小,默認是10,如果你不設置另外兩個queue相關的參數,等待隊列是關閉 //先進去線程池的是10個請求,5個請求進入等待隊列,線程池裏有空閒,等待隊列中的請求如果還沒有timeout,那麼就進去線程池去執行 .withCoreSize(10) //設置線程池的最大大小,只有在設置allowMaximumSizeToDivergeFromCoreSize的時候才能生效 .withMaximumSize(15) //允許線程池大小自動動態調整(默認爲false),設置爲true之後,maxSize就生效了,此時如果一開始是coreSize個線程,隨着併發量上來,那麼就會自動獲取新的線程, //但是如果線程在keepAliveTimeMinutes內空閒,就會被自動釋放掉 .withAllowMaximumSizeToDivergeFromCoreSize(true) //如果coreSize < maxSize,那麼這個參數就設置了一個線程多長時間空閒之後,就會被釋放掉 .withKeepAliveTimeMinutes(1) //設置的是你的等待隊列,緩衝隊列的大小 .withMaxQueueSize(5) //QueueSizeRejectionThreshold:控制隊列的最大大小HystrixCommand在提交到線程池之前,其實會先進入一個隊列中,這個隊列滿了之後,纔會reject //如果withMaxQueueSize<withQueueSizeRejectionThreshold,那麼取的是withMaxQueueSize,反之,取得是withQueueSizeRejectionThreshold .withQueueSizeRejectionThreshold(5) ) ); this.associatedId = associatedId; this.url = url; }
@Override protected String run() throws Exception { log.info(Thread.currentThread().getName()+"is running......"); Map<String,String> param =Maps.newHashMap(); param.put("associatedId", associatedId); try { String result = HttpClientUtils.simpleGetInvoke(url, param); return result; } catch (Exception e) { return null; } } /** * HystrixCommand和HystrixObservableCommand都可以指定一個緩存key, * 然後hystrix會自動進行緩存,接着在同一個request context內,再次訪問的時候,就會直接取用緩存用請求緩存, * 可以避免重複執行網絡請求 */ @Override protected String getCacheKey() { return "joint_examWholeAnalysis_" + associatedId; }
/** * 降級機制四種情況 * error(異常) reject(資源池已滿) timeout(超時) circuit breaker(開啓斷路器) * HystrixObservableCommand,是實現resumeWithFallback方法 */ @Override protected String getFallback() { return "associatedId="+associatedId+"信息Error"; }
/** * 緩存的手動清理 * @param associatedId */ public static void flushCache(String associatedId) { HystrixRequestCache.getInstance(COMMAND_KEY, HystrixConcurrencyStrategyDefault.getInstance()).clear(associatedId); }
} |
2.4 使用HystrixObservableCommand封裝Http請求批量查詢獲取多條數據
//http://blog.csdn.net/liuchuanhong1/article/details/73293318 public class JointExamSingleAnalysisObservableCommand extends HystrixObservableCommand<String> {
public static Logger log = LoggerFactory.getLogger(JointExamSingleAnalysisObservableCommand.class);
private String[] courseIds; private String url; private String associatedId;
public JointExamSingleAnalysisObservableCommand(String associatedId, String[] courseIds, String url) { super(HystrixCommandGroupKey.Factory.asKey("AssociatedExamAllCourses")); this.associatedId = associatedId; this.courseIds = courseIds; this.url = url; }
@Override protected Observable<String> construct() { log.info(Thread.currentThread().getName()+"is running......"); return Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> observer) { try { if (!observer.isUnsubscribed()) { for (int i = 0; i < courseIds.length; i++) { Map<String, String> param = Maps.newLinkedHashMap(); param.put("associatedId", associatedId); param.put("courseId", courseIds[i]); String result = HttpClientUtils.simpleGetInvoke(url, param); log.info("聯合考試 學校對比 獲取成功:" + url + "?associatedId=" + associatedId + "&courseId=" + courseIds[i]); observer.onNext(result); } observer.onCompleted(); } } catch (Exception e) { observer.onError(e); }
}
}).subscribeOn(Schedulers.io()); }
} |
2.5 線程池兩種command的四種調用方式對比
實現 |
HystrixCommand 獲取一條數據 protected String run() throws Exception {} |
HystrixObservableCommand 獲取多條數據 protected Observable<String> construct() {} |
同步 |
command.execute() |
Observable<K> observable = observablecommand.observe(); observable.subscribe(new Observer<String>(){ @Override public void onCompleted() { } @Override public void onError(Throwable e) { e.printStackTrace(); } @Override public void onNext(String t) { } }); observable.toBlocking().toFuture().get() 如果你認爲observable command只會返回一條數據,那麼可以調用上面的模式,去同步執行,返回一條數據 observable.toBlocking().single()同上 |
異步 |
Future<String> future =command.queue() String result = future.get()
|
Observable<K> observable =observablecommand.toObservable(); observable.subscribe(new Observer<String>(){ @Override public void onCompleted() { } @Override public void onError(Throwable e) { e.printStackTrace(); } @Override public void onNext(String t) { } }); |
同步:new CommandHelloWorld("World").execute(),new ObservableCommandHelloWorld("World").toBlocking().toFuture().get() 如果你認爲observable command只會返回一條數據,那麼可以調用上面的模式,去同步執行,返回一條數據
異步:new CommandHelloWorld("World").queue(),new ObservableCommandHelloWorld("World").toBlocking().toFuture() 對command調用queue(),僅僅將command放入線程池的一個等待隊列,就立即返回,拿到一個Future對象,後面可以做一些其他的事情,然後過一段時間對future調用get()方法獲取數據 // observe():hot,已經執行過了 // toObservable(): cold,還沒執行過
Observable<String> fWorld = new CommandHelloWorld("World").observe(); assertEquals("Hello World!", fWorld.toBlocking().single()); fWorld.subscribe(new Observer<String>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { e.printStackTrace(); } @Override public void onNext(String v) { System.out.println("onNext: " + v); } });
Observable<String> fWorld = new ObservableCommandHelloWorld("World").toObservable(); assertEquals("Hello World!", fWorld.toBlocking().single()); fWorld.subscribe(new Observer<String>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { e.printStackTrace(); } @Override public void onNext(String v) { System.out.println("onNext: " + v); } }); |