1.介绍Dubbo

1.1 应用架构演进过程

1.1.1 单体应用

(1) JEE时期
JEE即Java Platform Enterprise Edition,提供了企业级软件开发的运行环境和开发工具。将企业软件划分为展示层、业务逻辑层和数据存储层。
优点:分层设计明确了不同团队的分工,衍生出了前端团队、后端团队和DBA团队;JEE的开发简单,所有的类都能直接本地引用和使用,事务处理只需要依赖数据库。由于面向企业内部用户,使用者较少,不需要考虑高并发场景,加上JEE的稳定性,所有基本能满足日常需求。
缺点:大多数应用都在一个JVM中,随着应用增大,性能不断下降;业务之间的耦合严重,即是有各种规范和约束,但随着业务逻辑复杂度的增加,开发人员的流动,整个应用的维护会变得越来越难;EJB大量使用xml作为配置文件,后期需要配置出一个服务成本非常高;各种约束和规范也加大了开发的成本。

(2)MVC框架时期
由于EJB的各种问题严重影响开发效率,开源框架Spring、Struts、Hibernate等开始流行,架构也逐渐演变成MVC架构。其中Model属于模型部分,主要负责具体业务逻辑和数据存取;View是视图,处理应用的展示部分;Controller是控制器,处理用户的交互,它会从视图层读取数据并传递给模型。
这一时期架构与JEE比较相似,但MVC分层更加简单,框架更加轻量级,开发效率有了很大提升,单元测试也更加完善。但应用还是会被打到一个War包,系统耦合度仍没有太大改善。到了后期,很多企业会对应用做垂直拆分,把业务上没有关联的系统独立拆分出来,形成独立对外提供服务的系统。此时服务之间完全独立,无法进行远程调用,很多基础代码不能复用,需要复制使用。这种方式一定程度上缓解了单体应用的臃肿,但没有本质上解决问题。

1.1.2 分布式应用

(1)SOA
互联网toC业务不断星期,传统单应用已经无法满足高并发的需求,单进程的水平扩展能力也及其有限。为了解决这个问题,SOA架构出现,对单一进程的应用进行拆分,形成独立对外提供服务的组件,每个组件通过网络协议对外提供服务。服务之间通过接口进行交互。SOA有以下特点:

  • 明确的协议。服务之间通过特定协议进行交互。
  • 明确的接口。根据规则划分不同的服务,每个服务都有明确的对外接口,这让服务的复用成为可能。
  • 合作方式的改变。后端团队会根据不同的服务进一步拆分,有了更加细化的分工与合作方式。
  • 通信方式。初期的方式是xml,后来被json取代。
    常见的SOA有两种实现方式:Web Service和ESB。Web Service使用SOAP协议,用http来传输xml数据,所有web service都会注册到目录中,每个服务都依赖这个目录来发现存在的服务。
    ESB即企业服务总线,服务之间的通信与调用都通过总线来完成,因此ESB没有注册中心一说。总线负责服务之间消息的解析、转换、路由、控制服务的可插拔,统一编排业务信息处理流程等。这种实现特别适合老企业的内部,不同语言开发或不同来源的应用系统,如外部购买、自研等,应用之间没有统一的交互协议,通过ESB总线可以屏蔽这些问题。
    缺点:Web Service的通信协议笨重,服务化管理配套不完善;ESB本身也是很重的东西,系统的变更可能又反过来影响总线的变更。

(2)微服务化
为了解决SOA的各种问题,服务化架构得到了进一步的演进,逐渐形成更加细粒度的微服务架构。在微服务架构中,一个应用会被拆分成一个个独立、可配置、可运行、可维护的子服务,极大地方便了服务的复用,通过不同的服务编排方式,快速产生信的业务逻辑。微服务与SOA一脉相承,但仍有一定区别:

  • 目的不同。ESB更强调业务流程的编排,历史应用的继承。而微服务使用一系列小的服务来实现整体哭成,可以有效地拆分服务,从而实现敏捷的部署和交付。
  • 服务粒度不同。微服务拆分的更加细小,从而可以方便地复用服务,编排出信的业务逻辑。SOA通常是比较粗粒度的划分。
  • 协议的不同。微服务通常使用统一的交互协议,如HTTP、自定义的协议,兼容老系统比较困难。(个人不是很同意)

