Dubbo服务暴露(一)之本地暴露

Dubbo 服务暴露有两种方式:

  1. 本地暴露:JVM 本地调用。配置如下
<dubbo:service scope="local" />

  1. 远程暴露:网络远程通信。配置如下
<dubbo:service scope="remote" />

不暴露:

<dubbo:service scope="none" />

不配置scope的情况下,默认两种方式都暴露。大多数情况下,我们不需要配置 scope 。

Dubbo 提供了多种协议( Protocol )实现,这里讲本地暴露,该方式仅使用 Injvm 协议实现。

doExportUrls() 方法,开始暴露服务:

/**
 * 注册中心配置数组
 */
protected List<ProtocolConfig> protocols;

  /**
   * 暴露 Dubbo URL
   */
  @SuppressWarnings({"unchecked", "rawtypes"})
  private void doExportUrls() {
      // 加载注册中心 URL 数组
      List<URL> registryURLs = loadRegistries(true);
      // 循环 `protocols` ,向逐个注册中心分组暴露服务。
  // 使用对应的协议,逐个向注册中心分组暴露服务。
  // 在这个方法中,包含了本地和远程两种暴露方式。
  // 在下文中,我们会看到,本地暴露不会向注册中心注册服务,因为仅仅用于 JVM 内部本地调用,内存中已经有相关信息
      for (ProtocolConfig protocolConfig : protocols) {
         doExportUrlsFor1Protocol(protocolConfig, registryURLs);
      }
  }

loadRegistries(provider) 方法,加载注册中心的 com.alibaba.dubbo.common.URL 数组:

 /**
  * 加载注册中心 URL 数组
  *
  * @param provider 是否是服务提供者
  * @return URL 数组
  */
 protected List<URL> loadRegistries(boolean provider) {
     // 校验 RegistryConfig 配置数组。
     checkRegistry();
     // 创建 注册中心 URL 数组
     List<URL> registryList = new ArrayList<URL>();
     if (registries != null && !registries.isEmpty()) {
         for (RegistryConfig config : registries) {
             // 获得注册中心的地址
             String address = config.getAddress();
             if (address == null || address.length() == 0) {
                 address = Constants.ANYHOST_VALUE;
             }
             String sysaddress = System.getProperty("dubbo.registry.address"); // 从启动参数读取
             if (sysaddress != null && sysaddress.length() > 0) {
                 address = sysaddress;
             }
             // 有效的地址
             if (address.length() > 0
                     && !RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
                 Map<String, String> map = new HashMap<String, String>();
                 // 将各种配置对象,添加到 `map` 集合中。
                 appendParameters(map, application);
                 appendParameters(map, config);
                 // 添加 `path` `dubbo` `timestamp` `pid` 到 `map` 集合中。
                 map.put("path", RegistryService.class.getName());
                 map.put("dubbo", Version.getVersion());
                 map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
                 if (ConfigUtils.getPid() > 0) {
                     map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
                 }
                 // 若不存在 `protocol` 参数,默认 "dubbo" 添加到 `map` 集合中。
                 if (!map.containsKey("protocol")) {
                     if (ExtensionLoader.getExtensionLoader(RegistryFactory.class).hasExtension("remote")) { // "remote"
                         map.put("protocol", "remote");
                     } else {
                         map.put("protocol", "dubbo");
                     }
                 }
                 // 解析地址,创建 Dubbo URL 数组。(数组大小可以为一)
                 List<URL> urls = UrlUtils.parseURLs(address, map);
                 // 循环 `url` ,设置 "registry" 和 "protocol" 属性。
                 for (URL url : urls) {
                    // 设置 `registry=${protocol}` 和 `protocol=registry` 到 URL
                     url = url.addParameter(Constants.REGISTRY_KEY, url.getProtocol());
                     url = url.setProtocol(Constants.REGISTRY_PROTOCOL);
                     // 添加到结果
                     if ((provider && url.getParameter(Constants.REGISTER_KEY, true)) // 服务提供者 && 注册
                             || (!provider && url.getParameter(Constants.SUBSCRIBE_KEY, true))) { // 服务消费者 && 订阅
                         registryList.add(url);
                     }
                 }
             }
         }
     }
     return registryList;
 }

doExportUrlsFor1Protocol(protocolConfig, registryURLs) 方法,基于单个协议,暴露服务:

 /**
  * 基于单个协议,暴露服务
  *
  * @param protocolConfig 协议配置对象
  * @param registryURLs 注册中心链接对象数组
  */
 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
     // ... 【省略】 创建服务 URL 对象
 
     String scope = url.getParameter(Constants.SCOPE_KEY);
     // don't export when none is configured
     if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
 
         // export to local if the config is not remote (export to remote only when config is remote)
         if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
             exportLocal(url);
         }
         // export to remote if the config is not local (export to local only when config is local)
         if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
            // ... 【省略】远程暴露 
         }
     }
     this.urls.add(url);
 }

