package com.netflix.loadbalancer; import com.netflix.client.config.IClientConfig; /** * Given that * {@link IRule} can be cascaded, this {@link RetryRule} class allows adding a retry logic to an existing Rule. * * @author stonse * */ public class RetryRule extends AbstractLoadBalancerRule { IRule subRule = new RoundRobinRule(); long maxRetryMillis = 500; public RetryRule() { } public RetryRule(IRule subRule) { this.subRule = (subRule != null) ? subRule : new RoundRobinRule(); } public RetryRule(IRule subRule, long maxRetryMillis) { this.subRule = (subRule != null) ? subRule : new RoundRobinRule(); this.maxRetryMillis = (maxRetryMillis > 0) ? maxRetryMillis : 500; } public void setRule(IRule subRule) { this.subRule = (subRule != null) ? subRule : new RoundRobinRule(); } public IRule getRule() { return subRule; } public void setMaxRetryMillis(long maxRetryMillis) { if (maxRetryMillis > 0) { this.maxRetryMillis = maxRetryMillis; } else { this.maxRetryMillis = 500; } } public long getMaxRetryMillis() { return maxRetryMillis; } @Override public void setLoadBalancer(ILoadBalancer lb) { super.setLoadBalancer(lb); subRule.setLoadBalancer(lb); } /* * Loop if necessary. Note that the time CAN be exceeded depending on the * subRule, because we're not spawning additional threads and returning * early. */ public Server choose(ILoadBalancer lb, Object key) { long requestTime = System.currentTimeMillis(); long deadline = requestTime + maxRetryMillis; Server answer = null; answer = subRule.choose(key); if (((answer == null) || (!answer.isAlive())) && (System.currentTimeMillis() < deadline)) { // 這裏定義了一個InterruptTask,繼承TimerTask; // 而且實現了抽象的run方法,run方法的實現很簡單,就是判斷,若是InterruptTask裏面構建的當前線程不爲null而且還活着,就中斷該線程; // 這個InterruptTask裏面實現的run方法何時執行呢,就是在當前時間+maxRetryMillis時執行,中斷當前線程,這個run方法是在Timer裏的mainloop裏task.run()調用的 InterruptTask task = new InterruptTask(deadline - System.currentTimeMillis()); // 這裏判斷Thread.interrupted(),就是和上面InterruptTask的run方法呼應上了,在當前時間+maxRetryMillis以前,這個while爲true,從而在當前時間到當前時間+maxRetryMillis這個時間段內,反覆重試獲取server while (!Thread.interrupted()) { answer = subRule.choose(key); if (((answer == null) || (!answer.isAlive())) && (System.currentTimeMillis() < deadline)) { /* pause and retry hoping it's transient */ Thread.yield(); } else { break; } } // 最終要把這個InterruptTask取消掉,這樣就能把Timer.TimerQueue中對於該task的引用移除 task.cancel(); } if ((answer == null) || (!answer.isAlive())) { return null; } else { return answer; } } @Override public Server choose(Object key) { return choose(getLoadBalancer(), key); } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { } }