dubbo解析-回声测试实现原理

本文基于dubbo 2.7.5版本代码

dubbo的回声测试功能作用是,当客户端正式访问服务端前通过回声测试,以了解服务是否可用或者网络是否畅通,而且还可以使用该功能对服务监控。
dubbo的每个服务都自动实现了EchoService接口,使用方式也非常简单。

public class DubboClient {
	@Reference
    private RemoteService remoteService;

    public void process(){
      	...
		EchoService echoService = (EchoService)remoteService;
		//方法的入参是什么,返回值就是什么
		String status = (String)echoService.$echo("OK");
		
		...
	}
}

下面介绍一下该功能是如何实现的。

客户端实现

当看到下面代码时,我立刻产生了一个疑问,为什么RemoteService的对象可以强转为EchoService,下面先看一下这个问题。

EchoService echoService = (EchoService)remoteService;

dubbo客户端通过代理对象访问远程服务,所以remoteService其实是一个代理对象。
代理对象是在ReferenceBean的createProxy方法中创建的。

public synchronized void init() {
	....
	ref = createProxy(map);
	....
}

private T createProxy(Map<String, String> map) {
	...
	return (T) PROXY_FACTORY.getProxy(invoker);
}
//PROXY_FACTORY.getProxy(invoker)最终会调用到下面的方法上
public <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException {
        Set<Class<?>> interfaces = new HashSet<>();
        String config = invoker.getUrl().getParameter(INTERFACES);
        if (config != null && config.length() > 0) {
            String[] types = COMMA_SPLIT_PATTERN.split(config);
            if (types != null && types.length > 0) {
                for (int i = 0; i < types.length; i++) {
                    interfaces.add(ReflectUtils.forName(types[i]));
                }
            }
        }

        if (!GenericService.class.isAssignableFrom(invoker.getInterface()) && generic) {
            interfaces.add(com.alibaba.dubbo.rpc.service.GenericService.class);
        }

        interfaces.add(invoker.getInterface());
        //INTERNAL_INTERFACES是一个Class数组,包含两个Class对象:
        //EchoService.class和Destroyable.class
        interfaces.addAll(Arrays.asList(INTERNAL_INTERFACES));

        return getProxy(invoker, interfaces.toArray(new Class<?>[0]));
    }

从INTERNAL_INTERFACES可以看出代理对象是实现了EchoService接口的,这样可以通过强制转换访问$echo方法。

服务端实现

客户端通过代理对象实现EchoService接口,这样客户端可以像调用其他方法一样访问远程服务。
服务端收到$echo调用时,首先解析请求参数,请求参数显示调用的是RemoteService接口的$echo方法,因此服务端根据请求参数找到的Invoker对象其实是RemoteService接口的Invoker对象。接着调用Invoker对象里面的Filter过滤器,其中调用的第一个过滤器便是EchoFilter。

@Activate(group = CommonConstants.PROVIDER, order = -110000)
public class EchoFilter implements Filter {
    @Override
    public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
        if (inv.getMethodName().equals($ECHO) && inv.getArguments() != null && inv.getArguments().length == 1) {
        	//如果发现调用的方法是$echo,且入参只有一个,那么便直接生成Result对象,不在往后调用
            return AsyncRpcResult.newDefaultAsyncResult(inv.getArguments()[0], inv);
        }
        return invoker.invoke(inv);
    }
}
	public static AsyncRpcResult newDefaultAsyncResult(Object value, Invocation invocation) {
        return newDefaultAsyncResult(value, null, invocation);
    }
    public static AsyncRpcResult newDefaultAsyncResult(Object value, Throwable t, Invocation invocation) {
        CompletableFuture<AppResponse> future = new CompletableFuture<>();
        AppResponse result = new AppResponse();
        if (t != null) {
            result.setException(t);
        } else {
        	//直接根据inv.getArguments()[0]生成result对象,并将其返回到客户端
            result.setValue(value);
        }
        future.complete(result);
        return new AsyncRpcResult(future, invocation);
    }

从上面的代码可以看出,EchoFilter只在服务端生效(group = CommonConstants.PROVIDER)。
当调用的方法是$echo,且入参只有一个,那么该请求便会被EchoFilter拦截,并将入参设置到result对象中,然后将result对象返回至客户端。

自动检测

dubbo没有提供自动回声测试的功能,不过我们可以通过编码自行实现。

  1. 如果类中引用了远程服务,那么在该类的构造方法中调用回声测试,该方式可以在客户端启动时使用,如果测试失败,可以终止客户端的启动;
  2. 将每个引用的服务注册到一个服务集合中,启动定时任务进行回声测试,以监控服务是否正常;

不过我觉得回声测试的功能用在监控场景下比较有意义,因为客户端启动时,可以设置自动检测是否有可用的服务。

©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页