Hystrx权威指南--Hystrix的Timeout解析

来源:互联网 发布:淘宝网会员登录 编辑:程序博客网 时间:2024/06/16 09:12

在调用第三方服务时有些情况需要对服务响应时间进行把控,当超时的情况下进行fallback的处理
下面来看下超时的案例

public class CommandTimeout extends HystrixCommand<String> {    private final String name;    public CommandTimeout(String name) {        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));        this.name = name;    }    @Override    protected String run()  {        System.out.println("aaaa");        try {            //sleep10秒强制超时,默认超时时间是1s            Thread.sleep(10000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("run end");        return "";    }    @Override    protected String getFallback() {        return "Hello Failure " + name + "!";    }}

接下来是测试方法

@Testpublic void testSynchronous() throws InterruptedException {    System.out.println(new CommandHelloWorld("World").execute());}
java.lang.InterruptedException: sleep interrupted    at java.lang.Thread.sleep(Native Method)    at hystrix.CommandHelloWorld.run(CommandHelloWorld.java:32)    at hystrix.CommandHelloWorld.run(CommandHelloWorld.java:1)    ...run endHello Failure World!

可以看到sleep被强制interrupted,并且调用的输出也变成了fallback方法的返回值

如何查看是哪里调用的interrupt方法

这里顺便说下如何看是哪个方法调用的interrupt
根据stackoverflow的一个答案,没有直接的方法来断点到interrupt的方法,只能通过在Thread的interrupt方法上打断点,再反向看栈信息得知哪里中断当前线程。

如何改变timeout设置

在HystrixCommand的够着方法中可以在第二个参数配置一个timeout的毫秒数

public CommandHelloWorld(String name) {    super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"),1000000000);    this.name = name;}

这个构造方法是在调用AbastractCommand的构造方法时将毫秒数配置在CommandProperties中,如下:

super(group, null, null, null, null, HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(executionIsolationThreadTimeoutInMilliseconds), null, null, null, null, null, null);

Hystrix的timeout是怎么运行的

在运行对应的command时,Hystrix会通过HystrixObservableTimeoutOperator注册一个Timer到一个定时线程池中,当超时后会启用一个HystrixTimer线程来interruptCommand的执行

//创建一个TimerListenerpublic Reference<TimerListener> addTimerListener(final TimerListener listener) {    startThreadIfNeeded();    // add the listener    Runnable r = new Runnable() {        @Override        public void run() {            try {                listener.tick();            } catch (Exception e) {                logger.error("Failed while ticking TimerListener", e);            }        }    };    //通过ScheduledThreadPoolExecutor在到达超时时间时运行上面的listener.tick,而时间是从listener的getIntervalTimeInMilliseconds方法中获得的    ScheduledFuture<?> f = executor.get().getThreadPool().scheduleAtFixedRate(r, listener.getIntervalTimeInMilliseconds(), listener.getIntervalTimeInMilliseconds(), TimeUnit.MILLISECONDS);    //返回包含timer的Reference,在任务在规定时间内完成是用于cancel超时处理    return new TimerReference(listener, f);}//下面是上面listener的定义TimerListener listener = new TimerListener() {    @Override    public void tick() {        // if we can go from NOT_EXECUTED to TIMED_OUT then we do the timeout codepath        // otherwise it means we lost a race and the run() execution completed or did not start        //这里使用CAS的操作将将状态设置为TIME_OUT,使用CAS的原因是如果运行成功而timeout没有被取消时不会标记任务超时        if (originalCommand.isCommandTimedOut.compareAndSet(TimedOutStatus.NOT_EXECUTED, TimedOutStatus.TIMED_OUT)) {            // report timeout failure            originalCommand.eventNotifier.markEvent(HystrixEventType.TIMEOUT, originalCommand.commandKey);            // shut down the original request            //内部会取消当前并调用fallback            s.unsubscribe();            timeoutRunnable.run();            //if it did not start, then we need to mark a command start for concurrency metrics, and then issue the timeout        }    }    @Override    public int getIntervalTimeInMilliseconds() {        //这里从command的配置中获得配置的超时时间        return originalCommand.properties.executionTimeoutInMilliseconds().get();    }};

上面是Command超时后的处理操作,当Command在时间内完成时会调用TimeReference的clear方法,内部调用了future的cancel来取消timer的超时任务

private static class TimerReference extends SoftReference<TimerListener> {    private final ScheduledFuture<?> f;    //保存scheduledFuture    TimerReference(TimerListener referent, ScheduledFuture<?> f) {        super(referent);        this.f = f;    }    @Override    public void clear() {        super.clear();        // stop this ScheduledFuture from any further executions        //停止scheduledFuture        f.cancel(false);    }}