前言

本文基于Dubbo2.6.x版本,中文注释版源码已上传github:xiaoguyu/dubbo

上一篇文章,讲了Dubbo的服务导出:

Dubbo源码(三) - 服务导出(生产者)

本文,咱们来聊聊Dubbo的服务引用。

本文案例来自Dubbo官方Demo,路径为:

dubbo/dubbo-demo/dubbo-demo-consumer/

服务引用原理

Dubbo服务引用对象的生成,是在ReferenceBean#getObject()方法中

其生成时机有两个:

  1. 饿汉式

    ReferenceBean对象继承了InitializingBean接口

    1. public void afterPropertiesSet() throws Exception {
    2. ......
    3. Boolean b = isInit();
    4. if (b == null && getConsumer() != null) {
    5. b = getConsumer().isInit();
    6. }
    7. if (b != null && b.booleanValue()) {
    8. getObject();
    9. }
    10. }

    从代码可以看出,需要开启init属性

  2. 懒汉式

    因为ReferenceBean继承了FactoryBean接口,当服务被注入到其他类中时,Spring会调用getObject方法

而服务的调用方式分三种:

  1. 本地引用
  2. 直连方式引用
  3. 注册中心引用

不管是哪种引用方式,最后都会得到一个 Invoker 实例。

我们再次看看Invoker的官方解释:

Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。

在Dubbo中,Invoker是多重套娃的(可以理解为装饰器模式或者包装增强类),通过一层层的包装,使普通的Invoker具备了负载均衡、集群的功能。

最后,为服务接口(本文为DemoService)生成代理对象,Invoker#invoke(Invocation invocation)实现服务的调用。

本文不讨论直连方式引用,也不讨论负载均衡、集群等功能(后续再开坑说)。

创建代理对象

梦的开始,ReferenceBean#getObject()

  1. public Object getObject() throws Exception {
  2. return get();
  3. }
  4. public synchronized T get() {
  5. if (destroyed) {
  6. throw new IllegalStateException("Already destroyed!");
  7. }
  8. if (ref == null) {
  9. // init 方法主要用于处理配置,以及调用 createProxy 生成代理类
  10. init();
  11. }
  12. return ref;
  13. }

很明显,在 init 方法中生成了ref

  1. private void init() {
  2. // 省略大堆的检查以及参数处理
  3. ......
  4. //attributes are stored by system context.
  5. // 存储 attributes 到系统上下文中
  6. StaticContext.getSystemContext().putAll(attributes);
  7. // 创建代理类
  8. ref = createProxy(map);
  9. // 根据服务名,ReferenceConfig,代理类构建 ConsumerModel,
  10. // 并将 ConsumerModel 存入到 ApplicationModel 中
  11. ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods());
  12. ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);
  13. }

直接看 createProxy(map)

  1. private T createProxy(Map<String, String> map) {
  2. URL tmpUrl = new URL("temp", "localhost", 0, map);
  3. final boolean isJvmRefer;
  4. // isJvmRefer 的赋值处理
  5. ......
  6. // 本地引用
  7. if (isJvmRefer) {
  8. // 生成invoker
  9. ......
  10. // 远程引用
  11. } else {
  12. // 生成invoker、合并invoker
  13. ......
  14. }
  15. // invoker 可用性检查
  16. ......
  17. // 生成代理类
  18. return (T) proxyFactory.getProxy(invoker);
  19. }

这个方法主要做了两件事

  1. 创建以及合并Invoker
  2. 生成代理对象

这里先略过invoker的处理,先看看代理对象的生成。

