Dubbo 概述

Dubbo 概述

Dubbo 核心组件

在这里插入图片描述

层次 作用
Service 该层与业务逻辑相关,根据 provider 和 consumer 的业务设计对应的接口和实现
Config 对外配置接口,以 ServiceConfig 和 ReferenceConfig 为中心。可以理解为该层管理了整个 Dubbo 配置
Proxy 使用动态代理的方式为接口创建代理类,Proxy 层最主要的接口就是 ProxyFactory。其默认的扩展点有:stub、jdk、javassist。jdk 使用反射的方式创建代理类,javassist 通过拼接字符串然后编译的方式创建代理类。对于服务提供者,代理的对象是接口的真实实现。 对于服务消费者,代理的对象是远程服务的 invoker 对象
Registry 主要负责的就是服务的注册与发现。这层的主要接口就是 RegistryFactory,其接口方法有 @Adaptive 注解,会根据参数 protocol 来选择实现,默认的扩展实现有:zookeeper、redis、multicast(广播模式)、内存
Cluster 集群容错层,主要负责:远程调用时的容错策略(如快速失败、失败重试);选择具体调用节点的负载均衡策略(如随机、一致性 hash 等);特殊调用路径的路由策略(如消费者只会调用某个 IP 的生产者)
Monitor 负责监控统计调用次数和调用时间
Protocal 远程调用层,封装 RPC 调用的具体过程。Protocal 是 Invoker 暴露(发布一个任务让消费者调用)和引用(引用一个服务到本地)的主功能入口,它负责管理 Invoker 的整个生命周期。Invoker 是 Dubbo 的核心模型,它代表一个可执行体,允许向它发起 invoke 调用,它可能执行一个本地接口实现,也可能执行一个远程接口实现
Exchange 封装请求响应模式,如如何将同步请求转化为异步请求。以 Request 和 Response 为中心,扩展接口为 Exchanger、ExchangeChannel、ExchangeClient 和 ExchangeServer
Transport 网络传输层,抽象 Mina 和 Netty 为统一接口。用户也可以扩展接口添加更多网络传输方式
Serialize 序列化的作用是把对象转化为二进制流,然后在网络中传输。负责整个框架网络传输时的序列化和反序列化工作

Dubbo 总体调用流程

服务暴露过程

在这里插入图片描述

服务端在框架启动时,会初始化服务实例,通过 Proxy 组件调用具体协议(Protocol),把服务端要暴露的接口封装成 Invoker(真实是类型是 AbstractInvoker),然后转换成 Exporter,这时框架会打开服务端口并记录服务实例到内存中,最后通过 Registry 把服务元数据注册到注册中心。

  • Proxy 组件:Dubbo 中只需要引用一个接口就可以调用远程服务。其实这是 Dubbo 为我们生成的代理类,调用的方法是 Proxy 组件生成的代理方法,会自动发起远程/本地调用,并返回结果。整个过程对用户全透明
  • Protocol:可以将对接口的配置分居不同的协议转化成不同的 Invoker 对象。例如协议为 dubbo 时会将远程接口配置转换成一个 DubboInvoker
  • Exporter:用于暴露到注册中心的对象,持有一个 Invoker 对象
  • Registry:将 Exporter 注册到注册中心

以上就是服务暴露过程。消费者在启动时会通过 Registry 在注册中心订阅服务端的元数据(包括 IP 和 端口),并在第一个初始化时从注册中心**拉取全量服务端信息****

消费者调用流程

在这里插入图片描述

首先调用也是从一个 Proxy 开始,Proxy 持有一个 Invoker 对象并触发 invoke 调用。在 invoke 调用过程中,使用 Cluster 进行容错、路由以及负载均衡。Cluster 先通过 Directory 获取可用的远程服务 Invoker 列表,根据用户配置的路由规则(例如指定某些方法智能调用某个节点)将 Invoker 过滤一遍

然后存活下来的 Invoker 通过 LoadBalance 方法做负载均衡,选出一个可以调用的 Invoker。选中的 Invoker 会在调用 invoke 之前经过一个过滤器链(通常处理上下文、限流、计数等操作)

接着会使用 Network Client 做数据传输。传输之前通过 Codec 做私有协议构造,然后进行序列化传输至服务端。服务端接收到数据包后也会使用 Codec 处理协议头等,完成后对数据报文进行反序列化处理

随后远程调用请求(Request)被分配到 ThreadPool 中进行处理。Server 会处理这些 Request,根据请求查找对应的 Exporter(内部持有 Invoker),Invoker 用装饰器模式套了很多 Filter,因此在 Invoker 调用之前会经过服务端的过滤链

最终得到了服务端具体的接口实现并调用,然后将结果原路返回

Dubbo 注册中心

