在Dubbo的服务发布过程中,Invoker的构造是一个重要的步骤,Invoker代表的是一个可以调用的服务的接口。它的定义如下:

public interface Invoker<T> extends Node {/*** get service interface.** @return service interface.*/Class<T> getInterface();/*** invoke.** @param invocation* @return result* @throws RpcException*/Result invoke(Invocation invocation) throws RpcException;}

invoke方法就是代表一次RPC调用,Invocation类代表的是一次调用需要的参数。为了实现通用性,以及实现很多RPC相关的附加功能,在服务端对一个接口实现,不是机械地实现一个Invoker接口,而是实现了一个Invoker的调用链。本文主要就是分析这个调用链是如何构成的。(本文假设读者已经对Dubbo的基本配置和使用有所了解,对涉及到的基础知识不再讲解)

首先是ServiceConfig这个类的doExportUrlsFor1Protocol方法中的一段代码

                 for (URL registryURL : registryURLs) {//if protocol is only injvm ,not registerif (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {continue;}url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));URL monitorUrl = loadMonitor(registryURL);if (monitorUrl != null) {url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());}if (logger.isInfoEnabled()) {logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);}// For providers, this is used to enable custom proxy to generate invokerString proxy = url.getParameter(PROXY_KEY);if (StringUtils.isNotEmpty(proxy)) {registryURL = registryURL.addParameter(PROXY_KEY, proxy);}Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);Exporter<?> exporter = protocol.export(wrapperInvoker);exporters.add(exporter);}

这个for循环是遍历配置的所有的注册中心,进行服务的发布。下面看循环体的内容,前面的内容主要是对配置进行一些处理,之后到了下面这一行

Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));

这个PROXY_FACTORY定义

 /*** A {@link ProxyFactory} implementation that will generate a exported service proxy,the JavassistProxyFactory is its* default implementation*/private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

这个对象是一个ProxyFactory接口,它获取的是这个接口的AdaptiveExtension对象。关于Dubbo的SPI接口的Adaptive对象,它是一种接口的自适应机制。例如ProxyFactory接口的定义

@SPI("javassist")
public interface ProxyFactory {/*** create proxy.** @param invoker* @return proxy*/@Adaptive({PROXY_KEY})<T> T getProxy(Invoker<T> invoker) throws RpcException;/*** create proxy.** @param invoker* @return proxy*/@Adaptive({PROXY_KEY})<T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;/*** create invoker.** @param <T>* @param proxy* @param type* @param url* @return invoker*/@Adaptive({PROXY_KEY})<T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;}

生成的Adaptive对象的每一个方法,会根据Adaptive注解,从参数中获取对应实现在SPI配置中的key,从而获取到对应的实现类,再调用实现类对此方法的实现。比如说这个类的实现

package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException {if (arg2 == null) throw new IllegalArgumentException("url == null");org.apache.dubbo.common.URL url = arg2;String extName = url.getParameter("proxy", "javassist");if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);return extension.getInvoker(arg0, arg1, arg2);}public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0, boolean arg1) throws org.apache.dubbo.rpc.RpcException {if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");org.apache.dubbo.common.URL url = arg0.getUrl();String extName = url.getParameter("proxy", "javassist");if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);return extension.getProxy(arg0, arg1);}public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");org.apache.dubbo.common.URL url = arg0.getUrl();String extName = url.getParameter("proxy", "javassist");if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);return extension.getProxy(arg0);}
}

通过源码看出,getInvoker这个方法,如果没有修改默认配置的话,会获取到配置为javassist为key的SPI实现类。它就是JavassistProxyFactory