proxyFactory 是自适应拓展类,默认实现是JavassistProxyFactory,getProxy 方法在其父类AbstractProxyFactory

  1. // 这是AbstractProxyFactory类的方法
  2. public <T> T getProxy(Invoker<T> invoker) throws RpcException {
  3. return getProxy(invoker, false);
  4. }
  5. public <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException {
  6. Class<?>[] interfaces = null;
  7. // 获取接口列表
  8. String config = invoker.getUrl().getParameter("interfaces");
  9. if (config != null && config.length() > 0) {
  10. // 切分接口列表
  11. String[] types = Constants.COMMA_SPLIT_PATTERN.split(config);
  12. if (types != null && types.length > 0) {
  13. interfaces = new Class<?>[types.length + 2];
  14. // 设置服务接口类和 EchoService.class 到 interfaces 中
  15. interfaces[0] = invoker.getInterface();
  16. interfaces[1] = EchoService.class;
  17. for (int i = 0; i < types.length; i++) {
  18. // 加载接口类
  19. interfaces[i + 2] = ReflectUtils.forName(types[i]);
  20. }
  21. }
  22. }
  23. if (interfaces == null) {
  24. interfaces = new Class<?>[]{invoker.getInterface(), EchoService.class};
  25. }
  26. // 为 http 和 hessian 协议提供泛化调用支持,参考 pull request #1827
  27. if (!invoker.getInterface().equals(GenericService.class) && generic) {
  28. int len = interfaces.length;
  29. Class<?>[] temp = interfaces;
  30. // 创建新的 interfaces 数组
  31. interfaces = new Class<?>[len + 1];
  32. System.arraycopy(temp, 0, interfaces, 0, len);
  33. // 设置 GenericService.class 到数组中
  34. interfaces[len] = GenericService.class;
  35. }
  36. // 调用重载方法
  37. return getProxy(invoker, interfaces);
  38. }

这里的大段逻辑都是在处理interfaces参数,此时interfaces的值为{ DemoService.class, EchoService.class }

继续看子类JavassistProxyFactory实现的 getProxy(invoker, interfaces) 方法

  1. public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
  2. // return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
  3. // 源码是上面那行,我们将上面的代码改下面的格式
  4. Proxy proxy = Proxy.getProxy(interfaces);
  5. return (T) proxy.newInstance(new InvokerInvocationHandler(invoker));
  6. }

注意:我下面开始讲的 proxy 不是平时理解的代理对象,你可以理解为一个生成代理对象的 builder

此方法做了两件事:

  1. 生成proxy对象
  2. 调用 proxy 的newInstance方法生成实际的代理对象

这里,我就不讲 Proxy.getProxy 的源码了,感兴趣的朋友自行了解。简单讲下里面做了什么:

  1. 构建服务接口(本文为DemoService)的代理类的字节码对象,其生成的字节码对象如下:

    这里简化了下代码,实际上还实现了EchoService接口

    1. package org.apache.dubbo.common.bytecode;
    2. public class proxy0 implements org.apache.dubbo.demo.DemoService {
    3. public static java.lang.reflect.Method[] methods;
    4. private java.lang.reflect.InvocationHandler handler;
    5. public proxy0() {
    6. }
    7. public proxy0(java.lang.reflect.InvocationHandler arg0) {
    8. handler = $1;
    9. }
    10. public java.lang.String sayHello(java.lang.String arg0) {
    11. Object[] args = new Object[1];
    12. args[0] = ($w) $1;
    13. Object ret = handler.invoke(this, methods[0], args);
    14. return (java.lang.String) ret;
    15. }
    16. }
  2. 构建生成服务接口代理对象的builder

    1. package com.alibaba.dubbo.common.bytecode;
    2. public class Proxy0 extends com.alibaba.dubbo.common.bytecode.Proxy {
    3. public Proxy0() {
    4. }
    5. public Object newInstance(java.lang.reflect.InvocationHandler h){
    6. return new com.alibaba.dubbo.common.bytecode.proxy0($1);
    7. }
    8. }

注意一下:一个是proxy0,另一个是Proxy0,包名不同,类名的p子也有大小写的区别,别搞混了

再对照之前的 getProxy 方法

Proxy.getProxy(interfaces) 生成的是 Proxy0(大写的P)

proxy.newInstance(new InvokerInvocationHandler(invoker)) 生成的是 proxy0(小写的p)

至此,Dubbo服务引用对象已生成,可以看到,生成的引用对象结构也很简单,主要是依赖 invoker 对象完成接口调用的,下面就去看看 invoker 的生成。

创建Invoker

