【Pigeonソース読解】高可用性の故障注入実現原理(十三)
28539 ワード
フェイルインジェクション
pigeonは、呼び出しに異常が発生した場合の処理テストをシミュレートするためのフェイルオーバ機能を提供し、クライアントブロッキングFaultInjectionFilterによって完了します.具体的には、次の2つのフェイルオーバ方式があります.
関連する構成は次のとおりです.
# :
{
"requestKey": {
"enable": true,
"type": "EXCEPTION",
"maxDelay": 100,
"randomDelay": true,
"sample": true,
sampleRate: 0.1
}
}
ここでrequestKeyはサービスurlまたはサービスurl+’#’+methodNameであり、後者を優先的にマッチングし、valueはFaultInjectionConfigであり、属性定義は以下の通りである.
public static class FaultInjectionConfig implements Serializable {
private boolean enable = true; //
private String type = FaultInjectionType.EXCEPTION.getType(); // 1. exception 2. delay
private int maxDelay; // time_unit ms
private boolean randomDelay = false; // (0-maxDelay ms)
private boolean sample = false; //
private float sampleRate; //
}
FaultInjectionFilterでは、pigeonは上記の2つの構成に基づいて、クライアントが現在のメソッド呼び出しに対して依存注入を有効にするかどうかを判断し、具体的な動作はFaultInjectionManagement getActionによって構成に基づいて取得される.
public FaultInjectionAction getAction(String requestKey) {
FaultInjectionConfig config = configMap.get(requestKey);
if (config != null) {
FaultInjectionAction action = new FaultInjectionAction();
// ,
if (!config.getSample() || (config.getSample() && random(config.getSampleRate()))) {
action.setType(FaultInjectionType.getType(config.getType()));
// deley,
if (FaultInjectionType.DELAY.equals(action.getType())) { //
if (config.getRandomDelay()) { //
action.setDelay(random.nextInt(config.getMaxDelay()));
} else { //
action.setDelay(config.getMaxDelay());
}
}
return action;
}
}
return new FaultInjectionAction();
}
次に、全体的な実装ソースコードを見てみましょう.
public InvocationResponse invoke(ServiceInvocationHandler handler, InvokerContext invocationContext) throws Throwable {
InvocationResponse response = null;
InvokerConfig<?> invokerConfig = invocationContext.getInvokerConfig();
int timeout = invokerConfig.getTimeout(invocationContext.getMethodName());
Integer timeoutThreadLocal = InvokerHelper.getTimeout();
if (timeoutThreadLocal != null) {
timeout = timeoutThreadLocal;
}
// url + "#" + methodName, com.dianping.pigeon.demo.EchoService#echo
String requestKey = getRequestKey(invocationContext);
// ,
if (FaultInjectionManager.INSTANCE.isEnable(requestKey)) {
//
response = doFaultInjection(requestKey, invocationContext, timeout);
}
if (response != null) {
return response;
}
//
String serviceKey = invocationContext.getInvokerConfig().getUrl();
if (FaultInjectionManager.INSTANCE.isEnable(serviceKey)) {
response = doFaultInjection(serviceKey, invocationContext, timeout);
}
if (response != null) {
return response;
}
if (timeoutThreadLocal != null) {
InvokerHelper.setTimeout(timeoutThreadLocal);
}
return handler.handle(invocationContext);
}
//
private InvocationResponse doFaultInjection(String key, InvokerContext invocationContext, int timeout) {
InvocationResponse response = null;
InvokerConfig<?> invokerConfig = invocationContext.getInvokerConfig();
byte callMethodCode = invokerConfig.getCallMethod(invocationContext.getMethodName());
CallMethod callMethod = CallMethod.getCallMethod(callMethodCode);
// , none,exception,delay
FaultInjectionManager.FaultInjectionAction faultInjectionAction
= FaultInjectionManager.INSTANCE.getAction(key);
switch (callMethod) {
case SYNC:
case CALLBACK:
switch (faultInjectionAction.getType()) {
case EXCEPTION: //
exception(invocationContext);
break;
case DELAY: // ,
if (timeout <= faultInjectionAction.getDelay()) {
timeout(timeout, invocationContext);
}
break;
case NONE:
break;
}
break;
case FUTURE: // FutureResponse
switch (faultInjectionAction.getType()) {
case EXCEPTION:
response = exceptionFuture(timeout, invocationContext);
break;
case DELAY:
if (timeout <= faultInjectionAction.getDelay()) {
response = timeoutFuture(timeout, invocationContext);
}
break;
case NONE:
break;
}
break;
case ONEWAY:
break;
}
return response;
}
private String getRequestKey(InvokerContext context) {
return context.getInvokerConfig().getUrl() + "#" + context.getMethodName();
}
//
private void exception(InvokerContext context) {
InvocationRequest request = InvocationUtils.newRequest(context);
request.setCreateMillisTime(System.currentTimeMillis());
throw new FaultInjectionManager.FaultInjectionException(request.toString());
}
private void timeout(int timeout, InvokerContext context) {
InvocationRequest request = InvocationUtils.newRequest(context);
request.setCreateMillisTime(System.currentTimeMillis());
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
logger.warn(e.toString());
}
throw InvocationUtils.newTimeoutException(
"request timeout, current time:" + System.currentTimeMillis() + "\r
request:" + request);
}