Dubbo专题

Dubbo服务暴露分析

2020-02-04  本文已影响0人  九点半的马拉

Dubbo的服务暴露是一个重要的特性,了解其机制很重要。之前有很多人写了有关的源代码分析,在本文中不再重新分析。官方文档中的一篇写的就很好,本文主要是有关内容进行补充与总结。
传送门:服务导出

为什么要服务暴露

服务暴露分为远程暴露和本地暴露。在远程服务暴露中会将服务信息上传到注册中心。这时客户端要调用某个服务时会从注册中心找到该服务的远程地址等信息。然后客户端根据这个地址进行远程调用。服务端接收到远程调用请求后会最终调用getInvoker()方法进行查找对用的invoker。在getInvoker()方法中会从一个HashMap中进行查找,如果在这个Map中查找不到就会抛出异常。在远程服务暴露中,会按照规则将实例Invoker存储在HashMap中,其中Key名包含端口、接口名、接口版本和接口分组。所以进行服务暴露很重要。
本地服务暴露是暴露在JVM中,不需要远程通信。Dubbo会默认把远程服务用injvm协议再暴露一份。
为什么会有本地服务暴露
在Dubbo中,一个服务可以即是provider,又是Consumer,因此就存在它自己调用自己服务的时候,如果再通过网络去访问,那么就是舍近求远,因此有了本地暴露服务这个设计。消费者可以直接消费同一个JVM内部的服务,避免了跨网络进行远程通信。

服务暴露起点

我们会通过XML或注解的方式来指定要暴露的服务。l例子如下:

<bean id=“xxxService” class=“com.xxx.XxxServiceImpl” /> 
<!-- 增加暴露远程服务配置 -->
<dubbo:service interface=“com.xxx.XxxService” ref=“xxxService” /> 

这时会创建出一个ServiceBean对象。

public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
        ApplicationContextAware, BeanNameAware,
        ApplicationEventPublisherAware 
public class ServiceConfig<T> extends ServiceConfigBase<T>
public abstract class ServiceConfigBase<T> extends AbstractServiceConfig {
    private static final long serialVersionUID = 3033787999037024738L;
    protected String interfaceName;
    //要暴露服务类的接口类
    protected Class<?> interfaceClass;
    //实现类引用
    protected T ref;
    //服务名 
    protected String path;
    protected ProviderConfig provider;
    protected String providerIds;
    protected volatile String generic;
    protected ServiceMetadata serviceMetadata;

ServiceBean和Spring有关,它继承了InitializingBean和ApplicationEvent。在Bean初始化完成后会调用InitializingBean.afterPropertiesSet方法来执行服务暴露的准备工作。在Spring的context完成初始化后,会触发ApplicationEventListener事件进行服务暴露,会执行onApplicationEvent方法。这时服务服务暴露就开始了。

public void onApplicationEvent(ContextRefreshedEvent event) {
    // 是否有延迟导出 && 是否已导出 && 是不是已被取消导出
    if (isDelay() && !isExported() && !isUnexported()) {
        // 导出服务
        export();
    }
}

整体流程

Dubbo真正的服务暴露入口是ServiceConfig#doExport()方法。首先ServiceConfig类拿到对外提供服务的实际类ref,然后通过ProxyFactory类的getInvoker方法使用ref生成一个AbstractProxyInvoker实例,到这一步就完成具体服务到Invoker的转化。然后就是把Invoker通过具体的协议(比如Bubbo)转化成Exporter

整体流程
参考了简书肥朝的一篇文章: dubbo源码解析-服务暴露原理

这时,第一大步服务转化成Invoker已经完成,然后执行第二大步Invoker转化成Exporter
先介绍本地服务的暴露机制

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}
InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
    super(invoker);
    this.key = key;
    this.exporterMap = exporterMap;
    exporterMap.put(key, this);
}

然后介绍远程服务暴露
如果有注册中心,服务暴露后需要向注册中心注册服务信息
如果没有注册中心,直接暴露服务。

有注册中心的暴露

  1. 调用doLocalExport()方法暴露服务。
  2. 向注册中心注册服务
  3. 向注册中心进行订阅ovrride数据
  4. 创建并返回DestroyableExporter
    调用doLocalExport()方法暴露服务
// demoGroup/com.alibaba.dubbo.demo.DemoService:1.0.1:20880
String key = serviceKey(url);
// 创建 DubboExporter
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
// 将 <key, exporter> 键值对放入缓存中
exporterMap.put(key, exporter);
getExchanger(url).bind(url, handler);
   
public static Exchanger getExchanger(URL url) {
        String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
        return getExchanger(type);
    }
public static Exchanger getExchanger(String type) {
        return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
    }

Exchange层是为了封装请求/响应模式,例如:把同步请求转化为异步请求。默认的扩展点实现类是HeaderExchanger

public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
    if (url == null) {
        throw new IllegalArgumentException("url == null");
    }
    if (handlers == null || handlers.length == 0) {
        throw new IllegalArgumentException("handlers == null");
    }
    ChannelHandler handler;
    if (handlers.length == 1) {
        handler = handlers[0];
    } else {
        // 如果 handlers 元素数量大于1,则创建 ChannelHandler 分发器
        handler = new ChannelHandlerDispatcher(handlers);
    }
    // 获取自适应 Transporter 实例,并调用实例方法
    return getTransporter().bind(url, handler);
}
上一篇下一篇

猜你喜欢

热点阅读