【Pigeonソース読解】高可用性の故障注入実現原理(十三)


フェイルインジェクション


pigeonは、呼び出しに異常が発生した場合の処理テストをシミュレートするためのフェイルオーバ機能を提供し、クライアントブロッキングFaultInjectionFilterによって完了します.具体的には、次の2つのフェイルオーバ方式があります.
  • EXCEPTION:放出注入異常FaultInjectionException
  • タイムアウト:要求の特定時間をシミュレートし、RequestTimeoutExceptionタイムアウト例外を放出します.

  • 関連する構成は次のとおりです.
  • pigeon.fault.injection.Enable:フェイルインジェクショングローバルスイッチ
  • pigeon.fault.injection.configsフェイルインジェクション構成
  • #  :
    {
    	"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); }