Dubbo源码阅读笔记4

### 发布服务到本地

发布本地服务的代码在ServiceConfig.doExportUrlsFor1Protocol方法里

主要代码如下

// 通过动态代理工厂生成实现类调用器Invoker, Invoker相当于动态代理类
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

// 只是将代理和配置放到一起
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

// 发布服务到协议中,现在是InjvmProtocol
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);

默认用的动态代理工厂是JavassistProxyFactory

通过AbstractProxyInvoker类封装成Invoker

Wrapper是通过字节码技术给类增加了几个增强方法

想查看javassist生成的class源代码可以通过CtClass类的writeFile方法输出到本地文件,再反编译

// JavassistProxyFactory.java
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) {
        @Override
        protected Object doInvoke(T proxy, String methodName,
                                  Class<?>[] parameterTypes,
                                  Object[] arguments) throws Throwable {
            return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
        }
    };
}

JdkProxyFactory则是直接用反射调用

// JdkProxyFactory.java
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    return new AbstractProxyInvoker<T>(proxy, type, url) {
        @Override
        protected Object doInvoke(T proxy, String methodName,
                                  Class<?>[] parameterTypes,
                                  Object[] arguments) throws Throwable {
            Method method = proxy.getClass().getMethod(methodName, parameterTypes);
            return method.invoke(proxy, arguments);
        }
    };
}

接下来就是把invoker发布

用的协议是InjvmProtocol

injvm协议非常简单,不用监听端口什么的,直接返回InjvmExporter实例

到这里发布服务到本地已经完成了

// InjvmProtocol.java
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}

// InjvmExporter.java
class InjvmExporter<T> extends AbstractExporter<T> {

    private final String key;

    private final Map<String, Exporter<?>> exporterMap;

    InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
        super(invoker);
        this.key = key;
        this.exporterMap = exporterMap;
        exporterMap.put(key, this);
    }

    public void unexport() {
        super.unexport();
        exporterMap.remove(key);
    }
}

### 调用本地服务

服务引用在初始化时生成目标接口的代理实现

在ReferenceConfig.createProxy方法生成

// ReferenceConfig.java
if (isJvmRefer) {
    URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);

    // 通过服务协议获取服务调用器, refprotocol和发布时的protocol是同一实例
    invoker = refprotocol.refer(interfaceClass, url);
    if (logger.isInfoEnabled()) {
        logger.info("Using injvm service " + interfaceClass.getName());
    }
}

// InjvmProtocol.java
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
    // exporterMap保存了已发布的服务,因为发布和引用都用的同一个protocol实例,所以exporterMap也是同一个。
    return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap);
}

创建动态代理返回

默认用的动态代理工厂是JavassistProxyFactory,用字节码技术生成目标接口的代理类

如果使用JdkProxyFactory,则没有字节码生成类的步骤,直接动态代理调用Invoker类的invoke方法

// ReferenceConfig.java
// 创建服务代理
return (T) proxyFactory.getProxy(invoker);

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

// JdkProxyFactory.java
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
    return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
}

// 重点**
// javassist生成的类继承自Proxy,并实现了目标接口,目标方法会被实现一次,handler是上面的InvokerInvocationHandler实例
// 以下是生成的方法
public String sayHello(String $1) {
    Object[] args = new Object[1];
    args[0] = ($w)$1;
    Object ret = handler.invoke(this, methods[0], args);
    return (java.lang.String)ret;
}

// InvokerInvocationHandler.java
// 该类实现了InvocationHandler,目的是支持jdk动态代理
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String methodName = method.getName();
    Class<?>[] parameterTypes = method.getParameterTypes();
    if (method.getDeclaringClass() == Object.class) {
        return method.invoke(invoker, args);
    }
    if ("toString".equals(methodName) && parameterTypes.length == 0) {
        return invoker.toString();
    }
    if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
        return invoker.hashCode();
    }
    if ("equals".equals(methodName) && parameterTypes.length == 1) {
        return invoker.equals(args[0]);
    }
    return invoker.invoke(new RpcInvocation(method, args)).recreate();
}

最后在调用目标方法的时候,通过调用方invoker找到服务方invoker

// InjvmInvoker.java
// 此时在调用方invoker实例
public Result doInvoke(Invocation invocation) throws Throwable {
    // 通过exporterMap找到服务方exporter
    Exporter<?> exporter = InjvmProtocol.getExporter(exporterMap, getUrl());
    if (exporter == null) {
        throw new RpcException("Service [" + key + "] not found.");
    }
    RpcContext.getContext().setRemoteAddress(NetUtils.LOCALHOST, 0);
    // 拿出服务方invoker并调用方法
    return exporter.getInvoker().invoke(invocation);
}

