Dubbo应用与实践

具体介绍Dubbo微服务框架的应用与实践

Posted by chengweii on January 3, 2018

Dubbo

Dubbo是阿里巴巴开源的RPC和微服务框架。除此之外,它有助于加强服务管理,并使传统的整体应用程序能够平滑重构为可扩展的分布式体系结构。

架构

逻辑架构 调用流程

模块说明:

  • dubbo-common 公共逻辑模块:包括 Util 类和通用模型。
  • dubbo-remoting 远程通讯模块:相当于 Dubbo 协议的实现,如果 RPC 用 RMI协议则不需要使用此包。
  • dubbo-rpc 远程调用模块:抽象各种协议,以及动态代理,只包含一对一的调用,不关心集群的管理。
  • dubbo-cluster 集群模块:将多个服务提供方伪装为一个提供方,包括:负载均衡, 容错,路由等,集群的地址列表可以是静态配置的,也可以是由注册中心下发。
  • dubbo-registry 注册中心模块:基于注册中心下发地址的集群方式,以及对各种注册中心的抽象。
  • dubbo-monitor 监控模块:统计服务调用次数,调用时间的,调用链跟踪的服务。
  • dubbo-config 配置模块:是 Dubbo 对外的 API,用户通过 Config 使用D ubbo,隐藏 Dubbo 所有细节。
  • dubbo-container 容器模块:是一个 Standlone 的容器,以简单的 Main 加载 Spring 启动,因为服务通常不需要 Tomcat/JBoss 等 Web 容器的特性,没必要用 Web 容器去加载服务。 整体上按照分层结构进行分包,与分层的不同点在于:
    • container 为服务容器,用于部署运行服务,没有在层中画出。
    • protocol 层和 proxy 层都放在 rpc 模块中,这两层是 rpc 的核心,在不需要集群也就是只有一个提供者时,可以只使用这两层完成 rpc 调用。
    • transport 层和 exchange 层都放在 remoting 模块中,为 rpc 调用的通讯基础。
    • serialize 层放在 common 模块中,以便更大程度复用。

      初始化过程

  • 解析服务
    基于 dubbo.jar 内的 META-INF/spring.handlers 配置,Spring 在遇到 dubbo 名称空间时,会回调 DubboNamespaceHandler。
    所有 dubbo 的标签,都统一用 DubboBeanDefinitionParser 进行解析,基于一对一属性映射,将 XML 标签解析为 Bean 对象。
    在 ServiceConfig.export() 或 ReferenceConfig.get() 初始化时,将 Bean 对象转换 URL 格式,所有 Bean 属性转成 URL 的参数。
    然后将 URL 传给 协议扩展点,基于扩展点的 扩展点自适应机制,根据 URL 的协议头,进行不同协议的服务暴露或引用。
  • 暴露服务
    在没有注册中心,直接暴露提供者的情况下 1,ServiceConfig 解析出的 URL 的格式为: dubbo://service-host/com.foo.FooService?version=1.0.0。
    基于扩展点自适应机制,通过 URL 的 dubbo:// 协议头识别,直接调用 DubboProtocol的 export() 方法,打开服务端口。
    在有注册中心,需要注册提供者地址的情况下 2,ServiceConfig 解析出的 URL 的格式为: registry://registry-host/com.alibaba.dubbo.registry.RegistryService?export=URL.encode(“dubbo://service-host/com.foo.FooService?version=1.0.0”)。
    基于扩展点自适应机制,通过 URL 的 registry:// 协议头识别,就会调用 RegistryProtocol 的 export() 方法,将 export 参数中的提供者 URL,先注册到注册中心。
    再重新传给 Protocol 扩展点进行暴露: dubbo://service-host/com.foo.FooService?version=1.0.0,然后基于扩展点自适应机制,通过提供者 URL 的 dubbo:// 协议头识别,就会调用 DubboProtocol 的 export() 方法,打开服务端口。
  • 引用服务
    在没有注册中心,直连提供者的情况下 3,ReferenceConfig 解析出的 URL 的格式为:dubbo://service-host/com.foo.FooService?version=1.0.0。
    基于扩展点自适应机制,通过 URL 的 dubbo:// 协议头识别,直接调用 DubboProtocol 的 refer() 方法,返回提供者引用。
    在有注册中心,通过注册中心发现提供者地址的情况下 4,ReferenceConfig 解析出的 URL 的格式为: registry://registry-host/com.alibaba.dubbo.registry.RegistryService?refer=URL.encode(“consumer://consumer-host/com.foo.FooService?version=1.0.0”)。
    基于扩展点自适应机制,通过 URL 的 registry:// 协议头识别,就会调用 RegistryProtocol 的 refer() 方法,基于 refer 参数中的条件,查询提供者 URL,如: dubbo://service-host/com.foo.FooService?version=1.0.0。
    基于扩展点自适应机制,通过提供者 URL 的 dubbo:// 协议头识别,就会调用 DubboProtocol 的 refer() 方法,得到提供者引用。
    然后 RegistryProtocol 将多个提供者引用,通过 Cluster 扩展点,伪装成单个提供者引用返回。
  • 拦截服务
    基于扩展点自适应机制,所有的 Protocol 扩展点都会自动套上 Wrapper 类。
    基于 ProtocolFilterWrapper 类,将所有 Filter 组装成链,在链的最后一节调用真实的引用。
    基于 ProtocolListenerWrapper 类,将所有 InvokerListener 和 ExporterListener 组装集合,在暴露和引用前后,进行回调。
    包括监控在内,所有附加功能,全部通过 Filter 拦截实现。

    远程调用细节

    暴露服务细节 首先 ServiceConfig 类拿到对外提供服务的实际类 ref(如:HelloWorldImpl),然后通过 ProxyFactory 类的 getInvoker 方法使用 ref 生成一个 AbstractProxyInvoker 实例,到这一步就完成具体服务到 Invoker 的转化。接下来就是 Invoker 转换到 Exporter 的过程。

    Dubbo 处理服务暴露的关键就在 Invoker 转换到 Exporter 的过程,其中 Dubbo 和 RMI 这两种典型协议的实现细节分别是:
    Dubbo 协议的 Invoker 转为 Exporter 发生在 DubboProtocol 类的 export 方法,它主要是打开 socket 侦听服务,并接收客户端发来的各种请求,通讯细节由 Dubbo 自己实现。
    RMI 协议的 Invoker 转为 Exporter 发生在 RmiProtocol类的 export 方法,它通过 Spring 或 Dubbo 或 JDK 来实现 RMI 服务,通讯细节这一块由 JDK 底层来实现,这就省了不少工作量。