让我们的视线重新回到createProxy方法中

  1. private T createProxy(Map<String, String> map) {
  2. URL tmpUrl = new URL("temp", "localhost", 0, map);
  3. final boolean isJvmRefer;
  4. // isJvmRefer 的赋值处理
  5. ......
  6. // 本地引用
  7. if (isJvmRefer) {
  8. // 生成本地引用 URL,协议为 injvm
  9. URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
  10. // 调用 refer 方法构建 InjvmInvoker 实例
  11. invoker = refprotocol.refer(interfaceClass, url);
  12. if (logger.isInfoEnabled()) {
  13. logger.info("Using injvm service " + interfaceClass.getName());
  14. }
  15. // 远程引用
  16. } else {
  17. // url 不为空,表明用户可能想进行点对点调用
  18. if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
  19. ......
  20. } else { // assemble URL from register center's configuration
  21. // 加载注册中心 url
  22. ......
  23. }
  24. // 单个注册中心或服务提供者(服务直连,下同)
  25. if (urls.size() == 1) {
  26. // 调用 RegistryProtocol 的 refer 构建 Invoker 实例
  27. invoker = refprotocol.refer(interfaceClass, urls.get(0));
  28. // 多个注册中心或多个服务提供者,或者两者混合
  29. } else {
  30. List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
  31. URL registryURL = null;
  32. // 获取所有的 Invoker
  33. for (URL url : urls) {
  34. // 通过 refprotocol 调用 refer 构建 Invoker,refprotocol 会在运行时
  35. // 根据 url 协议头加载指定的 Protocol 实例,并调用实例的 refer 方法
  36. invokers.add(refprotocol.refer(interfaceClass, url));
  37. if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
  38. registryURL = url; // use last registry url
  39. }
  40. }
  41. if (registryURL != null) { // registry url is available
  42. // 如果注册中心链接不为空,则将使用 AvailableCluster
  43. URL u = registryURL.addParameterIfAbsent(Constants.CLUSTER_KEY, AvailableCluster.NAME);
  44. // 创建 StaticDirectory 实例,并由 Cluster 对多个 Invoker 进行合并
  45. invoker = cluster.join(new StaticDirectory(u, invokers));
  46. } else { // not a registry url
  47. invoker = cluster.join(new StaticDirectory(invokers));
  48. }
  49. }
  50. }
  51. // invoker 可用性检查
  52. ......
  53. // 生成代理类
  54. return (T) proxyFactory.getProxy(invoker);
  55. }

本地引用

  1. if (isJvmRefer) {
  2. // 生成本地引用 URL,协议为 injvm
  3. URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
  4. // 调用 refer 方法构建 InjvmInvoker 实例
  5. invoker = refprotocol.refer(interfaceClass, url);
  6. if (logger.isInfoEnabled()) {
  7. logger.info("Using injvm service " + interfaceClass.getName());
  8. }
  9. }

refprotocol 是自适应拓展,根据URL中的协议,确定实现类是InjvmProtocol

  1. public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
  2. return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap);
  3. }

其 refer 方法也很简单,就生成了 InjvmInvoker 对象并返回。其实这里搭配服务调用过程才容易理解(也就是InjvmInvoker#doInvoke(Invocation invocation方法),但本文是将服务引用过程,所以不展开。

远程引用

远程引用区分单注册中心或单服务提供者和多注册中心或多服务提供者,此处我们以单注册中心或单服务提供者举例,主要逻辑是下面这段

  1. // 单个注册中心或服务提供者(服务直连,下同)
  2. if (urls.size() == 1) {
  3. // 调用 RegistryProtocol 的 refer 构建 Invoker 实例
  4. invoker = refprotocol.refer(interfaceClass, urls.get(0));
  5. }

refprotocol 是自适应拓展类,根据 url 中的协议参数,其实现类为RegistryProtocol

  1. public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
  2. url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
  3. // 获取注册中心实例
  4. Registry registry = registryFactory.getRegistry(url);
  5. if (RegistryService.class.equals(type)) {
  6. return proxyFactory.getInvoker((T) registry, type, url);
  7. }
  8. // group="a,b" or group="*"
  9. // 将 url 查询字符串转为 Map
  10. Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
  11. // 获取 group 配置
  12. String group = qs.get(Constants.GROUP_KEY);
  13. if (group != null && group.length() > 0) {
  14. if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
  15. || "*".equals(group)) {
  16. // 通过 SPI 加载 MergeableCluster 实例,并调用 doRefer 继续执行服务引用逻辑
  17. return doRefer(getMergeableCluster(), registry, type, url);
  18. }
  19. }
  20. // 调用 doRefer 继续执行服务引用逻辑
  21. return doRefer(cluster, registry, type, url);
  22. }

获取注册中心实例的过程,就是创建 zookeeper 连接,我在上一篇Dubbo服务导出博文中讲过了,请自行查找。