原文地址:https://www.cnblogs.com/amwyyyy/p/8603830.html

时间: 2024-08-26 07:26:12

Dubbo源码阅读笔记4的相关文章

dubbo源码阅读笔记--服务调用时序

上接dubbo源码阅读笔记--暴露服务时序,继续梳理服务调用时序,下图右面红线流程. 整理了调用时序图 分为3步,connect,decode,invoke. 连接 AllChannelHandler.connected(Channel) line: 38 HeartbeatHandler.connected(Channel) line: 47 MultiMessageHandler(AbstractChannelHandlerDelegate).connected(Channel) line:

Dubbo源码阅读笔记3

### 扩展点加载(ExtensionLoader) 每一种类型的扩展点都有一个ExtensionLoader实例 变量说明 public class ExtensionLoader<T> { // dubbo服务扫描目录 private static final String SERVICES_DIRECTORY = "META-INF/services/"; // dubbo扩展点配置扫描目录(自定义扩展时使用此目录) private static final Stri

dubbo源码阅读笔记--暴露服务时序

本文许多内容来源于http://dubbo.io/Developer+Guide-zh.htm#DeveloperGuide-zh-%E8%B0%83%E7%94%A8%E9%93%BE,感谢作者的分享. dubbo的整体架构图中包含了很多内容,这里只分析服务暴露部分. 针对服务暴露,对上面的图做了些简化 时序图 分成六个阶段,下面分别列出每个阶段的调用堆栈. 1 初始化接口invoker JavassistProxyFactory.getInvoker(T, Class<T>, URL) l

Dubbo源码阅读笔记2

### 消费方初始化 消费方初始化的入口在ReferenceConfig类的get方法 前面基本和服务方的初始化一致 public class ReferenceConfig<T> extends AbstractReferenceConfig { private static final long serialVersionUID = -5864351140409987595L; private static final Protocol refprotocol = ExtensionLoa

CI框架源码阅读笔记3 全局函数Common.php

从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap引导文件都会最先引入全局函数,以便于之后的处理工作). 打开Common.php中,第一行代码就非常诡异: if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 上一篇(CI框架源码阅读笔记2 一切的入口 index

源码阅读笔记 - 1 MSVC2015中的std::sort

大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来 这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格式化,去掉或者展开用于条件编译或者debug检查的宏,依重要程度重新排序函数,但是不会改变命名方式(虽然MSVC的STL命名实在是我不能接受的那种),对于代码块的解释会在代码块前(上面)用注释标明. template<class _RanIt, class _Diff, class _Pr> in

CI框架源码阅读笔记5 基准测试 BenchMark.php

上一篇博客(CI框架源码阅读笔记4 引导文件CodeIgniter.php)中,我们已经看到:CI中核心流程的核心功能都是由不同的组件来完成的.这些组件类似于一个一个单独的模块,不同的模块完成不同的功能,各模块之间可以相互调用,共同构成了CI的核心骨架. 从本篇开始,将进一步去分析各组件的实现细节,深入CI核心的黑盒内部(研究之后,其实就应该是白盒了,仅仅对于应用来说,它应该算是黑盒),从而更好的去认识.把握这个框架. 按照惯例,在开始之前,我们贴上CI中不完全的核心组件图: 由于BenchMa

CI框架源码阅读笔记2 一切的入口 index.php

上一节(CI框架源码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程,这里这次贴出流程图,以备参考: 作为CI框架的入口文件,源码阅读,自然由此开始.在源码阅读的过程中,我们并不会逐行进行解释,而只解释核心的功能和实现. 1.       设置应用程序环境 define('ENVIRONMENT', 'development'); 这里的development可以是任何你喜欢的环境名称(比如dev,再如test),相对应的,你要在下面的switch case代码块中

Apache Storm源码阅读笔记

欢迎转载,转载请注明出处. 楔子 自从建了Spark交流的QQ群之后,热情加入的同学不少,大家不仅对Spark很热衷对于Storm也是充满好奇.大家都提到一个问题就是有关storm内部实现机理的资料比较少,理解起来非常费劲. 尽管自己也陆续对storm的源码走读发表了一些博文,当时写的时候比较匆忙,有时候衔接的不是太好,此番做了一些整理,主要是针对TridentTopology部分,修改过的内容采用pdf格式发布,方便打印. 文章中有些内容的理解得益于徐明明和fxjwind两位的指点,非常感谢.