Dubbo源码分析(2)—— 服务是如何暴露的

前言

上一篇文章解释了dubbo-spi的实现原理,这篇文章介绍如何暴露一个接口服务

正文

刚开始使用dubbo的使用,使用了XML配置去配置服务:

表示了DemoService这个接口,依赖了domoService这个bean,表示我们使用DemoService这个接口去请求dubbo服务器,会调用DemoServiceImpl这个实现类去处理。

dubbo:service是dubbo自定义的xml方式,bean标签则是属于spring,而spring也提供了自定义xml解析方式,,检查一下dubbo-config-spring这个包。

可以看到dubbo.xsd文件,里面规定了dubbo-xml所有标签以及参数:

看spring.handlers内部填写的解析类

使用了DubboBeanDefinitionParser,继承spring的BeanDefinitionParser,用于xml的解析

这个类里面内容写得很混乱,把所有的配置项全融合在一起写,大体思路就是解析xml,根据传入的class类型作出不同的解析,然后将类实例化并且给set或者is开头的方法注入值;根据上一张图片可以看出,service配置是由ServiceBean这个类来解析的。

ServiceBean这个类的继承和实现接口关系如下:

继承了spring的一些接口都与bean相关,而真正有用的是ApplicationListener,这个接口是一个Listener接口,ContextRefreshedEvent表明了是在这个类初始化完毕时执行该方法

查看该方法:

在ServiceBean初始化完毕后,检查状态,然后开始暴露服务

export方法交给了它的父类去执行,ServiceBean只是作为一个bean存在,真正干活的是ServiceConfig类

可以看到这就是暴露服务的核心方法,相比于xml配置方法,还有一种使用API配置方法,看一下xml和api配置的对比:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
context.start();
ServiceConfig<DemoService> serviceServiceConfig = new ServiceConfig<DemoService>();
System.in.read(); // press any key to exit
复制代码

这是xml配置方法的启动类

ServiceConfig<DemoService> serviceConfig = new ServiceConfig<DemoService>();
serviceConfig.setApplication(new ApplicationConfig("app"));
serviceConfig.setRegistry(new RegistryConfig("zookpeer://127.0.0.1:2181"));
serviceConfig.setInterface(DemoService.class);
serviceConfig.setRef(new DemoServiceImpl());
serviceConfig.export();
System.in.read();
复制代码

只需要执行export方法,就能将dubbo服务启动,说明ServiceConfig不仅做了服务暴露的事情,还把做了注册中心给一并添加了,先看export内部

同步方法,判断一些null值并获取,对延迟属性的判断,主要方法进入doExport

这一长段方法,主要是对监控、协议、注册中心、接口、实现类等的一些判断,然后进入 doExportUrls方法。

这里的protocols和registryURLs对应了xml上面的这两个部分,把注册中心配置转变成URL格式,其格式为:

主要是:ip-路径(类名)-param,param除了xml配置的一些属性外,还有dubbo版本,时间戳等。

在生成了注册中心URL和协议后,交给doExportUrlsFor1Protocol处理, 这个方法很长,前面很大一部分都是在构造URL,最后构造的URL如下所示:

根据不同协议和配置的参数,构造出这样一个URL,包含的信息全部在URL中。

最关键是这一段代码

proxyFactory是一个spi方法,默认使用JavassistProxyFactory,这里将上面的url的param和registryURL拼接在一起

这里将实现类用wrapper包装了一下,然后返回了一个AbstractProxyInvoker,内部调用了wrapper的invokeMethod方法,这个方法和反射类似

进入Wrapper内部

注意这个makeWrapper

使用Javassist,使用builder构造字节码,反编译出来是这样:

package org.apache.dubbo.common.bytecode;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import org.apache.dubbo.common.bytecode.ClassGenerator.DC;
import org.apache.dubbo.demo.DemoService;
public class Wrapper0 extends Wrapper implements DC {
public static String[] pns;
public static Map pts;
public static String[] mns;
public static String[] dmns;
public static Class[] mts0;
public String[] getPropertyNames() {
return pns;
}
public boolean hasProperty(String var1) {
return pts.containsKey(var1);
}
public Class getPropertyType(String var1) {
return (Class)pts.get(var1);
}
public String[] getMethodNames() {
return mns;
}
public String[] getDeclaredMethodNames() {
return dmns;
}
public void setPropertyValue(Object var1, String var2, Object var3) {
try {
DemoService var4 = (DemoService)var1;
} catch (Throwable var6) {
throw new IllegalArgumentException(var6);
}
throw new NoSuchPropertyException("Not found property \"" + var2 + "\" filed or setter method in class org.apache.dubbo.demo.DemoService.");
}
public Object getPropertyValue(Object var1, String var2) {
try {
DemoService var3 = (DemoService)var1;
} catch (Throwable var5) {
throw new IllegalArgumentException(var5);
}
throw new NoSuchPropertyException("Not found property \"" + var2 + "\" filed or setter method in class org.apache.dubbo.demo.DemoService.");
}
public Object invokeMethod(Object var1, String var2, Class[] var3, Object[] var4) throws InvocationTargetException {
DemoService var5;
try {
var5 = (DemoService)var1;
} catch (Throwable var8) {
throw new IllegalArgumentException(var8);
}
try {
if ("sayHello".equals(var2) && var3.length == 1) {
return var5.sayHello((String)var4[0]);
}
} catch (Throwable var9) {
throw new InvocationTargetException(var9);
}
throw new NoSuchMethodException("Not found method \"" + var2 + "\" in class org.apache.dubbo.demo.DemoService.");
}
public Wrapper0() {
}
}
复制代码

继承了Wrapper,实现了invokeMethod方法,根据方法的名称来反射调用方法,如果是多个方法那就是多个if操作而已

到目前为止,已经将需要暴露的服务实例包装起来了,下面就是将这个服务器暴露出去了。

所支持的协议如包名所见,默认为dubbo

调用Protocol的export方法暴露服务,默认使用dubbo

看DubboProtocol

主要是这个openServer方法

进入createServer

这里绑定了一个服务器和用于处理请求的hander

使用了tranposrt,属于dubbo-remoting层

tranposrt也是一个spi,默认使用netty,也可以跟换为其他比如http,根据xml配置构造成URL,选择不同的服务主要是在URL的param参数里面体现

打开一个nettyServer的方法,和经典netty方法差不多,主要做了一些封装。

到此为止,已经开启了一个netty服务器,绑定了指定端口号,并且将配置的service的实现类包装起来,通过netty服务器的请求,填入指定接口名称、方法名称以及参数,就可以获取invoker然后调用invokeMethod方法调用实现类的方法,实现了一次RPC

稀土掘金
我还没有学会写个人说明!
上一篇

企业级容器云平台的落地与实施

你也可能喜欢

评论已经被关闭。

插入图片