我们继续关注主要方法doRefer(cluster, registry, type, url)

  1. private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
  2. // 创建 RegistryDirectory 实例
  3. RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
  4. // 设置注册中心和协议
  5. directory.setRegistry(registry);
  6. directory.setProtocol(protocol);
  7. // all attributes of REFER_KEY
  8. Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
  9. // 生成服务消费者链接
  10. URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
  11. // 注册服务消费者,在 consumers 目录下新节点
  12. if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
  13. && url.getParameter(Constants.REGISTER_KEY, true)) {
  14. URL registeredConsumerUrl = getRegisteredConsumerUrl(subscribeUrl, url);
  15. registry.register(registeredConsumerUrl);
  16. directory.setRegisteredConsumerUrl(registeredConsumerUrl);
  17. }
  18. // 订阅 providers、configurators、routers 等节点数据
  19. directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
  20. Constants.PROVIDERS_CATEGORY
  21. + "," + Constants.CONFIGURATORS_CATEGORY
  22. + "," + Constants.ROUTERS_CATEGORY));
  23. // 一个注册中心可能有多个服务提供者,因此这里需要将多个服务提供者合并为一个
  24. Invoker invoker = cluster.join(directory);
  25. ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
  26. return invoker;
  27. }

此方法主要做了4个操作:

  1. 创建一个 RegistryDirectory 实例,这是一个服务目录对象。

    服务目录中存储了一些和服务提供者有关的信息,通过服务目录,服务消费者可获取到服务提供者的信息,比如 ip、端口、服务协议等。通过这些信息,服务消费者就可通过 Netty 等客户端进行远程调用。

  2. 向注册中心进行注册

  3. 订阅 providers、configurators、routers 等节点下的数据

  4. 生成invoker

    cluster.join(directory) 默认实现类是FailoverCluster,这个是集群处理,后续文章再讨论。

讨论了这么久,还没看到如何连接暴露出来的远程服务。

其实,连接远程服务的操作,就是在订阅 providers 节点数据时完成的

连接远程服务

这里,就不细说订阅 providers 之后的各种处理,直接快进到远程服务的连接。下面放上订阅节点数据到启动远程连接的调用路径

别问为什么是DubboProtocol,因为服务导出时,也就会zookeeper的providers节点中注册的url,就是Dubbo协议

下面来看看DubboProtocol的 refer 方法

  1. public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
  2. // 序列化优化处理
  3. optimizeSerialization(url);
  4. // create rpc invoker.
  5. DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
  6. invokers.add(invoker);
  7. return invoker;
  8. }

此方法创建了 DubboInvoker 并返回,但是 DubboInvoker 的构造方法没啥好说的,就是一些类变量的赋值。我们主要关注 getClients ,其返回的是客户端实例

  1. private ExchangeClient[] getClients(URL url) {
  2. // whether to share connection
  3. // 是否共享连接
  4. boolean service_share_connect = false;
  5. // 获取连接数,默认为0,表示未配置
  6. int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
  7. // 如果未配置 connections,则共享连接
  8. if (connections == 0) {
  9. service_share_connect = true;
  10. connections = 1;
  11. }
  12. ExchangeClient[] clients = new ExchangeClient[connections];
  13. for (int i = 0; i < clients.length; i++) {
  14. if (service_share_connect) {
  15. // 获取共享客户端
  16. clients[i] = getSharedClient(url);
  17. } else {
  18. // 初始化新的客户端
  19. clients[i] = initClient(url);
  20. }
  21. }
  22. return clients;
  23. }

connections 的默认值为0,也就是 service_share_connect 为 true ,进入 getSharedClient(url) 方法

  1. private ExchangeClient getSharedClient(URL url) {
  2. String key = url.getAddress();
  3. // 获取带有“引用计数”功能的 ExchangeClient
  4. ReferenceCountExchangeClient client = referenceClientMap.get(key);
  5. if (client != null) {
  6. if (!client.isClosed()) {
  7. // 增加引用计数
  8. client.incrementAndGetCount();
  9. return client;
  10. } else {
  11. referenceClientMap.remove(key);
  12. }
  13. }
  14. locks.putIfAbsent(key, new Object());
  15. synchronized (locks.get(key)) {
  16. if (referenceClientMap.containsKey(key)) {
  17. return referenceClientMap.get(key);
  18. }
  19. // 创建 ExchangeClient 客户端
  20. ExchangeClient exchangeClient = initClient(url);
  21. // 将 ExchangeClient 实例传给 ReferenceCountExchangeClient,这里使用了装饰模式
  22. client = new ReferenceCountExchangeClient(exchangeClient, ghostClientMap);
  23. referenceClientMap.put(key, client);
  24. ghostClientMap.remove(key);
  25. locks.remove(key);
  26. return client;
  27. }
  28. }