(3)云原生
企业应用又遇到了新的挑战:

  • 容量动态规划。微服务出现后,服务容量的评估,小服务资源的浪费等问题。为了实现资源的动态规划,容器化成为标配,容器编排技术逐渐成熟。
  • 服务框架的臃肿。应用虽然已经微服务化,但应用中包含大量业务无关的资源库,即开发小服务也要带上一个臃肿的框架。为了让应用更轻量,下沉更多通用能力,服务网格开始出现。

1.2 Dubbo

1.2.1 什么是Dubbo

Dubbo是一个分布式高性能的RPC服务框架,核心设计原则:微内核+插件体系。


Dubbo的架构

图中Provider启动时会向注册中心把自己的元数据注册上去,Consumer启动时从注册中心订阅(第一次订阅会拉去全量数据)Provider的元数据,注册中心中发生数据变更会推送给订阅的Consumer。在获取服务元数据后,Consumer可以发起RPC调用,在RPC调用前后会向监控中心上报统计信息(并法数和调用的接口等)

1.2.2 Dubbo所包含的关键特性:

  • 面向接口代理的高性能RPC调用。
  • 服务自动注册与发现。
  • 运行期流量调度。内置条件、脚本等路由策略,通过配置不同的路由规则,轻松实现灰度发布、同机房优先等功能。
  • 智能负载均衡。
  • 高度可扩展能力。所有核心能力如Protocal、Transport、Serialization被设计为扩展点。
    可视化服务治理与运维。

1.2.3 Dubbo解决什么问题

架构演进

我们来回归以下不同应用架构的区别:

  • 单一应用架构:流量小时,只需一个应用,将所有功能都部署在一起,减少部署节点和成本。此时,用于简化增删改查工作量的ORM框架是关键。
  • 垂直应用架构:访问量逐渐增大时,单一应用增加机器带来的效果越来越小,将应用拆分成互不相干的几个应用以提升效率。此时,加速前端页面开发的Web框架(MVC)是关键。
    • 分布式服务架构:当垂直应用越来越多,应用之间的交互不可避免,将核心业务抽取处理,作为独立的服务,逐渐形成稳定的服务中心,使应用能更快速响应多变的市场需求。此时,用于提高业务复用与整合的分布式服务(RPC)框架是关键。
    • 流动计算架构:当服务越来越多,容量的评估、小服务资源浪费的问题显现,此时需要增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心是关键。

Dubbo着眼解决以下几个最基本的问题:

  • 高性能、透明的RPC调用。Dubbo可以让开发者像调用本地方法一样调用远程服务,不需要现式在代码中制定远程调用。整个过程对上次开发者透明,Dubbo会自动完成所有后续的操作,例如:负载均衡、路由、协议转换、序列化等。
  • 服务的自动注册与发现。当服务越来越多时,服务URL配置管理变得非常困难,服务的注册与发现已经不能人工管理,此时需要一个注册中心,动态地注册和发现服务,使服务的位置透明。Dubbo适配了多种注册中心,Consumer可以通过订阅注册中心,及时知道Provider的信息,无须人工干预。
    • 自动负载均衡与容错。当服务越来越多时,F5硬件负载均衡的单点压力越来越大,Dubbo提供完整的集群容错机制,可以实现软件层面的负载均衡,以此降低硬件的压力。Dubbo还提供调用失败的各种容错机制,包括failover、failfast、结果集合并等。
    • 动态流量调度。在运行时,某些服务节点可能因为硬件原因需要减少负载,或者需要暂时下线,又或者需要单元化调用或者灰度等功能。Dubbo提供了管理控制台,用于可以在界面上动态地调整每个服务的权重、路由规则、禁用/启用,实现运行时流量调度。
    • 依赖分析与调用统计。当应用规模很大,服务间的依赖关系错综复杂,甚至分不清哪个要在哪个之前启动,架构师都不能完整地描述应用的架构关系。服务的调用量越来越大,服务的容量问题就暴露出来,这个服务徐阿哟多少机器支持?什么事后该加机器?Dubbo可以介入三方APM做分布式链路追踪与性能分析,或者使用已有的独立控制中心监控接口的调用次数与耗时,用户可以根据这些数据反推出系统容量。

1.3 Dubbo总体大图

1.3.1 Dubbo分层

