Dubbo原理及其所涉及到的设计模式?

基于源码进行解析Dubbo

Posted by MasterJen on December 4, 2018

Hey Dubbo

Living without an aim is like sailing without a compass.–生活没有目标,犹如航海没有罗盘.

在今天的blog开始之前,先和大家浅谈一下网站的发展架构发展历史.

随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,

分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进.
  • 架构演进

单一应用架构

        网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本. 

        此时,用于简化增删改查工作量的 数据访问框架(ORM) 是关键.  

垂直应用架构

        当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率. 

        此时,用于加速前端页面开发的 Web框架(MVC) 是关键.

分布式服务架构

        当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求. 

        此时,用于提高业务复用及整合的 分布式服务框架(RPC) 是关键.            

流动计算架构

        当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率. 

        此时,用于提高机器利用率的 资源调度和治理中心(SOA) 是关键.            

那么我们面临的问题又是什么呢? 总结下来 无非几点:

    1 多种调用传输方式:HTTP方式、WebService方式;

    2 服务调用依赖关系:人工记录,查看代码分析;

    3 服务调用性能监控:日志记录,人工查看时间;

    4 服务与应用紧耦合:服务挂掉,应用无法可用;

    5 服务集群负载配置:Nginx配置,存在单点问题;

那么有了问题,我们就会思考了,去追寻最佳解决方案了,我们选择什么技术来实现我们的需求呢?该技术首先要符合什么标准呢?

1 支持当前业务需求,这是最最基本的条件;

2 服务避免单点问题,去中心化;

3 服务高可用、高并发,解耦服务依赖;

4 服务通用化,支持异构系统调用服务;

5 服务依赖关系自维护,可视化;

6 服务性能监控自统计,可视化;

7 服务需自带注册、发现、健康检查、负载均衡等特性;

8 开发人员关注度高,上手快,简单轻量,低侵入; 

有了问题,有了标准,那么我们就可以去选择哪种技术了.

回到我们今天的主题 Dubbo ,那么Dubbo 究竟有什么优点值得我们去使用呢?

1.远程通讯: 提供对多种基于长连接的NIO框架抽象封装, 

    包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式.

2.软负载均衡及容错机制: 提供基于接口方法的透明远程过程调用,

    包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持,可在内网替代F5等硬件负载均衡器,降低成本,减少单点 

3 服务自动注册与发现: 基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器 .

4.提供完善的管理控制台dubbo-admin与简单的控制中心dubbo-monitor 

5 Dubbo提供了伸缩性很好的插件模型,很方便进行扩展(ExtensionLoader)

6 支持多协议 

7 Dubbo采用全spring配置方式,透明化接入应用,对应用没有任何API侵入,只需用Spring加载Dubbo的配置即可,Dubbo基于Spring的Schema扩展进行加载.

了解了其种种优势,那么我们该去贴近它,去一看究竟了.

Dubbo 源码架构如下图所示:

Dubbo架构图

各个模块的作用是什么呢?

1 dubbo-common 公共逻辑模块,包括Util类和通用模型.

2 dubbo-remoting 远程通讯模块,相当于Dubbo协议的实现,如果RPC用RMI协议则不需要使用此包.

3 dubbo-rpc 远程调用模块,抽象各种协议,以及动态代理,只包含一对一的调用,不关心集群的管理.

4 dubbo-cluster 集群模块,将多个服务提供方伪装为一个提供方,包括:负载均衡、容错、路由等,集群的地址列表可以是静态配置的,也可以是由注册中心下发.

5 dubbo-registry 注册中心模块,基于注册中心下发地址的集群方式,以及对各种注册中心的抽象.

6 dubbo-monitor 监控模块,统计服务调用次数,调用时间的,调用链跟踪的服务.

7 dubbo-config 配置模块,是Dubbo对外的API,用户通过Config使用Dubbo,隐藏Dubbo所有细节.

8 dubbo-container 容器模块,是一个Standalone的容器,以简单的Main加载Spring启动,因为服务通常不需要Tomcat/JBoss等Web容器的特性,没必要用Web容器去加载服务.

各个模块之间的联系是:

Dubbo联系

那么Dubbo 的工作流程是什么呢?