public class JavassistProxyFactory extends AbstractProxyFactory {@Override@SuppressWarnings("unchecked")public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));}@Overridepublic <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {// TODO Wrapper cannot handle this scenario correctly: the classname contains '$'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);}};}}

为什么这个类叫做JavassistProxyFactory呢?因为getInvoker这个方法的第一步就是获取一个包裹类,这个包裹类是用Javassist这个第三方库实现的。简单地说,就是在程序运行的过程中动态地生成一个类的源代码,这个类是实现了T这个接口类型的,再在运行时将这个类的源码编译成字节码并加载到JVM中以供使用。

那为什么不直接使用这个接口的实现类,而是还要包一层呢?主要是为了对一些异常情况做通用化的处理,这样能减少应用层的工作量。例如

/*** @param o 实现类的对象 * @param n 方法名* @param p 参数类型列表* @param v 参数对象列表*/
public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException { org.apache.dubbo.demo.DemoService w; try{ w = ((org.apache.dubbo.demo.DemoService)$1); } catch(Throwable e) { throw new IllegalArgumentException(e); } try{ if( "sayHello".equals( $2 )  &&  $3.length == 1 ) {  return ($w)w.sayHello((java.lang.String)$4[0]); } } catch(Throwable e) {      throw new java.lang.reflect.InvocationTargetException(e);  } throw new org.apache.dubbo.common.bytecode.NoSuchMethodException("Not found method \""+$2+"\" in class org.apache.dubbo.demo.DemoService.");
}

这是对DemoService这个接口的包装类的源码。(请参考Dubbo源码中dubbo-demo中的实例)它首先通过强制类型转换检验了传入实现类是否是对应接口的实现类,之后检验了方法名和参数类型列表长度是否正确,如果正确再调用方法。

获取到包装类之后,利用它生成了一个AbstractProxyInvoker的抽象子类。并实现了它的doInvoker方法,里面正是利用这个包装类进行了调用。这个AbstractProxyInvoker是一个通用的Invoker实现类,实现了一些通用的逻辑,它实现RPC调用的方法invoke