Dubbo的总体氛围业务(Biz)层,RPC层,Remote三层。再细分一共可以分为10层。

  • Biz -> Service(Interface)

  • RPC -> Config(ConfigParser), Proxy(ProxyFactory),
    Registry(Registry,RegistryFactory,RegistryService), Cluster(Cluster,Directory,LoadBalance),
    Monitor(Monitor,MonitorFactory,MonitorService),Protocol(Protocal,Invoker,Exporter)

  • Remoting -> Exchange(Exchanger,ExchangeChannel,ExchangeClient,ExchangeServer),
    Transport(Codec,Transporter,ChannelHandler,Server/Client),
    Serialzie(ObjectOutput,Serialization,ObjectInput)
    其中Service和Config又可以认为是API层,主要提供给API使用者,使用者无须关心底层实现,只需要配置和完成业务代码即可。其他的都可以认为是SPI层,主要提供给扩展者使用,即二次开发扩展功能。

  • service 业务层:包括开发人员写的业务代码的接口与实现。

  • config 配置层:对外配置接口,以 ServiceConfig(暴露的服务配置), ReferenceConfig(引用的服务配置) 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类

  • proxy 服务代理层:无论Provider还是Consumer框架都会生成代理类,整个过程对上层透明, 使用起来像调用本地接口一样,代理层会自动做远程调用并返回结果。以 ServiceProxy 为中心,扩展接口为 ProxyFactory

  • registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry, RegistryService

  • cluster 集群容错层/路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance

  • monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService

  • protocol 远程调用层:封装 RPC 调用,Protocal是Invoker暴露和引用的主功能入口,负责管理Invoker的整个生命周期。Invoker是Dubbo的核心模型,框架中所有其他模型都向它靠拢,或者转换成它,它代表一个可执行体,允许向它发起invoker调用,可能是执行本地接口实现,也可能是一个远程的实现,还可能是一个集群的实现。以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter

  • exchange 信息交换层:封装请求响应模式,可以同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer

  • transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec

  • serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool

1.3.2 Dubbo总体调用过程

首先,Provider在框架启动时,会初始化服务实例,通过Proxy组件调用具体协议(Protocol),把服务端要暴露的接口封装成Invoker(真实类型是AbstractProxyInvoker),然后转换成Exporter,这个时候框架会打开服务端口等记录服务实例到内存中,最后通过Registry把服务元数据注册到注册中心。这就是服务端整个接口暴露的过程。下面讲解各个组件的定义:

  • Proxy组件:我们知道,Dubbo只需要引用一个接口就可以调用远程的服务,并且只需要像调用本地方法一样调用即可。其实是Dubbo为我们生成了代理类,调用的方法其实是Proxy组件生成的代理方法,会自动发起远程/本地调用,并返回结果,整个过程对用户完全透明。
  • Protocol:协议是对数据格式的一种约定,根据不同的协议可以转换成不同的Invoker对象。例如:用DubboProtocol可以把xml文件中一个远程接口的配置转换成一个DubboInvoker。
  • Exporter: 用于暴露到注册中心的对象,它内部持有Invoker对象,我们可以认为它在Invoker上包了一层。
  • Registry:把Exporter注册到注册中心。

以上就是整个服务暴露的过程,下面是消费者调用provider的过程:


Dubbo调用总体流程

首先,调用过程也是从给一个Proxy开始,Proxy持有一个Invoker对象,然后出发invoke调用。在invoke调用过程中,需要使用Cluster,Cluster负责容错,如调用失败的重试。Cluster在调用之前会通过Directory获取所有可以调用的远程服务Invoker列表(一个接口可能有多个节点提供服务)。由于可以调用的远程服务很多,如果用户配置了路由规则(如指定某些方法只能调用某个节点),那么还会根据路由规则将Invoker列表过滤一遍。然后存活下来的Invoker可能还会有很多,就需要使用LoadBalance方法做负载均衡,最终选出一个可以调用的Invoker。这个Invoker在调用之前又会经过一个过滤器链,处理上下文、限流、计数等。接着,会使用Client做数据传输,如Netty Client等。传输之前需要做一些私有协议的构造,使用Codec接口。构造完成后,就会对数据包做序列化,然后传输到Provider。(个人认为此处应该是先序列化再使用codec进行协议编码)
Provider收到数据包,也会使用Codec处理协议头和一些半包、粘包,处理完之后再对完整数据做反序列化处理。随后,这个Request会被分配到线程池中进行处理,Server会处理这些Request,根据请求查找对应的Exporter,进而拿到里面的Invoker,Invoker通过装饰器模式套了很多的Filter,因此在调用最终的实现类之前,又会经过一个Provider的过滤器链。最终得到具体接口实现并调用,再原路把结果返回。

你可能感兴趣的:(1.介绍Dubbo)