1. @HystrixCommand
まず、我々は@HystrixCommandコメントからスタート
@HystrixCommand(fallbackMethod = "fallBack")
public String hiService() {
return restTemplate.getForObject("http://provider/zone?",String.class);
}
現在のメソッドのための注釈HystrixCommandオブジェクトを作成し@HystrixCommand、このクラスの主な役割は、コマンドモードコマンドパッケージのためにある、次の2つのメソッドを実行します。
//用于异步请求,返回的是Future对象,包含服务执行结束时会返回的结果对象。
public Future<R> queue() {
/*
* The Future returned by Observable.toBlocking().toFuture() does not implement the
* interruption of the execution thread when the "mayInterrupt" flag of Future.cancel(boolean) is set to true;
* thus, to comply with the contract of Future, we must wrap around it.
*/
final Future<R> delegate = toObservable().toBlocking().toFuture();
final Future<R> f = new Future<R>() {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (delegate.isCancelled()) {
return false;
}
if (HystrixCommand.this.getProperties().executionIsolationThreadInterruptOnFutureCancel().get()) {
interruptOnFutureCancel.compareAndSet(false, mayInterruptIfRunning);
}
final boolean res = delegate.cancel(interruptOnFutureCancel.get());
if (!isExecutionComplete() && interruptOnFutureCancel.get()) {
final Thread t = executionThread.get();
if (t != null && !t.equals(Thread.currentThread())) {
t.interrupt();
}
}
return res;
}
@Override
public boolean isCancelled() {
return delegate.isCancelled();
}
@Override
public boolean isDone() {
return delegate.isDone();
}
@Override
public R get() throws InterruptedException, ExecutionException {
return delegate.get();
}
@Override
public R get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return delegate.get(timeout, unit);
}
};
/* special handling of error states that throw immediately */
if (f.isDone()) {
try {
f.get();
return f;
} catch (Exception e) {
Throwable t = decomposeException(e);
if (t instanceof HystrixBadRequestException) {
return f;
} else if (t instanceof HystrixRuntimeException) {
HystrixRuntimeException hre = (HystrixRuntimeException) t;
switch (hre.getFailureType()) {
case COMMAND_EXCEPTION:
case TIMEOUT:
// we don't throw these types from queue() only from queue().get() as they are execution errors
return f;
default:
// these are errors we throw from queue() as they as rejection type errors
throw hre;
}
} else {
throw Exceptions.sneakyThrow(t);
}
}
}
return f;
}
//用于同步执行,从依赖服务返回单一对象结果;调用的是queue.get
public R execute() {
try {
return queue().get();
} catch (Exception e) {
throw Exceptions.sneakyThrow(decomposeException(e));
}
}
HystrixCommand、そこHystrixObservableCommand、そのような二つの方法の主な成果を削除します。
- 熱観察である)(観察
- 冷観察であるtoObservable()
)実際には、excuteを達成するために()が(キューで、キューながら、私たちは以下を参照してくださいすることができます:
//使用了冷观察;并且转化成为Future异步对象。
final Future<R> delegate = toObservable().toBlocking().toFuture();
したがって、我々は次のように二つの重要な部分であるHstrixコマンドを実行し、知ることができます。
- イベントソース処理(冷、ホット観察モード)
- 、非同期処理(他の仕事であること、そして最終的にリターンを将来的に所望の結果を得ることができる非同期、非同期処理を用いた同期戻り待ち、将来のオブジェクト)と
Hystrixコマンドの実行に関する次の話では、図に示します:
2. HystrixCircuitBreakerブレーカー
Hystrix、ブレーカコアコンセプトでは、以下がその定義は次のとおりです。
public interface HystrixCircuitBreaker {
//每个Hystrix命令请求,在执行之前,都会调用这个方法,判断当前是否可以执行。
public boolean allowRequest();
//判断开闭状态
public boolean isOpen();
//当断路器处于半开状态,且命令运行成功,则用于关闭断路器
void markSuccess();
}
回路遮断器は複数の内部クラスを使用して実装しました。
//都不实现
static class NoOpCircuitBreaker implements HystrixCircuitBreaker {
@Override
public boolean allowRequest() {
return true;
}
@Override
public boolean isOpen() {
return false;
}
@Override
public void markSuccess() {
}
}
//默认实现
static class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {
private final HystrixCommandProperties properties;
private final HystrixCommandMetrics metrics;
/* track whether this circuit is open/closed at any given point in time (default to false==closed) */
private AtomicBoolean circuitOpen = new AtomicBoolean(false);
/* when the circuit was marked open or was last allowed to try a 'singleTest' */
private AtomicLong circuitOpenedOrLastTestedTime = new AtomicLong();
//还有具体几个方法的实现。
}
回路遮断器は、回路遮断器は、回路遮断器は、このHystrixCommandコマンドを実行するかどうかを決定するために、オン、またはフォールバックダウングレードを呼び出して、開口部とメトリック以下の回路遮断器を閉じていることを、状態自体に基づくことになる、メトリックはhealthCountにより取得されたオブジェクトを測定します10Sの裁判官は、この時間は、ウィンドウのデフォルト:
- 予め設定した閾値内QPS、それはfalseを返し、デフォルトは20です。
- 要求が予め設定した閾値以下のエラー率であれば、それは偽を返します。
ソースとして、次のとおりです。
@Override
public boolean isOpen() {
if (circuitOpen.get()) {
return true;
}
HealthCounts health = metrics.getHealthCounts();
// check if we are past the statisticalWindowVolumeThreshold
if (health.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
// we are not past the minimum volume threshold for the statisticalWindow so we'll return false immediately and not calculate anything
return false;
}
if (health.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) {
return false;
} else {
// our failure rate is too high, trip the circuit
if (circuitOpen.compareAndSet(false, true)) {
// if the previousValue was false then we want to set the currentTime
circuitOpenedOrLastTestedTime.set(System.currentTimeMillis());
return true;
} else {
return true;
}
}
}
許可するかどうかを決定します。
@Override
public boolean allowRequest() {
//这个地方要注意,我们看到了,强制开启的优先级大于强制关闭。
if (properties.circuitBreakerForceOpen().get()) {
return false;
}
if (properties.circuitBreakerForceClosed().get()) {
isOpen();
return true;
}
//这里通过两个方法的结合使用,实现关闭、开启的切换
return !isOpen() || allowSingleTest();
}
//这个方法的功能是尝试允许命令请求;
public boolean allowSingleTest() {
long timeCircuitOpenedOrWasLastTested = circuitOpenedOrLastTestedTime.get();
// 1) if the circuit is open
// 2) and it's been longer than 'sleepWindow' since we opened the circuit
if (circuitOpen.get() && System.currentTimeMillis() > timeCircuitOpenedOrWasLastTested + properties.circuitBreakerSleepWindowInMilliseconds().get()) {
// We push the 'circuitOpenedTime' ahead by 'sleepWindow' since we have allowed one request to try.
// If it succeeds the circuit will be closed, otherwise another singleTest will be allowed at the end of the 'sleepWindow'.
if (circuitOpenedOrLastTestedTime.compareAndSet(timeCircuitOpenedOrWasLastTested, System.currentTimeMillis())) {
//尝试允许请求,则使得断路器处于半开状态。
return true;
}
}
return false;
}
上記を実現するための要求を許可します:
- allowSingleTestすることで、アクセス要求を許可するようにしてみてください。
- スリープ時間を設定することにより、過去の睡眠時間の後に、その後、回路ブレーカは、「セミオープン状態」で、この半開放状態であるが、実際の要求は、彼らが実行に失敗した場合、コマンドの実行を許可する、またはタイムアウトし、回路ブレーカを有効にすることです完全に開いた状態に置かれ、そして睡眠時間の後、試みが半開状態で継続することができました。
以下のことを実現するために回路ブレーカをオフにします:
public void markSuccess() {
if (circuitOpen.get()) {
//关闭断路器
if (circuitOpen.compareAndSet(true, false)) {
//win the thread race to reset metrics
//Unsubscribe from the current stream to reset the health counts stream. This only affects the health counts view,
//and all other metric consumers are unaffected by the reset
//重置统计信息
metrics.resetStream();
}
}
}