dubbo源码分析-consumer端4-MockClusterInvoker

      在前面几篇文章中,我们分析了consumer端的代理生成过程。创建完成后,应用就可以进行调用了,调用的代码如下:

  // 代理类中的sayHello方法
  public String sayHello(String paramString) {  
    // 将调用时的参数传入arrayOfObject中
    Object[] arrayOfObject = new Object[1];  
    arrayOfObject[0] = paramString;  
    // methods[0]表示的就是sayHello, handler的实现为com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler
    Object localObject = this.handler.invoke(this, methods[0], arrayOfObject);  
    return (String)localObject;  
  } 

  // InvokerInvocationHandler的invoke方法       
       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
               // Object中的方法不用远程调用
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }
               // 将方法和参数封装成RpcInvocation后调用,recreate方法主要是在调用时如果发生异常则抛出异常,否则正常返回
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }
        Invocation的主要作用是将(远程)方法调用时需要的信息+Invoker封装起来, 这些信息包括:方法名、参数类型、参数值、附加信息。结合前一篇Invoker创建流程,我们知道此处的invoker为com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker,对应的invoke方法:

public Result invoke(Invocation invocation) throws RpcException {
	Result result = null;
        // 查询mock值,查询顺序methodName[sayHello].mock -> mock -> default.mock, 默认为false,即不进行mock
        String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim(); 
        if (value.length() == 0 || value.equalsIgnoreCase("false")){
        	// 不进行mock的话直接调用后面的invoker
        	result = this.invoker.invoke(invocation);
        } else if (value.startsWith("force")) {
        	if (logger.isWarnEnabled()) {
        		logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " +  directory.getUrl());
        	}
        	//如果值为force,表示强制mock,即不访问远端方法,直接调用mock数据
        	result = doMockInvoke(invocation, null);
        } else {
        	// 其他的值,则是先调用后面的invoker,如果失败且不是业务错误时使用mock数据,
                // 非业务错误包含:网络错误、超时错误、禁止访问错误、序列化错误及其他未知的错误,
                // 业务错误则是接口实现类中的方法抛出的错误,如sayHello调用时产生异常
               try {
        		result = this.invoker.invoke(invocation);
        	}catch (RpcException e) {
				if (e.isBiz()) {
					throw e;
				} else {
					if (logger.isWarnEnabled()) {
		        		logger.info("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " +  directory.getUrl(), e);
		        	}
					result = doMockInvoke(invocation, e);
				}
			}
        }
        return result;
}

        mock支持的配置方式包含多种:

       1、return xxx  (注意这里的xxx区分大小写)

               empty: 返回空对象,根据Type返回空对象(单纯的new xxx()),如果是基础类型则返回对应的默认值

               null: 返回null

               true/false: 返回boolean

               开头+结尾都是双引号(")或单引号(')的字符串:返回去掉开头、结尾字符的字符串

               数字:返回数字

               以{开头:按json格式解析,并将结果放入Map, 最终返回一个对象

               以[开头:按json格式解析,并将结果放入List,最终返回一个List

               其他:直接返回xxx

        2、true/default

               根据{interfaceName}Mock构造类名(如com.alibaba.dubbo.demo.DemoServiceMock),Mock类必须带有无参构造方法,如果失败则初始化失败,如果成功则在需要mock时会调用该类的对应方法

        3、force:return+null

               直接往注册中心写入该mock规则,在配置文件中不能使用这种方式。主要是为了临时降级。

    private Result doMockInvoke(Invocation invocation,RpcException e){
		Result result = null;
    	Invoker<T> minvoker ;
    	// directory根据invocation中是否有Constants.INVOCATION_NEED_MOCK,来判断获取的是一个normal invoker 还是一个 mock invoker 
        // 如果directorylist 返回多个mock invoker,只使用第一个invoker.
    	List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);
		if (mockInvokers == null || mockInvokers.size() == 0){
                        // 如果没有则根据url创建一个MockInvoker
			minvoker = (Invoker<T>) new MockInvoker(directory.getUrl());
		} else {
			minvoker = mockInvokers.get(0);
		}
		try {
			result = minvoker.invoke(invocation);
		} catch (RpcException me) {
			if (me.isBiz()) {
				result = new RpcResult(me.getCause());
			} else {
				throw new RpcException(me.getCode(), getMockExceptionMessage(e, me), me.getCause());
			}		
		} catch (Throwable me) {
			throw new RpcException(getMockExceptionMessage(e, me), me.getCause());
		}
		return result;
    }
        MockInvoker的invoke方法:

    public Result invoke(Invocation invocation) throws RpcException {
    	String mock = getUrl().getParameter(invocation.getMethodName()+"."+Constants.MOCK_KEY);
    	if (invocation instanceof RpcInvocation) {
    		((RpcInvocation) invocation).setInvoker(this);
    	}
    	if (StringUtils.isBlank(mock)){
    		mock = getUrl().getParameter(Constants.MOCK_KEY);
    	}
    	
    	if (StringUtils.isBlank(mock)){
    		throw new RpcException(new IllegalAccessException("mock can not be null. url :" + url));
    	}
        mock = normallizeMock(URL.decode(mock));
        if (Constants.RETURN_PREFIX.trim().equalsIgnoreCase(mock.trim())){
                // 如果只含return则返回null
        	RpcResult result = new RpcResult();
        	result.setValue(null);
        	return result;
        } else if (mock.startsWith(Constants.RETURN_PREFIX)) {
            // 如果是return 开头,则根据上面介绍的规则返回值
            mock = mock.substring(Constants.RETURN_PREFIX.length()).trim();
            mock = mock.replace('`', '"');
            try {
                Type[] returnTypes = RpcUtils.getReturnTypes(invocation);
                Object value = parseMockValue(mock, returnTypes);
                return new RpcResult(value);
            } catch (Exception ew) {
            	throw new RpcException("mock return invoke error. method :" + invocation.getMethodName() + ", mock:" + mock + ", url: "+ url , ew);
            }
        } else if (mock.startsWith(Constants.THROW_PREFIX)) {
            // throw开头则按照throw后的字符串构造异常
        	mock = mock.substring(Constants.THROW_PREFIX.length()).trim();
            mock = mock.replace('`', '"');
            if (StringUtils.isBlank(mock)){
            	throw new RpcException(" mocked exception for Service degradation. ");
            } else { //用户自定义类
            	Throwable t = getThrowable(mock);
				throw new RpcException(RpcException.BIZ_EXCEPTION, t);
            }
        } else { //调用实现类的对应方法,如实现类为com.alibaba.dubbo.demo.DemoServiceMock,调用sayHello时其mock方法也为sayHello
             try {
                 Invoker<T> invoker = getInvoker(mock);
                 return invoker.invoke(invocation);
             } catch (Throwable t) {
                 throw new RpcException("Failed to create mock implemention class " + mock , t);
             }
        }
    }

    private Invoker<T> getInvoker(String mockService){
              // Invoker由反射生成,需要缓存生成的Invoker(否则效率低)
        Invoker<T> invoker =(Invoker<T>) mocks.get(mockService);
        if (invoker != null ){
            return invoker;
        } else {
                       // 如果配置为默认,则mock类为原类名+Mock,如com.alibaba.dubbo.demo.DemoService的mock类为com.alibaba.dubbo.demo.DemoServiceMock 
                       // 如果非默认配置,则按照配置的字符串创建类
                Class<T> serviceType = (Class<T>)ReflectUtils.forName(url.getServiceInterface());
            if (ConfigUtils.isDefault(mockService)) {
                mockService = serviceType.getName() + "Mock";
            }
            // 创建mock类并判断mock类是否是原类的子类
            Class<?> mockClass = ReflectUtils.forName(mockService);
            if (! serviceType.isAssignableFrom(mockClass)) {
                throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());
            }
              // 这里copy的,应该是作者写重复了
            if (! serviceType.isAssignableFrom(mockClass)) {
                throw new IllegalArgumentException("The mock implemention class " + mockClass.getName() + " not implement interface " + serviceType.getName());
            }
            try {
                               // 创建实例,并创建对应的代理
                T mockObject = (T) mockClass.newInstance();
                invoker = proxyFactory.getInvoker(mockObject, (Class<T>)serviceType, url);
                               // 只缓存10000个Invoker
                if (mocks.size() < 10000) {
                    mocks.put(mockService, invoker);
                }
                return invoker;
            } catch (InstantiationException e) {
                throw new IllegalStateException("No such empty constructor \"public " + mockClass.getSimpleName() + "()\" in mock implemention class " + mockClass.getName(), e);
            } catch (IllegalAccessException e) {
                throw new IllegalStateException(e);
            }
        }
    }

        此时的url protocol,proxyFactory最终调用包装了JavassistProxyFactory的StubProxyFactoryWrapper,这里的getInvoker方法直接调用了JavassistProxyFactory.getInvoker(proxy, type, url), JavassistProxyFactory根据mock类生成的代理类,在invoke方法中调用mock类的对应方法。

        mock 逻辑并不复杂,但是在系统降级时非常有用。至于如何在线上修改配置进行降级,我们后面再聊。


你可能感兴趣的:(源码分析,DUBBO,mock)