Dubbo服务暴露的流程
在Dubbo服务暴露中,需要被暴露的服务的接口类首先会通过proxyFactory代理工厂获得代理的对象invoker,而暴露之后的服务被调用,也将都会通过这个invoker来调用相关的类。
在dubbo中默认采用javassistProxyFactory来获取由javassist代理的invoker。
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {// TODO Wrapper类不能正确处理带$的类名final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);return new AbstractProxyInvoker<T>(proxy, type, url) {@Overrideprotected Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable {return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);}};
}
首先会根据所要代理的类名产生相应的wrapper来对所要代理的类进行包装。
在getWrapper中,会先根据所要包装的类是否已经被包装过。如果已经包装过,则会直接在来保存已经生成的包装类的map里去寻找,否则会直接生成新的包装类。
makeWrapper()根据传递的类获取新的包装类。
在makeWrapper()方法中,通过javassist根据所需要包装的类动态生成了包装类。由于Invoker的作用,大概在这里动态实现的最重要的方法应该是invokeMothed()方法。
if( hasMethod ){c3.append(" try{");
}
for( Method m : methods )
{if( m.getDeclaringClass() == Object.class ) //ignore Object's method.continue;String mn = m.getName();c3.append(" if( \"").append(mn).append("\".equals( $2 ) ");int len = m.getParameterTypes().length;c3.append(" && ").append(" $3.length == ").append(len);boolean override = false;for( Method m2 : methods ) {if (m != m2 && m.getName().equals(m2.getName())) {override = true;break;}}if (override) {if (len > 0) {for (int l = 0; l < len; l ++) {c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"").append(m.getParameterTypes()[l].getName()).append("\")");}}}c3.append(" ) { ");if( m.getReturnType() == Void.TYPE )c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");").append(" return null;");elsec3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");");c3.append(" }");mns.add(mn);if( m.getDeclaringClass() == c )dmns.add(mn);ms.put(ReflectUtils.getDesc(m), m);
}
if( hasMethod ){c3.append(" } catch(Throwable e) { " );c3.append(" throw new java.lang.reflect.InvocationTargetException(e); " );c3.append(" }");}c3.append(" throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c.getName() + ".\"); }");
在这段动态生成了invokeMethod的方法体。将会根据方法名调用包装类中真正所需要被调用的类的方法。
大致的invokeMethod()方法如下。
public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {dubbo.provider.hello.service.impl.HelloWorld w;try {w = ((dubbo.provider.hello.service.impl.HelloServiceImpl) $1);} catch (Throwable e) {throw new IllegalArgumentException(e);}try {if ("helloWorld".equals($2) && $3.length == 0) {w. helloWorld ();return null;}if ("getName".equals($2) && $3.length == 0) {return ($w)w. getNanme ();}} catch (Throwable e) {throw new java.lang.reflect.InvocationTargetException(e);}throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + $2 + "\" in class dubbo.provider.hello.service.impl.HelloServiceImpl.");}
可以见到,当外部代用被包装的类的相应的方法,都会在这里被找到方法并调用。
在得到了包装类之后,在javassistProxyFactory的getInvoker()方法最后会生成新的AbstractProxyInvoker(),重写了doInvoke()方法,会直接调用刚刚生成的包装类的invokeMethod()方法。
在得到invoker之后,会根据获得的invoker开始服务的暴露,通过protocol的export()方法。配置有filter或者listener的情况下,会在这里产生关于具体服务暴露操作的过滤与监听。值得一提的是,如果采用的是registry协议,那么并不会经过ProtocolListenerWrapper的监听,而是直接进入export()方法开始服务的暴露。
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {//export invokerfinal ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);//registry providerfinal Registry registry = getRegistry(originInvoker);final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);registry.register(registedProviderUrl);// 订阅override数据// FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl);overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);//保证每次export都返回一个新的exporter实例return new Exporter<T>() {public Invoker<T> getInvoker() {return exporter.getInvoker();}public void unexport() {try {exporter.unexport();} catch (Throwable t) {logger.warn(t.getMessage(), t);}try {registry.unregister(registedProviderUrl);} catch (Throwable t) {logger.warn(t.getMessage(), t);}try {overrideListeners.remove(overrideSubscribeUrl);registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener);} catch (Throwable t) {logger.warn(t.getMessage(), t);}}};
}
以上是registryProtocol的暴露服务export()方法的流程。
首先会通过doLocalExport()方法开始本地服务的暴露。
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker){String key = getCacheKey(originInvoker);ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);if (exporter == null) {synchronized (bounds) {exporter = (ExporterChangeableWrapper<T>) bounds.get(key);if (exporter == null) {final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);bounds.put(key, exporter);}}}return (ExporterChangeableWrapper<T>) exporter;
}
在本地暴露的过程中,首先会根据传入的invoker获取其提供方的url来获得key,根据获得的key来尝试获得ExporterChangeableWrapper,如果没有,则会先通过原本采用的protocol协议进行服务暴露,默认dubbo。
在dubboProtocol的export()方法中,会根据url生成server key,根据invoker和key生成新的dubboExporter并根据key和exporter存放在map里。但是最关键的是,将在这里调用openServer()方法开启与注册中心的连接。
在openServer中,如果已经建立过与服务器的连接,那么会直接在这里返回,否则会通过netty新建与注册中心的网络连接。
private ExchangeServer createServer(URL url) {//默认开启server关闭时发送readonly事件url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());//默认开启heartbeaturl = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);if (str != null && str.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))throw new RpcException("Unsupported server type: " + str + ", url: " + url);url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);ExchangeServer server;try {server = Exchangers.bind(url, requestHandler);} catch (RemotingException e) {throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);}str = url.getParameter(Constants.CLIENT_KEY);if (str != null && str.length() > 0) {Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();if (!supportedTypes.contains(str)) {throw new RpcException("Unsupported client type: " + str);}}return server;
}
在这里会通过生成ExchangeServer开始监听相应的channel并绑定对应的requestHandler进行相关的回调处理。
可见,在dubboProtocol的服务暴露过程中,完成了对网络监听的配置与开启。
在完成dubboProtocol的export()方法之后,回到RegistryProtocol的doLocalExport()方法,根据dubboProtocol暴露生成的dubboExporter()以及invoker生成新的ExportChangeableWrapper返回。
之后,通过getRegistry获得注册中心的实例。会根据配置的registryFactory生成对应的registry实例。
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {super(url);if (url.isAnyHost()) {throw new IllegalStateException("registry address == null");}String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);if (! group.startsWith(Constants.PATH_SEPARATOR)) {group = Constants.PATH_SEPARATOR + group;}this.root = group;zkClient = zookeeperTransporter.connect(url);zkClient.addStateListener(new StateListener() {public void stateChanged(int state) {if (state == RECONNECTED) {try {recover();} catch (Exception e) {logger.error(e.getMessage(), e);}}}});
}
以zookeeper为例,在其构造方法当中会根据url新生成一个zkClient得到与zookeeper注册中心的连接。
在得到了注册中心的实例之后,通过register()方法正式注册服务到注册中心当中去。
在调用注册方法的过程中,被注册的url将会在zookeeperRegistry的父类的父类abstractRegistry中保存被注册的url。然后在zookeeperRegistry的父类当中调用zookeeperRegistry的doRegistry()方法。也就说在zookeeperRegistry的doRegistry方法当中,会将要暴露的服务信息提交给注册中心。
之后,会向注册中心订阅这一服务,以保证注册数据变动时的自动推送。
Dubbo服务暴露的流程相关推荐
- dubbo服务暴露流程总结
这篇文章主要总结一下dubbo服务端启动的时候服务暴露过程,虽然官方网站和各种博客上已经有很多介绍服务暴露的帖子,但还是想把自己跟源码过程中遇到的问题和心得记录下来,算是个总结,并且本篇文章是基于du ...
- 面试官问我:解释一下Dubbo服务暴露
今天我们要分析的就是Dubbo的服务暴露过程,这个过程属于Dubbo的核心过程之一了,因为Dubbo的大体流程就是服务暴露->服务引用->服务消费这几个主流程,当然还会涉及到注册发现.负载 ...
- Dubbo服务暴露(导出)流程
Dubbo作为一个Rpc框架,服务端必须得将自己暴露出去,以便客户端的调用,所以我们来看一下dubbo是如何将服务进行暴露的. 首先我们知道,启动dubbo得进行一些配置,如下图所示的一些dubbo标 ...
- Dubbo——服务暴露过程分析
这篇文章来叙述对dubbo的服务暴露前的准备工作: 使用Spring配置文件,通过main方法来启动spring容器,来观察dubbo服务的启动过程. dubbo配置文件 <context:co ...
- dubbo服务暴露原理解析
配置解析 dubbo 的各个配置项,详细的可以参考官网 只有 group,interface,version 是服务的匹配条件,三者决定是不是同一个服务,其它配置项均为调优和治理参数 所有的配置最终都 ...
- Dubbo服务暴露原理
服务暴露原理 配置文件 IOC容器启动,加载配置文件的时候 Dubbo标签处理器,解析每一个标签 封装成对应的组件 service 解析service标签 将service标签信息,封装成Servic ...
- 深入解析 Dubbo 3.0 服务端暴露全流程
简介:随着云原生时代的到来,Dubbo 3.0 的一个很重要的目标就是全面拥抱云原生.正因如此,Dubbo 3.0 为了能够更好的适配云原生,将原来的接口级服务发现机制演进为应用级服务发现机制. 作者 ...
- Dubbo服务端暴露全流程
本文来说下Dubbo服务端暴露全流程 文章目录 概述 什么是应用级服务发现 服务端暴露全流程 暴露injvm协议的服务 注册service-discovery-registry协议 暴露Triple协 ...
- dubbo服务发布一之服务暴露
整体流程以调试 om.alibaba.dubbo.demo.provider.DemoProvider来演示dubbo服务的发布流程. 1.启动Spring容器 参照dubbo容器的启动, https ...
最新文章
- 【paddlepaddle速成】paddlepaddle图像分类从模型自定义到测试
- 用tinyxml创建xml文件
- SCCM部署前的IIS、WSUS等准备
- skin文件启用智能提示的小技巧
- HFSS学习笔记—19.HFSS模型导出dxf文件并绘制PCB
- 百度拼音输入法 v2.10.2.52 官方免费版
- vue 年月日时分秒毫秒
- 想转行学IT,Java怎么样?
- QQ群聊天记录统计分析 V0.2
- 服务器没有显示器能接笔记本吗,笔记本能连显示器吗,笔记本怎么才能接显示器(图文)...
- 虚拟串口软件VSPD的使用
- 安装RocketChat报错:npm WARN saveError ENOENT: no such file or directory, open ‘/tmp/bundle/programs/web.
- PyG搭建GAT实现节点分类
- 2022英特尔AI开发者大会视频专区
- SpringBoot 整合实现ActiveMQ
- Android 项目工程优化
- 操作系统实验(linux内核编译,添加系统调用,windows进程创建,脚本程序编写)
- 游戏+区块链,意义究竟在哪
- 中小企业外贸erp软件有什么特点?
- 2017.2.7 开涛shiro教程-第六章-Realm及相关对象(一)