Dubbo服务引用(一)本地引用

哈哈哈和服务暴露一样,也有两种:

  1. 本地引用,jvm本地调用:
// 推荐
<dubbo:service scope="local" />
// 不推荐使用,准备废弃
<dubbo:service injvm="true" />

  1. 远程暴露,网络远程通信:
<dubbo:service scope="remote" />

这篇看看本地引用。

在ReferenceConfig中的init() 方法里,会在配置初始化完成后,调用顺序图的起点 createProxy(map) 方法,开始引用服务:

/**
 * 自适应 Protocol 实现对象
 */
private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
/**
 * 自适应 ProxyFactory 实现对象
 */
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
    
/**
 * 直连服务提供者地址
 */
// url for peer-to-peer invocation
private String url;

  private T createProxy(Map<String, String> map) {
      URL tmpUrl = new URL("temp", "localhost", 0, map);
      // 是否本地引用
      final boolean isJvmRefer;
      // injvm 属性为空,不通过该属性判断
      if (isInjvm() == null) {
          // 直连服务提供者,参见文档《直连提供者》http://dubbo.apache.org/zh-cn/docs/user/demos/explicit-target.html
          if (url != null && url.length() > 0) { // if a url is specified, don't do local reference
              isJvmRefer = false;
          // 通过 `tmpUrl` 判断,是否需要本地引用
          } else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
             // by default, reference local service if there is
              isJvmRefer = true;
          // 默认不是
          } else {
              isJvmRefer = false;
          }
      // 通过 injvm 属性。
      } else {
          isJvmRefer = isInjvm();
      }
  
      // 本地引用
      if (isJvmRefer) {
          // 创建服务引用 URL 对象
          URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
          // 引用服务,返回 Invoker 对象
          invoker = refprotocol.refer(interfaceClass, url);
          if (logger.isInfoEnabled()) {
              logger.info("Using injvm service " + interfaceClass.getName());
          }
      // 正常流程,一般为远程引用
      } else {
          // ... 省略本文暂时不分享的服务远程引用 
          }
      }
  
      // 启动时检查
      Boolean c = check;
      if (c == null && consumer != null) {
          c = consumer.isCheck();
      }
      if (c == null) {
          c = true; // default true
      }
      if (c && !invoker.isAvailable()) {
          throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
      }
      if (logger.isInfoEnabled()) {
          logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
      }
  
      // 创建 Service 代理对象
      // create service proxy
      return (T) proxyFactory.getProxy(invoker);
  }

上面调用InjvmProtocol的isInjvmRefer(url) 方法,通过 tmpUrl 判断,是否需要本地引用。使用 tmpUrl ,相当于使用服务引用配置对象的配置项:

 /**
  * 是否本地引用
  *
  * @param url URL
  * @return 是否
  */
 public boolean isInjvmRefer(URL url) {
     final boolean isJvmRefer;
     String scope = url.getParameter(Constants.SCOPE_KEY);
     // Since injvm protocol is configured explicitly, we don't need to set any extra flag, use normal refer process.
     // 当 `protocol = injvm` 时,本身已经是 jvm 协议了,走正常流程就是了。
    if (Constants.LOCAL_PROTOCOL.toString().equals(url.getProtocol())) {
         isJvmRefer = false;
     // 当 `scope = local` 或者 `injvm = true` 时,本地引用
     } else if (Constants.SCOPE_LOCAL.equals(scope) || (url.getParameter("injvm", false))) {
         // if it's declared as local reference
         // 'scope=local' is equivalent to 'injvm=true', injvm will be deprecated in the future release
         isJvmRefer = true;
     // 当 `scope = remote` 时,远程引用
     } else if (Constants.SCOPE_REMOTE.equals(scope)) {
         // it's declared as remote reference
         isJvmRefer = false;
     // 当 `generic = true` 时,即使用泛化调用,远程引用。
     } else if (url.getParameter(Constants.GENERIC_KEY, false)) {
         // generic invocation is not local reference
         isJvmRefer = false;
     // 当本地已经有该 Exporter 时,直接本地引用
     } else if (getExporter(exporterMap, url) != null) {
         // by default, go through local reference if there's the service exposed locally
         isJvmRefer = true;
     // 默认,远程引用
     } else {
         isJvmRefer = false;
     }
     return isJvmRefer;
 }

refer(type, url) 方法:

 public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
     // 注册中心
     if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
         return protocol.refer(type, url);
     }
     // 引用服务,返回 Invoker 对象
     // 给改 Invoker 对象,包装成带有 Filter 过滤链的 Invoker 对象
     return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
 }

ProtocolListenerWrapper:refer(type, url) 方法:

 public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
     // 注册中心协议
     if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
         return protocol.refer(type, url);
     }
     // 引用服务
     Invoker<T> invoker = protocol.refer(type, url);
     // 获得 InvokerListener 数组
     List<InvokerListener> listeners = Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(InvokerListener.class).getActivateExtension(url, Constants.INVOKER_LISTENER_KEY));
     // 创建 ListenerInvokerWrapper 对象
     return new ListenerInvokerWrapper<T>(invoker, listeners);
 }

InjvmProtocol : refer(type, url) 方法 : 创建 InjvmInvoker 对象。注意,传入的 exporterMap 参数,包含所有的 InjvmExporter 对象。

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

AbstractInvoker ,实现 Invoker 接口,抽象 Invoker 类,主要提供了 Invoker 的通用属性和 invoke(Invocation) 方法的通用实现。属性:

