dubbo本地服务发布与引用

服务本地发布流程

dubbo本地服务发布

对于服务本地发布来说,主要分为如下三个步骤

  • 根据要发布服务的接口,方法名称以及方法参数组装URL,之前的文章有提到过,URL在dubbo框架中至关重要
  • 利用JavassitProxyFactory以及Wrapper创建Invoker的过程
  • 通过InjvmProtocol创建exporter的过程

dubbo://10.15.20.142:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&loadbalance=roundrobin&methods=sayHello&owner=william&pid=10445&side=provider&timestamp=1494254502368

第一个步骤没什么可说的,上面为远程服务暴露的URL示例。下面从第二个步骤开始解释。JavassitProxyFactory是dubbo中默认创建Proxy和Invoker的类,除此之外,开发者也可以通过URL参数指定采用JdkProxyFactory来创建Proxy和Invoker,至于为什么默认采用JavassitProxyFactory,在此不做讨论,之后会专门研究。JavassitProxyFactory在创建Invoker时,首先创建了哥Wrapper对象,那么这个Wrapper是什么呢?这个Wrapper实际上是对所发布服务类的一个包装,主要包装了对这个类Field的获取和赋值,以及Method的调用。这个Wrapper主要有三个方法,如下:

public Object getPropertyValue(Object instance, String pn) throws NoSuchPropertyException, IllegalArgumentException;  
public void setPropertyValue(Object instance, String pn, Object pv) throws NoSuchPropertyException, IllegalArgumentException;  
public Object invokeMethod(Object instance, String mn, Class<?>[] types, Object[] args) throws NoSuchMethodException, InvocationTargetException;  

从方法的名字就可以看出来这三个方法各自的作用,这里就不再赘述。值得一提的是,dubbo在创建这个Wrapper类的时候,是根据每个具体的类动态生成的(听起来挺高大上的,其实就是根据具体的类的Field和Method,通过字符串拼接的方式创建这个类,里面有很多的if else判断当前调用的是哪个方法,这段代码看起来相当恶心)。然后当触发上述Invoker的invoke方法时,invoke便会调用对应wrapper的invokeMethod的方法,通过传入类实例,方法名,方法参数类型以及参数值便可以实现正确方法的调用。

Exporter实际上是对Invoker的封装,其中除了封装Invoker以外,还有一个重要的成员变量时exporterMap,其指向对应Protocol中的exporterMap,其中以所要发布服务的接口名称为key,对应的exporter为value进行存储。这样,当consumer端传过来所要引用的服务时,便可以通过接口名称找到对应的Exporter,进而找到Invoker,从而实现真正方法的调用。

服务本地引用流程

dubbo本地服务引用

服务本地引用主要可以分为如下四个步骤:

  • 根据所要引用服务的接口,方法,方法参数等信息,创建URL对象
  • 通过对应的Protocol创建Invoker实例,本例中由于是本地服务引用,因此采用InjvmProtocol创建InjvmInvoker对象
  • 利用JavassitProxyFactory的getProxy方法创建所要引用服务接口的Proxy对象
  • 当通过上述代理对象调用接口方法时,会触发InvokerInvocationHandler的invoke()方法,进而调用对应Invoker的invoke方法,由于是本地服务引用,在invoke方法中,可以直接根据接口名称获取对应Exporter,进而调用真正服务的方法,获取结果

上述步骤中,除了第三个实现起来复杂一些以外,其余三个不管从流程还是实现上都比较简单,不再赘述,这里主要对步骤三做一个详细解释。下面为创建代理的代码。

public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {  
    return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}

JavassitProxyFactory通过调用Proxy类的getProxy方法创建代理实例,然后通过newInstance()方法传入InvocationHandler创建代理实例,下面将分析这个过程是怎么实现的,包括代理的创建以及方法的拦截过程。首先看下面创建代理的代码:

ClassGenerator ccp = null, ccm = null;  
// create ProxyInstance class.
String pcn = pkg + ".proxy" + id;  
ccp.setClassName(pcn);  
ccp.addField("public static java.lang.reflect.Method[] methods;");  
ccp.addField("private " + InvocationHandler.class.getName() + " handler;");  
ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{ InvocationHandler.class }, new Class<?>[0], "handler=$1;");  
ccp.addDefaultConstructor();  
Class<?> clazz = ccp.toClass();  
clazz.getField("methods").set(null, methods.toArray(new Method[0]));

// create Proxy class.
String fcn = Proxy.class.getName() + id;  
ccm = ClassGenerator.newInstance(cl);  
ccm.setClassName(fcn);  
ccm.addDefaultConstructor();  
ccm.setSuperClass(Proxy.class);  
ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");  
Class<?> pc = ccm.toClass();  
proxy = (Proxy)pc.newInstance();  

这里采用ClassGenerator类创建了两个类,ClassGenerator是类似于CGLIB的类生成器,都是采用继承的方式生成新类。仔细观察上述生成Proxy的代码,可以发现,当JavassitProxyFactory调用newInstance(InvocationoHandler)方法时,实际上是创建ProxyInstance对象(注意pcn和fcn)。那么这个ProxyInstance,即pcn实现方法的拦截的呢?请看如下代码:

ccp.addInterface(ics[i]);

for( Method method : ics[i].getMethods() )  
{
    String desc = ReflectUtils.getDesc(method);
    if( worked.contains(desc) )
        continue;
    worked.add(desc);

    int ix = methods.size();
    Class<?> rt = method.getReturnType();
    Class<?>[] pts = method.getParameterTypes();

    StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
    for(int j=0;j<pts.length;j++)
        code.append(" args[").append(j).append("] = ($w)$").append(j+1).append(";");
    code.append(" Object ret = handler.invoke(this, methods[" + ix + "], args);");
    if( !Void.TYPE.equals(rt) )
        code.append(" return ").append(asArgument(rt, "ret")).append(";");

    methods.add(method);
    ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());
}

上述这段代码是构造ProxyInstance的主要过程,分析代码可以发现,这段代码是对所有接口的一个实现过程,而所有方法只有一个实现,那就是调用handler.invoke()方法,而handler是什么呢?handler正是上文中创建ProxyInstance时传进来的InvokerInvocationHandler。看到这里就明白了,代理的拦截是通过在实现接口方法时调用handler.invoke()方法来完成的。这样,当客户端通过代理对象调用接口方法时,实际上是调用handler.invoke()方法,以上就是JavassitProxyFactory实现代理拦截的过程。

这篇文章主要是记录dubbo本地服务的发布与引用,仔细分析下来还挺简单的,下面便会进入到dubbo的主要部分——远程服务的发布与引用。

Shaohang Zhao

Read more posts by this author.