 @Overridepublic Result invoke(Invocation invocation) throws RpcException {try {// 调用RPC实现获得结果Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());// 将结果包装为一个CompletableFutureCompletableFuture<Object> future = wrapWithFuture(value, invocation);// 构造一个未完成的RPC调用结果AsyncRpcResult asyncRpcResult = new AsyncRpcResult(invocation);// 采用异步编程思维,当future完成时执行传入的函数future.whenComplete((obj, t) -> {AppResponse result = new AppResponse();if (t != null) {if (t instanceof CompletionException) {result.setException(t.getCause());} else {result.setException(t);}} else {result.setValue(obj);}// 结果完成asyncRpcResult.complete(result);});// 这里返回的可能是未完成的结果return asyncRpcResult;} catch (InvocationTargetException e) {if (RpcContext.getContext().isAsyncStarted() && !RpcContext.getContext().stopAsync()) {logger.error("Provider async started, but got an exception from the original method, cannot write the exception back to consumer because an async result may have returned the new thread.", e);}return AsyncRpcResult.newDefaultAsyncResult(null, e.getTargetException(), invocation);} catch (Throwable e) {throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);}}

wrapWithFuture方法

 private CompletableFuture<Object> wrapWithFuture (Object value, Invocation invocation) {// if (RpcContext.getContext().isAsyncStarted()) {return ((AsyncContextImpl)(RpcContext.getContext().getAsyncContext())).getInternalFuture();} else if (value instanceof CompletableFuture) {return (CompletableFuture<Object>) value;}return CompletableFuture.completedFuture(value);}

以上是一个RPC调用在服务端包装之后的Invoker。只要拿到这个对象,已经可以完成一次RPC的最底层调用(也就是调用到实际的接口实现类)。

下面再看这行代码

DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

这里传入的invoker参数就是上面获取到的invoker,这里由把它和这个ServiceConfig本身传给DelegateProviderMetaDataInvoker这个类的构造方法。那这个DelegateProviderMetaDataInvoker有什么用呢?

/**** A Invoker wrapper that wrap the invoker and all the metadata (ServiceConfig)*/
public class DelegateProviderMetaDataInvoker<T> implements Invoker {protected final Invoker<T> invoker;private ServiceConfig metadata;

这个类其实就是把ServiceConfig作为元数据也封装了进去,这样如果调用的时候需要这些数据就可以很方便的拿到了。

总结

本文主要是分析了服务端服务发布过程中,在最常用的场景(采用注册中心)的两行重要的代码,也就是生成Invoker。因为发布的过程非常复杂,如果通过一篇文章讲所有内容的话,信息量太大。所以一篇文章只是重点讲一个点。后续会讲解通过protocol发布服务端,包括建立Invoker调用链,建立服务端服务器等内容。

Dubbo服务端服务发布(一)Invoker创建相关推荐

  1. mcrpg服务器文件,【服务端发布】我的世界勇士RPG服务端重新发布!

    我的世界勇士RPG服务端重新发布! 不好意思 上次因为链接掉了 这次重新发布 因为正处于学习状态 所以没怎么看麦块 让大家久等了 ! 请大家耐心看完帖子好么? 好玩的RPG服务端! 大型生存RPG ! ...

  2. nuxt2,服务端渲染应用框架, 从创建开发到部署上线

    文章目录 前言 一.创建一个nuxt项目 二.目录解读 三.新建页面,路由跳转 四.组件的使用 五.插件的使用 六.异步数据和代理,nuxt中使用axios和proxy代理 七.nuxt Seo优化 ...

  3. 永恒之塔linux服务端,服务端类 - 永恒之塔服务端 - 67pp网站

    软件名称 软件大小 更新时间 人 气 本模拟器采用OPEN-AIONXEMU官方源码制作,并和官方同步更新.本版本支持最新的国服2.5版本(主神的感召)下载地址1:http://dl.dbank.co ...

  4. 从源码全面解析 dubbo 服务端服务调用的来龙去脉

  5. 传奇服务端服务端运行7个窗口的各窗口功能讲解

    大家都知道打开传奇版本里的游戏引擎后,就会弹出7各窗口,下面给大家讲解下各窗口的功能定义 GameCenter是游戏服务器启动程序,负责将DBServer.exe处转来的客户 端转发到相应的M2Ser ...

  6. nfs服务端服务停掉,导致的客户端访问挂载目录卡死的情况

    为什么80%的码农都做不了架构师?>>>    环境描述: A 机器(192.168.1.201), B机器上挂载了A机器上的storage目录 到本机的storage目录(命令: ...

  7. dubbo消费端如何找到服务端对象,进行方法调用的

    关于该问题,要从以下几点点出发 消费端如何生成代理对象的 dubbo的ReferenceBean实现了InitializingBean,这是Spring中Bean的生命周期的方法,所以生成代理对象的逻 ...

  8. 3.菜鸟教你一步一步开发 web service 之 axis 服务端创建

    转自:https://blog.csdn.net/shfqbluestone/article/details/37610601 第一步,新建一个工程,如图: 选 Java 写一个工程名,选择好工程路径 ...

  9. cocos2d-lua ARPG手机游戏《烈焰遮天》(客户端+服务端+数据库)发布说明

    服务器发布流程及其规范 1,环境准备         a, mvn命令行:从\\10.21.210.161\share\tools\apache-maven-3.1.1-bin.tar.gz取出安装包 ...

最新文章

  1. 基于双目事件相机的视觉里程计
  2. Linux 组调度学习
  3. c++ primer 5th,练习11.19,编写代码验证
  4. MySQL InnoDB索引介绍及优化
  5. 2021牛客暑期多校训练营4 E-Tree Xor(异或+思维+区间交 or Trie树)
  6. 【Redis】三、Redis安装及简单示例
  7. windows软链接的建立及删除
  8. 2.5D休闲娱乐生活类插画素材,给设计添彩!
  9. 如何在请求转发的时候对url解码_Java技术分享:Forward和Redirect这两种转发方式的区别...
  10. linux正则表达式_Linux 中几个正则表达式的用法
  11. python framework jdon_python – Django Rest Framework和JSONField
  12. SpringBoot日常游玩---当你需要在一个静态方法中使用一个带有其他@Autowried的实现类的时候怎么办
  13. 小米2s安卓10刷机包_小米10刷机包
  14. 微信小程序生成海报图片导出相册
  15. jsp技术被淘汰了?那还要不要学它?
  16. JAVA类似ABP框架_【Net】ABP框架学习之它并不那么好用
  17. 【Python】python中[-1]、[:-1]、[::-1]、[n::-1]使用方法;random.choice()
  18. 洛谷 P1338 末日的传说 解题报告
  19. CESI: Canonicalizing Open Knowledge Bases using Embeddings and Side Information
  20. Office系列---将Office文件(Word、PPT、Excel)转换为PDF文件,提取Office文件(Word、PPT)中的所有图片

热门文章

  1. AndroidInputSystem
  2. IDEA+Java控制台实现医院管理系统
  3. Python语言程序设计(嵩天老师)-期末考试3.2-站队顺序输出
  4. 正斜杠(/)反斜杠(\)简单区别
  5. 英语学习笔记(二)语法
  6. 【转】DDR3中的ODT
  7. mysql权限问题_Mysql 数据库的权限问题
  8. 欧几里得、扩展的欧几里得算法
  9. 2019年第十届山东省acm省赛总结
  10. 张俊林:对比学习研究进展精要