vlambda博客
学习文章列表

阅读Dubbo源码无从下手?

Apache Dubbo作为一款高性能、轻量级的开源 Java RPC 框架,已经广泛的运用在生产中。并且在我们升职加薪的路上,阅读Dubbo源码、理解Dubbo的设计思想以及可以举一反三应用于实践的能力,可以极大的提高面试时的议价筹码。

今天起,我们将展开一个新的系列——Dubbo源码阅读,一起开启对Dubbo这个优秀RPC框架的学习之路。本篇将介绍Dubbo的架构和角色、源码环境的搭建、项目结构解读和运行官方Demo。

官网:http://dubbo.apache.org/

架构和角色

Dubbo有三大核心能力:提供了面向接口的远程方法调用;可靠、智能的容错和负载均衡;服务自动注册和发现能力。简单地说, Dubbo 是一个分布式服务框架,致力于提供高性能、透明化的 RPC 远程服务调用方案以及服务治理方案,以帮助我们解决微服务架构落地时的问题。

阅读Dubbo源码无从下手?

如上图所示,Dubbo的角色主要分为四种:

Consumer:服务消费者。在它启动的时候,会向 Registry 进行订阅操作。订阅操作会从 ZooKeeper 中获取 Provider 注册的 URL,并在 ZooKeeper 中添加相应的监听器。获取到 Provider URL 之后,Consumer 会根据负载均衡算法从多个 Provider 中选择一个 Provider 并与其建立连接,最后发起对 Provider 的 RPC 调用。如果 Provider URL 发生变更,Consumer 将会通过之前订阅过程中在注册中心添加的监听器,获取到最新的 Provider URL 信息,进行相应的调整,比如断开与宕机 Provider 的连接,并与新的 Provider 建立连接。Consumer 与 Provider 建立的是长连接,且 Consumer 会缓存 Provider 信息,所以一旦连接建立,即使注册中心宕机,也不会影响已运行的 Provider 和 Consumer。

Monitor:监控中心。非必须角色, 宕机不会影响 其他角色功能,用于统计服务的调用次数和调用时间。Provider 和 Consumer 在运行过程中,会在内存中统计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

源码环境搭建

1.Fork源码到自己的GitHub仓库。

阅读Dubbo源码无从下手?

2.下载项目到本地:

git clone git@github.com:xxxxxxxx/dubbo.git

3.Check Out分支

git checkout -b dubbo-2.7.x dubbo-2.7.x

4.使用mvn编译项目

mvn clean install -Dmaven.test.skip=true 

如果编译成功,可以看到如下效果:

阅读Dubbo源码无从下手?

Zookeeper注册中心搭建

除了源码编译,dubbo 启动还需要依赖注册中心,这里我们使用 zookeeper。

1.首先下载源码。https://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/

2.解压缩源码包并且进入源码目录

tar -zxf zookeeper-3.4.14.tar.gzcd zookeeper-3.4.14

3.复制配置文件

cd confcp zoo_sample.cfg zoo.cfg

4.启动项目

./bin/zkServer.sh start

项目结构

使用Idea打开项目之后,可以看到Dubbo的项目结构如下:

阅读Dubbo源码无从下手?

dubbo-rpc 模块Dubbo 中对远程调用协议进行抽象的模块,其中抽象了各种协议,依赖于 dubbo-remoting 模块的远程调用功能。dubbo-rpc 模块的实现中只包含一对一的调用,不关心集群的相关内容。

dubbo 的很多模块层级结构相似,为一个抽象的 api 模块和其他实现 api 模块的子模块。

以 dubbo-rpc 为例,dubbo-rpc-api 子模块是核心抽象,其他子模块是针对具体协议的实现,例如,dubbo-rpc-dubbo 子模块是对 Dubbo 协议的实现,依赖了 dubbo-remoting-netty4 等 dubbo-remoting 子模块。

dubbo-cluster 模块负责管理 dubbo 集群的模块,提供了负载均衡、容错、路由等一系列集群相关的功能,最终的目的是将多个 Provider 伪装为一个 Provider,这样 Consumer 就可以像调用一个 Provider 那样调用 Provider 集群了。 

dubbo-configcenter 模块Dubbo 的动态配置模块,主要负责外部化配置以及服务治理规则的存储与通知,提供了多个子模块用来接入多种开源的服务发现组件。