此处就是一些引用计数和缓存操作,主要关注 ExchangeClient 的创建

  1. private ExchangeClient initClient(URL url) {
  2. // 获取客户端类型,默认为 netty
  3. String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
  4. ......
  5. ExchangeClient client;
  6. try {
  7. // 获取 lazy 配置,并根据配置值决定创建的客户端类型
  8. if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
  9. // 创建懒加载 ExchangeClient 实例
  10. client = new LazyConnectExchangeClient(url, requestHandler);
  11. } else {
  12. // 创建普通 ExchangeClient 实例
  13. client = Exchangers.connect(url, requestHandler);
  14. }
  15. } catch (RemotingException e) {
  16. throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
  17. }
  18. return client;
  19. }

我们这里不讨论懒加载的情况。有见到了熟悉的 Exchangers, 在服务导出的时候,调用的是Exchangers.bind 方法,服务引用这里用的是 Exchangers.connect

  1. public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
  2. if (url == null) {
  3. throw new IllegalArgumentException("url == null");
  4. }
  5. if (handler == null) {
  6. throw new IllegalArgumentException("handler == null");
  7. }
  8. url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
  9. // 获取 Exchanger 实例,默认为 HeaderExchangeClient
  10. return getExchanger(url).connect(url, handler);
  11. }

这里 getExchanger(url) 返回的是 HeaderExchangeClient,直接进去看 connect 方法

  1. public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
  2. // 这里包含了多个调用,分别如下:
  3. // 1. 创建 HeaderExchangeHandler 对象
  4. // 2. 创建 DecodeHandler 对象
  5. // 3. 通过 Transporters 构建 Client 实例
  6. // 4. 创建 HeaderExchangeClient 对象
  7. return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
  8. }

HeaderExchangeClient内部持有 client ,并封装了心跳的功能。我们重点在 Transporters.connect ,也就是Dubbo的网络传输层是如何连接的

  1. public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException {
  2. if (url == null) {
  3. throw new IllegalArgumentException("url == null");
  4. }
  5. ChannelHandler handler;
  6. if (handlers == null || handlers.length == 0) {
  7. handler = new ChannelHandlerAdapter();
  8. } else if (handlers.length == 1) {
  9. handler = handlers[0];
  10. } else {
  11. // 如果 handler 数量大于1,则创建一个 ChannelHandler 分发器
  12. handler = new ChannelHandlerDispatcher(handlers);
  13. }
  14. // 获取 Transporter 自适应拓展类,并调用 connect 方法生成 Client 实例
  15. return getTransporter().connect(url, handler);
  16. }

getTransporter() 获取的是Transporter的自适应拓展类,默认是NettyTransporter

  1. public Client connect(URL url, ChannelHandler listener) throws RemotingException {
  2. return new NettyClient(url, listener);
  3. }