Dubbo工作流程

    1、Provider:暴露服务的服务提供方. Consumer: 调用远程服务的服务消费方.

  2、Registry:服务注册与发现的注册中心. Monitor: 统计服务的调用次调和调用时间的监控中心.

  3、Container: 服务运行容器    

根据上图的流程我们可以知道Dubbo之间的调用关系是:

   1、服务容器负责启动,加载,运行服务提供者. 

    2、服务提供者在启动时,向注册中心注册自己提供的服务. 

    3、服务消费者在启动时,向注册中心订阅自己所需的服务. 

    4、注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者. 

    5、服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用. 

    6、服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心.

Dubbo 调用的方式 其实有两种 一种是 异步调用 ,另一种是 本地调用

异步调用

   基于NIO的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小    
​                        

本地调用:

    本地调用,使用了Injvm协议,是一个伪协议,它不开启端口,不发起远程调用,只在JVM内直接关联,但执行Dubbo的Filter链.
  • Dubbo支持的注册中心

Multicast注册中心

    不需要启动任何中心节点,只要广播地址一样,就可以互相发现

    组播受网络结构限制,只适合小规模应用或开发阶段使用.

    组播地址段: 224.0.0.0 - 239.255.255.255

Zookeeper注册中心

    是Apacahe Hadoop的子项目,是一个树型的目录服务,支持变更推送,适合作为Dubbo服务的注册中心,工业强度较高

    只需搭一个原生的Zookeeper服务器,

    并将Provider和Consumer里的dubbo.properties中的dubbo.registry.addrss的值改为zookeeper://127.0.0.1:2181即可使用

    阿里内部并没有采用Zookeeper做为注册中心,而是使用自己实现的基于数据库的注册中心,即:Zookeeper注册中心并没有在阿里

    内部长时间运行的可靠性保障,其可靠性依赖于Zookeeper本身的可靠性.

     ZooKeeper集群由一组Server节点组成,这一组Server节点中存在一个角色为Leader的节点,其他节点都为Follower.

     当客户端Client连接到ZooKeeper集群,并且执行写请求时,这些请求会被发送到Leader节点上,然后Leader节点上数据变更会同步到集群中其他的Follower节点.

Redis注册中心

Simple注册中心

相信大家又会问了,每个框架一般都会有其设计模式来支撑框架的使用,那么Dubbo的设计模式是什么呢?

  • 设计模式

工厂模式

    在 package com.alibaba.dubbo.config;  包下的  ServiceConfig中有这么一局代码

    public class ServiceConfig<T> extends AbstractServiceConfig {
    ​    
        private static final Protocol protocol = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

        private static final ProxyFactory proxyFactory = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

        private static final Map<String, Integer> RANDOM_PORT_MAP = new HashMap();

        private static final ScheduledExecutorService delayExportExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("DubboServiceDelayExporter", true));

由此可以知道

private static final Protocol protocol = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

只是实现类的获取采用了jdkspi的机制完成了工厂模式.

装饰器模式

Dubbo在启动和调用阶段都大量使用了装饰器模式.以Provider提供的调用链为例,具

体的调用链代码是在ProtocolFilterWrapper的buildInvokerChain完成的,具体是将注解中含

有group=provider的Filter实现,按照order排序,最后的调用顺序是 

EchoFilter-》ClassLoaderFilter-》GenericFilter-》ContextFilter-》ExceptionFilter-》  

TimeoutFilter-》MonitorFilter-》TraceFilter 

更确切地说,这里是装饰器和责任链模式的混合使用

观察者模式

Dubbo的provider启动时,需要与注册中心交互,先注册自己的服务,再订阅自己的服

务,订阅时,采用了观察者模式,开启一个listener.注册中心会每5秒定时检查是否有服

务更新,如果有更新,向该服务的提供者发送一个notify消息,provider接受到notify消息

后,即运行NotifyListener的notify方法,执行监听器方法. 

动态代理模式

根据上述代码就可以知道private static final ProxyFactory proxyFactory = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

Dubbo扩展jdkspi的类ExtensionLoader的Adaptive实现是典型的动态代理实现.

好了,相信大家看到这里也已经对Dubbo有了更明确的认识了.如果还有什么疑问,可以随时留言哦.