dubbo-common 模块:Dubbo 的一个公共模块,其中有很多工具类以及公共逻辑,比如 Dubbo 的 SPI 实现、时间轮实现、动态编译器等。

dubbo-remoting 模块:Dubbo 的远程通信模块,其中的子模块依赖各种开源组件实现远程通信。有如下主要实现,例如,dubbo-remoting-netty4 子模块依赖 Netty 4 实现远程通信,dubbo-remoting-zookeeper 通过 Apache Curator 实现与 ZooKeeper 集群的交互。

dubbo-registry 模块:Dubbo 中负责与多种开源注册中心进行交互的模块,提供注册中心的能力。其中,dubbo-registry-zookeeper 子模块是 Dubbo 接入 ZooKeeper 注册中心的具体实现。

dubbo-monitor 模块:Dubbo 的监控模块,主要用于统计服务调用次数、调用时间以及实现调用链跟踪的服务。

dubbo-config 模块:Dubbo 对外暴露的配置都是由该模块进行解析的,用户无需了解细节,只需按照配置规则进行配置。dubbo-config-api 子模块负责处理 API 方式使用时的相关配置,dubbo-config-spring 子模块负责处理与 Spring 集成使用时的相关配置方式。

dubbo-metadata 模块:Dubbo 的元数据模块。dubbo-metadata 模块的实现套路也是有一个 api 子模块进行抽象,然后其他子模块进行具体实现。

官方Demo

Dubbo 为我们提供了三种非常基础的示例程序,在 dubbo-demo 模块下。

阅读Dubbo源码无从下手?

这四个子模块分别为 demo-api(demo 的api抽象)、demo-api(使用 api 配置)、demo-xml(xml 方式配置)、demo-annotation(注解配置)。

1、dubbo-demo-interface

这里定义的 demo 所需要使用的业务接口 DemoService。

public interface DemoService { 
    String sayHello(String name); // 同步调用     default CompletableFuture<String> sayHelloAsync(String name) {          return CompletableFuture.completedFuture(sayHello(name));     }//异步}

因为远程调用都需要有 provider 和 counsumer 两个角色,一个用来提供服务,一个用来调用服务,两者对业务接口一个是实现,一个是示例化调用。这里的三个子模块具体实现都是基于这样对逻辑。

2、dubbo-demo-annotation

因为三个子模块实现大同小异,这里我们以注解配置方式为例。

因为三个子模块实现大同小异,这里我们只以注解配置方式为例。

dubbo.registry.address=zookeeper://localhost:2181registryConfig.setAddress("zookeeper://localhost:2181");

然后分别启动 provider 和 consumer。consumer 成功输出结果。


原理:

先看 provider 的实现

public class Application     public static void main(String[] args) throws Exception       // 使用AnnotationConfigApplicationContext初始化Spring容器,       // 从ProviderConfiguration这个类的注解上拿相关配置信息         AnnotationConfigApplicationContext context =                new AnnotationConfigApplicationContext(                   ProviderConfiguration.class);         context.start();         System.in.read();     }    @Configuration // 配置类     //@EnableDubbo注解指定包下的Bean都会被扫描,并做Dubbo服务暴露出去     @EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.provider")          // PropertySource注解指定了其他配置信息     @PropertySource("classpath:/spring/dubbo-provider.properties")          static class ProviderConfiguration         @Bean         public RegistryConfig registryConfig()             RegistryConfig registryConfig = new RegistryConfig();             registryConfig.setAddress("zookeeper://127.0.0.1:2181");             return registryConfig;         }     } 

DemoServiceImpl 实现了 DemoService 接口,在 org.apache.dubbo.demo.provider 目录下被扫描到从而暴露成 Dubbo 服务。

在看consumer模块,Application 通过 AnnotationConfigApplicationContext 初始化 Spring 容器,会扫描指定目录下 DemoServiceComponent 这个 Bean,然后通过 @Reference 注解注入 Dubbo 服务相关的 Bean

@Component("demoServiceComponent"public class DemoServiceComponent implements DemoService     @Reference // 注入Dubbo服务     private DemoService demoService;     @Override     public String sayHello(String name)         return demoService.sayHello(name);     } 

希望上述内容可以帮助到你。下一篇将讲解 JDK SPI 和 Dubbo SPI。