二、Hystrix线程池隔离技术的应用

 

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

    }

});

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值