【Pigeon源码阅读】高可用之故障注入实现原理(十三)

故障注入

pigeon提供故障注入功能来模拟在调用出现异常时的处理测试,由客户端拦截器FaultInjectionFilter完成,具体有两种故障注入方式,分别为:

  1. EXCEPTION:抛出注入异常FaultInjectionException
  2. 超时:尝试模拟请求特定时长,然后抛出RequestTimeoutException超时异常。

相关的配置如下:

  1. pigeon.fault.injection.enable:故障注入全局开关
  2. 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根据上面的两个配置,判断客户端对当前的方法调用是否启用依赖注入,具体行为通过FaultInjectionManager#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\nrequest:" + request);
}

你可能感兴趣的:(javaweb,分布式,Pigeon源码阅读)