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