exportLocal(url) 方法,本地暴露服务:

/**
 * 自适应 Protocol 实现对象
 */
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
/**
 * 自适应 ProxyFactory 实现对象
 */
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

/**
 * {@link #interfaceName} 对应的接口类
 *
 * 非配置
 */
private Class<?> interfaceClass;
/**
 * Service 对象
 */
// reference to interface impl
private T ref;
/**
 * 服务配置暴露的 Exporter 。
 * URL :Exporter 不一定是 1:1 的关系。
 * 例如 {@link #scope} 未设置时,会暴露 Local + Remote 两个,也就是 URL :Exporter = 1:2
 *      {@link #scope} 设置为空时,不会暴露,也就是 URL :Exporter = 1:0
 *      {@link #scope} 设置为 Local 或 Remote 任一时,会暴露 Local 或 Remote 一个,也就是 URL :Exporter = 1:1
 *
 * 非配置。
 */
private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();

  /**
   * 本地暴露服务
   *
   * @param url 注册中心 URL
   */
  @SuppressWarnings({"unchecked", "rawtypes"})
  private void exportLocal(URL url) {
      if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
          // 创建本地 Dubbo URL
          URL local = URL.valueOf(url.toFullString())
                  .setProtocol(Constants.LOCAL_PROTOCOL) // injvm
                  .setHost(LOCALHOST) // 本地
                 .setPort(0); // 端口=0
          // 添加服务的真实类名,例如 DemoServiceImpl ,仅用于 RestProtocol 中。
          ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
          // 使用 ProxyFactory 创建 Invoker 对象
         // 使用 Protocol 暴露 Invoker 对象.此处 Dubbo SPI 自适应的特性的好处就出来了,可以自动根据 URL 参数,获得对应的拓展实现。例如,invoker 传入后,根据 invoker.url 自动获得对应 Protocol 拓展实现为 InjvmProtocol 。
          Exporter<?> exporter = protocol.export(proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
          // 添加到 `exporters`
          exporters.add(exporter);
          logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
      }
  }

ProtocolFilterWrapper ,实现 Protocol 接口,Protocol 的 Wrapper 拓展实现类,用于给 Invoker 增加过滤链。

export(invoker) 方法:

 public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
     // 注册中心
     if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
         return protocol.export(invoker);
     }
     // 建立带有 Filter 过滤链的 Invoker ,再暴露服务。
     return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
 }

buildInvokerChain(invoker, key, group) 方法,创建带 Filter 链的 Invoker 对象:

 /**
  * 创建带 Filter 链的 Invoker 对象
  *
  * @param invoker Invoker 对象
  * @param key 获取 URL 参数名
  * @param group 分组
  * @param <T> 泛型
  * @return Invoker 对象
  */
 private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
     Invoker<T> last = invoker;
     // 获得过滤器数组
     List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
     // 倒序循环 Filter ,创建带 Filter 链的 Invoker 对象。
     // 因为是通过嵌套声明匿名类循环调用的方式,所以要倒序
     if (!filters.isEmpty()) {
         for (int i = filters.size() - 1; i >= 0; i--) {
             final Filter filter = filters.get(i);
             final Invoker<T> next = last;
             last = new Invoker<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 filter.invoke(next, invocation);
                }
 
                 public void destroy() {
                     invoker.destroy();
                 }
 
                 @Override
                 public String toString() {
                     return invoker.toString();
                 }
             };
         }
     }
     return last;
 }

ProtocolListenerWrapper ,实现 Protocol 接口,Protocol 的 Wrapper 拓展实现类,用于给 Exporter 增加 ExporterListener ,监听 Exporter 暴露完成和取消暴露完成。

export(invoker) 方法:

 public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    // 注册中心
     if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
         return protocol.export(invoker);
     }
     // 暴露服务,创建 Exporter 对象
     Exporter<T> exporter = protocol.export(invoker);
     // 获得 ExporterListener 数组
     List<ExporterListener> listeners = Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class).getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY));
     // 创建带 ExporterListener 的 Exporter 对象
     return new ListenerExporterWrapper<T>(exporter, listeners);
 }

AbstractProtocol ,实现 Protocol 接口,协议抽象类:

public abstract class AbstractProtocol implements Protocol {

    /**
     * Exporter 集合
     *
     * key: 服务键 {@link #serviceKey(URL)} 或 {@link URL#getServiceKey()} 。
     *      不同协议会不同
     */
     // exporterMap 属性,Exporter 集合.该集合拥有该协议中,所有暴露中的 Exporter 对象。
    protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();
}

injvm.InjvmProtocol ,实现 AbstractProtocol 抽象类,Injvm 协议实现类:
属性:

/**
 * 协议名
 */
public static final String NAME = Constants.LOCAL_PROTOCOL;
/**
 * 默认端口
 */