/**
 * 接口类型
 */
private final Class<T> type;
/**
 * 服务 URL
 */
private final URL url;
/**
 * 公用的隐式传参。在 {@link #invoke(Invocation)} 方法中使用。
 */
private final Map<String, String> attachment;
/**
 * 是否可用
 */
private volatile boolean available = true;
/**
 * 是否销毁
 */
private AtomicBoolean destroyed = new AtomicBoolean(false);

InjvmInvoker ,实现 AbstractInvoker 抽象类,Injvm Invoker 实现类, 属性:

/**
 * 服务键
 */
private final String key;
/**
 * Exporter 集合
 *
 * key: 服务键
 *
 * 该值实际就是 {@link com.alibaba.dubbo.rpc.protocol.AbstractProtocol#exporterMap}
 */
private final Map<String, Exporter<?>> exporterMap;

InjvmInvoker(Class<T> type, URL url, String key, Map<String, Exporter<?>> exporterMap) {
    super(type, url);
    this.key = key;
    this.exporterMap = exporterMap;
}

isAvailable() 方法,是否可用:
开启启动时检查时,调用该方法,判断该 Invoker 对象,是否有对应的 Exporter 。若不存在,说明依赖服务不存在,检查不通过。

@Override
public boolean isAvailable() {
    // 判断是否有 Exporter 对象
    InjvmExporter<?> exporter = (InjvmExporter<?>) exporterMap.get(key);
    if (exporter == null) {
        return false;
    } else {
        return super.isAvailable();
    }
}

ListenerInvokerWrapper ,实现 Invoker 接口,具有监听器功能的 Invoker 包装器:

public class ListenerInvokerWrapper<T> implements Invoker<T> {

    private static final Logger logger = LoggerFactory.getLogger(ListenerInvokerWrapper.class);

    /**
     * 真实的 Invoker 对象
     */
    private final Invoker<T> invoker;
    /**
     * Invoker 监听器数组
     */
    private final List<InvokerListener> listeners;

    public ListenerInvokerWrapper(Invoker<T> invoker, List<InvokerListener> listeners) {
        if (invoker == null) {
            throw new IllegalArgumentException("invoker == null");
        }
        this.invoker = invoker;
        this.listeners = listeners;
        // 执行监听器
        if (listeners != null && !listeners.isEmpty()) {
            for (InvokerListener listener : listeners) {
                if (listener != null) {
                    try {
                        listener.referred(invoker);
                    } catch (Throwable t) {
                        logger.error(t.getMessage(), t);
                    }
                }
            }
        }
    }

    public Class<T> getInterface() {
        return invoker.getInterface();
    }

    public URL getUrl() {
        return invoker.getUrl();
    }

    public boolean isAvailable() {
        return invoker.isAvailable();
    }

    public Result invoke(Invocation invocation) throws RpcException {
        return invoker.invoke(invocation);
    }

    @Override
    public String toString() {
        return getInterface() + " -> " + (getUrl() == null ? " " : getUrl().toString());
    }

    public void destroy() {
        try {
            invoker.destroy();
        } finally {
            // 执行监听器
            if (listeners != null && !listeners.isEmpty()) {
                for (InvokerListener listener : listeners) {
                    if (listener != null) {
                        try {
                            listener.destroyed(invoker);
                        } catch (Throwable t) {
                            logger.error(t.getMessage(), t);
                        }
                    }
                }
            }
        }
    }

}

InvokerListener ,Invoker 监听器:

@SPI
public interface InvokerListener {

    /**
     * The invoker referred
     *
     * 当服务引用完成
     *
     * @param invoker
     * @throws RpcException
     * @see com.alibaba.dubbo.rpc.Protocol#refer(Class, URL)
     */
    void referred(Invoker<?> invoker) throws RpcException;

    /**
     * The invoker destroyed.
     *
     * 当服务销毁引用完成
     *
     * @param invoker
     * @see com.alibaba.dubbo.rpc.Invoker#destroy()
     */
    void destroyed(Invoker<?> invoker);

}

InvokerListenerAdapter ,实现 InvokerListener 接口,InvokerListener 适配器抽象类:

public abstract class InvokerListenerAdapter implements InvokerListener {

    public void referred(Invoker<?> invoker) throws RpcException { }

    public void destroyed(Invoker<?> invoker) { }

}

DeprecatedInvokerListener ,实现 InvokerListenerAdapter 抽象类 ,引用废弃的服务时,打印错误日志提醒:

@Activate(Constants.DEPRECATED_KEY)
public class DeprecatedInvokerListener extends InvokerListenerAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(DeprecatedInvokerListener.class);

    public void referred(Invoker<?> invoker) throws RpcException {
        if (invoker.getUrl().getParameter(Constants.DEPRECATED_KEY, false)) {
            LOGGER.error("The service " + invoker.getInterface().getName() + " is DEPRECATED! Declare from " + invoker.getUrl());
        }
    }

}

@Activate(Constants.DEPRECATED_KEY) 注解,基于 Dubbo SPI Activate 机制加载。配置方式如下:

    <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" deprecated="true" />
  

本地引用服务的配置方式如下 (本地引用服务时,不是使用服务提供者的 URL ,而是服务消费者的 URL ):

<dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService" protocol="injvm">
    <dubbo:parameter key="deprecated" value="true" />
</dubbo:reference>

你可能感兴趣的:(dubbo)