消费服务细节 首先 ReferenceConfig 类的 init 方法调用 Protocol 的 refer 方法生成 Invoker 实例(如上图中的红色部分),这是服务消费的关键。接下来把 Invoker 转换为客户端需要的接口(如:HelloWorld)。

注册中心的选择:Multicast和Zookeeper

Multicast

Dubbo 的 Multicast注册中心 是通过使用组播,可以不依赖其它组件,实现去中心化。但因为使用组播,会受网络结构限制,只适合小规模应用或开发阶段使用。
广播时的流程图如下:

  • 提供方启动时广播自己的地址。
  • 消费方启动时广播订阅请求。
  • 提供方收到订阅请求时,单播自己的地址给订阅者,如果设置了unicast=false,则广播给订阅者。
  • 消费方收到提供方地址时,连接该地址进行RPC调用。

    Zookeeper

    Zookeeper 是 Dubbo 官方推荐的注册中心,Zookeeper注册中心支持以下功能:

  • 当提供者出现断电等异常停机时,注册中心能自动删除提供者信息。
  • 当注册中心重启时,能自动恢复注册数据,以及订阅请求。
  • 当会话过期时,能自动恢复注册数据,以及订阅请求。
  • 当设置 check=”false” 时,记录失败注册和订阅请求,后台定时重试。
  • 可通过 username=”admin” password=”1234” 设置zookeeper登录信息。
  • 可通过 group=”dubbo” 设置zookeeper的根节点,不设置将使用无根树。
  • 支持号通配符 group=”” version=”*” ,可订阅服务的所有分组和所有版本的提供者。

    Dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,发布者和订阅者之间还能通信么?
    可以的,启动dubbo时,消费者会从zk拉取注册的生产者的地址接口等数据,缓存在本地。每次调用时,按照本地存储的地址进行调用。但是无法从注册中心去同步最新的服务列表。

Dubbo支持协议【常用】

dubbo协议

  • 缺省协议,使用基于mina1.1.7+hessian3.2.1的tbremoting交互。
  • 连接个数:单连接
  • 连接方式:长连接
  • 传输协议:TCP
  • 传输方式:NIO异步传输
  • 序列化:Hessian二进制序列化
  • 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用dubbo协议传输大文件或超大字符串。
  • 适用场景:常规远程服务方法调用

    为什么采用异步单一长连接?
    因为服务的现状大都是服务提供者少,通常只有几台机器,而服务的消费者多,可能整个网站都在访问该服务,比如Morgan的提供者只有6台提供者,却有上百台消费者,每天有1.5亿次调用,如果采用常规的hessian服务,服务提供者很容易就被压跨,通过单一连接,保证单一消费者不会压死提供者,长连接,减少连接握手验证等,并使用异步IO,复用线程池,防止C10K问题。 接口增加方法,对客户端无影响,如果该方法不是客户端需要的,客户端不需要重新部署; 输入参数和结果集中增加属性,对客户端无影响,如果客户端并不需要新属性,不用重新 部署; 输入参数和结果集属性名变化,对客户端序列化无影响,但是如果客户端不重新部署,不管输入还是输出,属性名变化的属性值是获取不到的。 总结:服务器端和客户端对领域对象并不需要完全一致,而是按照最大匹配原则。

    rmi协议

    hessian协议

    http协议

    webservice协议

    thrift协议

    memcached协议

    redis协议

参数配置


调用链监控

dubbo+zipkin实现调用链监控

参考文献

Dubbo官方文档
精通Dubbo——Dubbo支持的协议的详解
精通Dubbo——Dubbo配置文件详解
dubbo+zipkin调用链监控
分布式系统调用跟踪实践