NettyTransporter的 connect 方法就创建了一个 NettyClient 对象,所以启动连接的相关逻辑在其构造函数中

  1. public NettyClient(final URL url, final ChannelHandler handler) throws RemotingException {
  2. super(url, wrapChannelHandler(url, handler));
  3. }
  4. // NettyClient的父类AbstractClient
  5. public AbstractClient(URL url, ChannelHandler handler) throws RemotingException {
  6. ......
  7. try {
  8. doOpen();
  9. } catch (Throwable t) {
  10. ......
  11. }
  12. try {
  13. connect();
  14. if (logger.isInfoEnabled()) {
  15. logger.info("Start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress());
  16. }
  17. } catch (RemotingException t) {
  18. ......
  19. }
  20. ......
  21. }

这里又是使用模板方法,doOpen() 和 connect() 的具体实现在子类NettyClient中,其作用就是创建对远程服务的连接。这部分属于Netty的API调用,就不做具体描述了。

总结

本文讲述了Dubbo服务导出的过程,也就是创建服务接口代理对象的过程。其中服务调用、集群、负载均衡等部分并未描述,可以期待后续文章。

Dubbo源码(四) - 服务引用(消费者)的更多相关文章

  1. Dubbo 源码分析 - 服务引用

    1. 简介 在上一篇文章中,我详细的分析了服务导出的原理.本篇文章我们趁热打铁,继续分析服务引用的原理.在 Dubbo 中,我们可以通过两种方式引用远程服务.第一种是使用服务直联的方式引用服务,第二种 ...

  2. Dubbo源码(五) - 服务目录

    前言 本文基于Dubbo2.6.x版本,中文注释版源码已上传github:xiaoguyu/dubbo 今天,来聊聊Dubbo的服务目录(Directory).下面是官方文档对服务目录的定义: 服务目 ...

  3. Dubbo 源码分析 - 服务调用过程

    注: 本系列文章已捐赠给 Dubbo 社区,你也可以在 Dubbo 官方文档中阅读本系列文章. 1. 简介 在前面的文章中,我们分析了 Dubbo SPI.服务导出与引入.以及集群容错方面的代码.经过 ...

  4. Dubbo源码学习--服务是如何引用的

    ReferenceBean 跟服务引用一样,Dubbo的reference配置会被转成ReferenceBean类,ReferenceBean实现了InitializingBean接口,直接看afte ...

  5. Dubbo 源码分析 - 服务导出

    1.服务导出过程 本篇文章,我们来研究一下 Dubbo 导出服务的过程.Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可 ...

  6. Dubbo源码(九) - 服务调用过程

    1. 前言 本文基于Dubbo2.6.x版本,中文注释版源码已上传github:xiaoguyu/dubbo 源码分析均基于官方Demo,路径:dubbo/dubbo-demo 如果没有看过之前Dub ...

  7. dubbo源码之四——服务发布二

    dubbo版本:2.5.4 2. 服务提供者暴露一个服务的详细过程 上图是服务提供者暴露服务的主过程: 首先ServiceConfig类拿到对外提供服务的实际类ref(如:HelloWorldImpl ...

  8. Dubbo源码学习--服务是如何发布的

    相关文章: Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 ServiceBean ServiceBean 实现ApplicationListener接口监听Conte ...

  9. SOFA 源码分析 —— 服务引用过程

    前言 在前面的 SOFA 源码分析 -- 服务发布过程 文章中,我们分析了 SOFA 的服务发布过程,一个完整的 RPC 除了发布服务,当然还需要引用服务. So,今天就一起来看看 SOFA 是如何引 ...

随机推荐

  1. 渗透:dSploit

    dSploit--开源的专业的Android平台安全管理工具包 只能在横屏模式下工作,即使你旋转你的设备也将继续保持横屏,如果你有一个应用程序,如旋转控制器,迫使每一个应用程序旋转,将导致dSploi ...

  2. linux篇-linux数据库mysql的安装

    1数据库文件放到opt下面 2赋予权限775 3运行脚本 4运行成功 5数据库操作 密码修改并刷新 权限修改,允许外部设备访问 6工具连接 7附录 1.显示当前数据库服务器中的数据库列表: mysql ...

  3. yolov1学习笔记

    yolov1学习笔记 yolov1将目标检测归为一个回归问题,具有real-time的特点.局限性是:对于群体性的小目标检测效果很差. 论文概括 本文重新构造目标检测作为一个回归问题. 直接输入图像到 ...

  4. 第06组 Alpha冲刺 (2/6)

    目录 1.1 基本情况 1.2 冲刺概况汇报 1.郝雷明 2. 方梓涵 3. 黄少丹 4. 董翔云 5.曾丽莉 6. 詹鑫冰 7.鲍凌函 8.杜筱 9.曹兰英 10. 吴沅静 1.3 冲刺成果展示 1 ...

  5. 面试官:Dubbo是什么,他有什么特性?

    哈喽!大家好,我是小奇,一位热爱分享的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新 一.前言 书接上回,今天还是过周末,虽然 ...

  6. model.apply(fn)或net.apply(fn)

    详情可参考:https://pytorch.org/docs/1.11/generated/torch.nn.Module.html?highlight=torch%20nn%20module%20a ...

  7. java8 Stream新特性

    import lombok.Getter; import lombok.Setter; @Setter @Getter public class Person { private String nam ...

  8. 从Vue源码中我学到了几点精妙方法

    话不多说,赶快试试这几个精妙方法吧!在工作中肯定会用得到. 立即执行函数 页面加载完成后只执行一次的设置函数. (function (a, b) { console.log(a, b); // 1,2 ...

  9. lvm逻辑卷创建及使用

    创建逻辑卷 pvcreate /dev/md0 pvs 查看创建的pv组 pvdisplay /dev/md0 查看磁盘详细信息 添加vg组: 创建vg组: vgcreate vg1 /dev/md0 ...

  10. 安装gitlab客户端

    1. 下载客户端软件包 https://pan.baidu.com/disk/home#/category?type=6&vmode=list 安装顺序: Git-2.13.3-64-bit. ...