public static final int DEFAULT_PORT = 0;
/**
 * 单例。在 Dubbo SPI 中,被初始化,有且仅有一次。
 */
 /*
NAME 静态属性,injvm ,协议名。
DEFAULT_PORT 静态属性,默认端口为 0 。
INSTANCE 静态属性,单例。通过 Dubbo SPI 加载创建,有且仅有一次。
getInjvmProtocol() 静态方法,获得单例。
*/
private static InjvmProtocol INSTANCE;

public InjvmProtocol() {
    INSTANCE = this;
}

public static InjvmProtocol getInjvmProtocol() {
    if (INSTANCE == null) {
        ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(InjvmProtocol.NAME); // load
    }
    return INSTANCE;
}

export(invoker) 方法:

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

AbstractExporter ,实现 Exporter 接口,Exporter 抽象类:

public abstract class AbstractExporter<T> implements Exporter<T> {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * Invoker 对象
     */
    private final Invoker<T> invoker;
    /**
     * 是否取消暴露服务
     */
    private volatile boolean unexported = false;

    public AbstractExporter(Invoker<T> invoker) {
        if (invoker == null)
            throw new IllegalStateException("service invoker == null");
        if (invoker.getInterface() == null)
            throw new IllegalStateException("service type == null");
        if (invoker.getUrl() == null)
            throw new IllegalStateException("service url == null");
        this.invoker = invoker;
    }

    @Override
    public Invoker<T> getInvoker() {
        return invoker;
    }
    
    @Override
    public void unexport() {
        if (unexported) {
            return;
        }
        unexported = true;
        getInvoker().destroy();
    }

    public String toString() {
        return getInvoker().toString();
    }

}

InjvmProtocol ,实现 AbstractExporter 抽象类,Injvm Exporter 实现类:

class InjvmExporter<T> extends AbstractExporter<T> {

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

    InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
        super(invoker);
        this.key = key;
        this.exporterMap = exporterMap;
        // 添加到 Exporter 集合
        exporterMap.put(key, this);
    }

    @Override
    public void unexport() {
        super.unexport();
        // 移除出 Exporter 集合
        exporterMap.remove(key);
    }

}

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

public class ListenerExporterWrapper<T> implements Exporter<T> {

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

    /**
     * 真实的 Exporter 对象
     */
    private final Exporter<T> exporter;
    /**
     * Exporter 监听器数组
     */
    private final List<ExporterListener> listeners;

    public ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners) {
        if (exporter == null) {
            throw new IllegalArgumentException("exporter == null");
        }
        this.exporter = exporter;
        this.listeners = listeners;
        // 执行监听器
        if (listeners != null && !listeners.isEmpty()) {
            RuntimeException exception = null;
            for (ExporterListener listener : listeners) {
                if (listener != null) {
                    try {
                        listener.exported(this);
                    } catch (RuntimeException t) {
                        logger.error(t.getMessage(), t);
                        exception = t;
                    }
                }
            }
            if (exception != null) {
                throw exception;
            }
        }
    }

    public Invoker<T> getInvoker() {
        return exporter.getInvoker();
    }

    public void unexport() {
        try {
        /*unexport() 方法,循环 listeners ,执行 ExporterListener#unexported(listener) 。若执行过程中发生异常 RuntimeException ,打印错误日志,继续执行,最终才抛出。*/
            exporter.unexport();
        } finally {
            // 执行监听器
            if (listeners != null && !listeners.isEmpty()) {
                RuntimeException exception = null;
                for (ExporterListener listener : listeners) {
                    if (listener != null) {
                        try {
                            listener.unexported(this);
                        } catch (RuntimeException t) {
                            logger.error(t.getMessage(), t);
                            exception = t;
                        }
                    }
                }
                if (exception != null) {
                    throw exception;
                }
            }
        }
    }

}

ExporterListener ,Exporter 监听器:

@SPI
public interface ExporterListener {

    /**
     * The exporter exported.
     *
     * 当服务暴露完成
     *
     * @param exporter
     * @throws RpcException
     * @see com.alibaba.dubbo.rpc.Protocol#export(Invoker)
     */
    void exported(Exporter<?> exporter) throws RpcException;

    /**
     * The exporter unexported.
     *
     * 当服务取消暴露完成
     *
     * @param exporter
     * @throws RpcException
     * @see com.alibaba.dubbo.rpc.Exporter#unexport()
     */
    void unexported(Exporter<?> exporter);

}

ExporterListenerAdapter ,实现 ExporterListener 接口,ExporterListener 适配器抽象类:

public abstract class ExporterListenerAdapter implements ExporterListener {

    public void exported(Exporter<?> exporter) throws RpcException { }

    public void unexported(Exporter<?> exporter) throws RpcException { }

}
发布了46 篇原创文章 · 获赞 6 · 访问量 3847

猜你喜欢

转载自blog.csdn.net/weixin_43257196/article/details/105496685
今日推荐