在 Dubbo 为服务体系中,注册中心是核心组件之一。Dubbo 通过注册中心实现了分布式环境中服务的注册和发现,是各个分布式节点之前的纽带。其主要作用如下:

  • 动态加入:一个服务提供者通过注册中心可以动态地把自己暴露给其他消费者,无需消费者逐个更新配置文件
  • 动态发现:一个消费者可以动态感知新的配置、路由规划和服务提供者,无需重启服务使之生效
  • 动态调整:注册中心支持参数动态调整,新参数自动更新到所有相关服务点
  • 统一配置:避免本地配置导致每个服务的配置不一致问题

1 工作流程

注册中心整体流程如下图所示:

在这里插入图片描述

  • 服务提供者启动时,会向注册中心写入自己的元数据信息,同时会订阅配置元数据信息
  • 消费者启动时,会向注册中心写入自己的元数据信息,同时订阅服务提供者、配置、路由元数据信息
  • 服务治理中心(dubbo-admin)启动时,会同时订阅 服务提供者、消费者、配置、路由元数据信息
  • 当有服务离开或新的服务加入时,注册中心服务提供者目录会发生变化,变化信息将动态通知给消费者和服务治理中心
  • 当消费者发起服务调用时,会异步将调用、统计信息上报至监控中心(dubbo-monitor)

2 数据结构

注册中心的总体流程相同,但不同的注册中心有不同的实现方式,其数据结构也不同。常见的注册中心实现是 ZookeeperRedis。下面重点介绍注册中心的 Zookeeper 的实现方式

Zookeeper 是树形结构的注册中心,每个节点分为持久节点、持久顺序节点、临时节点、临时顺序节点

  • 持久节点:服务注册后保证节点不会丢失,注册中心重启仍然节点存在
  • 持久顺序节点:在持久节点的基础上增加节点顺序功能
  • 临时节点:服务注册后连接丢失或 session 超时,注册节点会被自动移除
  • 临时顺序节点:在临时节点的基础上增加节点顺序功能

Dubbo 使用 Zookeeper 作为注册中心时,只使用持久节点和临时节点

假设 /dubbo/com.test.TestService/providers 是服务提供者在 Zookeeper 上的注册路径,该结构分为四层:root(根节点,默认是 dubbo)、service(接口名称,对应于 com.test.TestService)、四种服务目录(示例中的 providers 以及 consumers、routers、configurators)。在服务分类节点下是具体的 DUbbo 服务 URL,树形结构实例如下:

+ /dubbo
	+-- com.test.TestService
		+-- providers 
		+-- consumers
		+-- routers
		+-- configurators
  • 树节点的根目录是注册中心分组,下面有多个服务接口,分组值来自用户配置 < dubbo:registry > 中的 group 属性
  • 服务接口下包含 4 个子目录(providers、consumers、routers 以及 configurators),都是持久节点
  • 服务提供者目录(/dubbo/com.test.TestService/providers)下面包含多个服务提供者 URL 元数据信息
  • 服务消费者目录(/dubbo/com.test.TestService/consumers)下面包含多个消费者 URL 元数据信息
  • 路由配置目录(/dubbo/com.test.TestService/routers)下面包含多个用于消费者路由策略 URL 元数据信息
  • 动态配置目录(/dubbo/com.test.TestService/configurators)下面包含多个用于服务提供者动态配置 URL 元数据信息

3 订阅发布的实现

3.1 发布实现

服务提供者和服务消费者都需要将自己的元数据信息注册到注册中心。服务提供者的注册是为了让服务消费者感知服务的存在,从而发起远程调用;也让服务治理中心感知有新的服务提供者上线。消费者的注册是让了让服务治理中心可以发现自己

Zookeeper 实现发布的代码很简单,只是调用了 Zookeeper 客户端在注册中心上创建一个目录

zkClient.create(toUrlPath(url));

取消发布也很简单,只是把 ZooKeeper 注册中心上对应的路径删除即可

zkClient.delete(toUrlPath(url));
3.2 订阅实现

订阅通常有 pull 和 push 两种方式,Dubbo 目前采用 pull(拉取方式),后续接收事件并重新拉取数据

在服务暴露时,服务提供者会订阅 configurators 用于监听动态配置。消费者启动时会订阅 providers、routers 以及 configurators 这三个目录

Dubbo 中 Zookeeper 的客户端实现有两种:

  • Apache Curator(默认)
  • zKClient

用户可以通过 < dubbo:registry > 的 client 属性设置 curator 或 zkclient 作为 Zookeeper 客户端的实现方式

Zookeeper 注册中心采用的是 事件通知 + 客户端拉取 的方式,客户端第一次连接上注册中心时,会获取对应目录下的全量数据,并在订阅的节点上注册一个 watcher,客户端于注册中心保持 TCP 长连接,后续每个节点有数据变化的时候,注册中心会根据 watcher 的回调通知客户端数据有更新,客户端会将对应节点下的数据重新从注册中心拉取过来。

Dubbo 启停原理解析

1 优雅停机原理解析

优雅停机特性是所有 RPC 框架中非常重要的特性之一,因为核心业务在服务器正在执行时突然中断可能出现严重后果。Dubbo 的优雅停机原理如下图所示:

在这里插入图片描述

Dubbo 中实现优雅停机有几下几步:

  1. 收到 kill -9 进程退出信号,Spring 容器会触发容器销毁事件
  2. provider 端取消注册服务元数据信息
  3. consumer 端会收到最新的服务调用列表(不包含准备停机的地址)
  4. Dubbo 协议发送 readonly 事件报文通知 consumer 服务不可用
  5. 服务端等待已执行的任务结束并拒绝新任务执行
  6. provider 端断开与 consumer 端的 TCP 连接

既然第三步 consumer 端已经被通知最新的服务调用地址,provider 端为什么还要发送 readonly 事件报文给 consumer 端呢?这里主要考虑到注册中心推送服务有网络延迟,以及客户端计算服务列表可能占用一些时间。Dubbo 协议发送 readonly 事件报文时,consumer 端会设置响应的 provider 为不可用状态,下次负载均衡将不再调用下线的机器

Dubbo 远程调用

1 Dubbo 通信协议

Dubbo 支持 9 中通信协议

2.1 dubbo://(推荐)

在这里插入图片描述

描述:Dubbo 默认通信协议,单一长连接,进行的是 NIO 异步通信,基于 hessian 作为序列化协议

连接个数:单连接

连接方式:长链接

传输协议:TCP

传输方式:NIO 异步传输

序列化:Hessian 二进制序列化

适用范围:

  • 传入传出参数数据包较小(建议小于100K)
  • 消费者比提供者个数多,单一消费者无法压满提供者
  • 尽量不要用 dubbo 协议传输大文件或超大字符串

适用场景 :

  • 高并发场景,一般是服务提供者就几台机器,但是服务消费者有上百台,可能每天调用量达到上亿次!此时用长连接是最合适的,就是跟每个服务消费者维持一个长连接就可以,可能总共就100个连接。然后后面直接基于长连接 NIO 异步通信,可以支撑高并发请求 ,如果上亿次请求每次都是短连接的话,服务提供者会扛不住
  • 因为走的是单一长连接,所以传输数据量太大的话,会导致并发能力降低。所以一般建议是传输数据量很小,支撑高并发访问

约束 :

  • 参数及返回值需实现 Serializable 接口
  • 参数及返回值不能自定义实现 List, Map, Number, Date, Calendar 等接口,只能用 JDK 自带的实现,因为 hessian 会做特殊处理,自定义实现类中的属性值都会丢失
  • Hessian 序列化,只传成员属性值和值的类型,不传方法或静态变量
2.2 rmi://

描述:RMI 协议采用 JDK 标准的 java.rmi.* 实现,采用阻塞式短连接和 JDK 标准序列化方式

连接个数:多连接

连接方式:短链接

传输协议:TCP

传输方式:同步传输

序列化:Java 标准二进制序列化

适用范围:

  • 传入传出参数数据包大小混合
  • 消费者与提供者个数差不多
  • 可传文件

适用场景 :

  • 常规远程服务方法调用,与原生RMI服务互操作,一般较少用

约束 :

  • 参数及返回值需实现 Serializable 接口
  • dubbo 配置中的超时时间对 RMI 无效,需使用 java 启动参数设置 -Dsun.rmi.transport.tcp.responseTimeout=3000
2.3 hessian://

描述:Hessian 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现

连接个数:多连接

连接方式:短链接

传输协议:HTTP

传输方式:同步传输

序列化:Hessian 二进制序列化

适用范围:

  • 传入传出参数数据包较大
  • 提供者比消费者个数多,提供者压力较大
  • 可传文件

适用场景 :

  • 页面传输
  • 文件传输
  • 原生 Hessian 服务互操作

约束 :

  • 同 dubbo 协议
2.4 http://

描述:基于 HTTP 表单的远程调用协议,采用 Spring 的 HttpInvoker 实现

连接个数:多连接

连接方式:短链接

传输协议:HTTP

传输方式:同步传输

序列化:表单序列化

适用范围:

  • 传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或 URL 传入参数,暂不支持传文件

适用场景 :

  • 需同时给应用程序和浏览器 JS 使用的服务

约束 :

  • 参数及返回值需符合 Bean 规范
2.5 webservice://

描述:基于 WebService 的远程调用协议,基于 Apache CXF 的 frontend-simple 和 transports-http 实现

连接个数:多连接

连接方式:短链接

传输协议:HTTP

传输方式:同步传输

序列化:同步传输 SOAP 文本序列化

适用场景 :

  • 系统集成,跨语言调用
2.6 thrift://

描述:当前 dubbo 支持 的 thrift 协议是对 thrift 原生协议的扩展,在原生协议的基础上添加了一些额外的头信息,比如 service name,magic number 等

2.7 memcached://

描述:基于 memcached 实现的 RPC 协议

2.8 redis://

描述:基于 Redis 实现的 RPC 协议

2.9 rest://

描述:基于标准的 Java REST API——JA

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值