如果Dubbo还没精通原理,就从这里开始吧
Apache Dubbo作为一款高性能、轻量级的开源 Java RPC 框架,已经广泛的运用在生产中。并且在我们升职加薪的路上,阅读Dubbo源码、理解Dubbo的设计思想以及可以举一反三应用于实践的能力,可以极大的提高面试时的议价筹码。
本文讲解了如何学习 Dubbo 以及 Dubbo 产生的推理过程,并且还详细介绍了 Dubbo 源码的编译以及调试环境的搭建。
记得在2年多前面试某头部大厂的时候,一个面试官对我发出来了灵魂提问,如果让你设计一个RPC框架你怎么做,有什么要点要注意?
当时我支支吾吾答了半天也没有说出个所以然。虽然看过源码,但是我从来没思考过这种问题。当面试结束我知道晾凉了,我问面试官能不能给我点建议?
面试官很直接了当的对我说:那我直说了你别觉得受打击,我觉得你思考太少了,你肯定没有系统化的阅读源码和思考过一些技术背后的原理以及深层次的原因和联系,没有自己的沉淀并融入自己的体系,如果你想去更好的平台,这样是不行的。
这几句话深深刺痛了我的内心,对于一个一直在保持学习,却又无能为力只能在小公司CRUD的我来说,这像是对我前进的梦想判了死刑。
是啊,他说的对,但我该怎么做,我为什么没有思考这些问题呢?
这也许不只是我的问题,可能还有更多的你和我一样,心有余,而力不足。
从此我慢慢开始注意这些问题,从被动接受到主动吸收,学习的时候多问一些为什么,注意完善自己的知识体系。
总有天,正在阅读文章的你也会找到适合自己的方法以及理想的工作,让自己的努力变得更值得。
怎么学 Dubbo
和学习 MyBatis 一样,在学习 Dubbo 前先问自己几个问题。
在 Dubbo 的出现是为了解决什么问题?如果是你怎么解决这些问题?Dubbo 怎么做?
解决了什么问题?
Dubbo的产生一定是因为他解决了一些业务场景下的难题,Dubbo解决的问题其实设计到一个系统的架构演进过程。
一个系统最开始体量小,数据少,结构简单,单机系统就可以支持,但是随着业务发展系统产生性能瓶颈,系统架构也需要随之不断变化。对于系统逐渐产生的性能问题,我们可以从XYZ三个轴的方向去发展。X轴可以增加单机性能,比如加大物理机的配置,提高性能上限;y轴可以将单机进行复制,用多个机器来解决单机性能,Z轴可以将业务或者数据进行拆分,Y轴的基础上进一步提高总体系统性能的极限。
所以我们一般先从单体架构进入集群架构,在集群架构中通过负载均衡技术,将流量尽可能均摊到集群中的每台机器上,以此克服单台机器硬件资源的限制,做到横向扩展。
然后,因为集群架构扩展性差,并且不同业务的压力有不一样,我们拆分成微服务,进一步提高了服务的复用能力和瓶颈上限,对于压力大的服务还可以继续复制做集群架构。
看起来很完美,但是这都是有代价的。
原本在一个服务上就可以完成的操作现在却要跨机器,如何像调用本地服务一样方便?如何屏蔽复杂的网络通信细节?如何高效的通信?不同服务之间调用失败怎么办?服务多了如何去治理呢?如何去发现有效服务以及下线失效服务……
如何解决?
既然服务被拆开但需要通过网络像调用本地一样调用其他服务的能力,这其实就是RPC,那我们就一定会用到网络协议,需要在调用服务时候写繁琐的网络操作相关代码。如何解决这个问题呢?那就需要对网络IO操作的代码进行复用,所有远程调用的方法都直接调用同样的网络IO逻辑。这就不由的让我们想到了动态代理,我们只需要关注于数据,让代理去处理网络交互的细节。同样的,业务发起方需要有代理去发起请求,业务提供方也需要一个代理来处理请求,并且返回数据。
因为网络传输都是二进制而方法调用都是对象,如果需要高效通信,那就需要支持不同的序列化协议以及网络通信协议。如果如果需要发现服务就需要有一个中间层来解决,保存服务提供方的ip信息以及提供的能力信息。数据库、Redis、Zookeeper都可以提供这种能力作为一个注册中心,服务下线就需要及时的维护存贮在这些注册中心的信息。如果有多个服务提供,那就需要负载均衡能力,可以在客户端增加负载均衡算法……这样我们就构建出了一个很粗糙的RPC框架。
我并不是说Dubbo就是这么做的,Dubbo在各种环节的实现上要复杂得多,并且架构设计扩展性非常高。这样的推理重要的是会提供一个学习思路给你,让你明白你在做什么,而不是一头扎进源码里从代码去推理功能,明白需求才是根本。
Apache Dubbo
Apache Dubbo是一款高性能、轻量级的开源 Java RPC 框架,提供了三大核心能力:
● 面向接口的远程方法调用;
● 可靠、智能的容错和负载均衡;
● 服务自动注册和发现能力。
对于我们前面所提到的那些问题,Dubbo 都给出了一套解决方案。
如上图所示,Dubbo的角色主要分为四种:
Registry:注册中心。负责服务地址的注册与查找,服务的 Provider 和 Consumer 只在启动时与注册中心交互。注册中心通过长连接感知 Provider 的存在,在 Provider 出现宕机的时候,注册中心会立即推送相关事件通知 Consumer。
Provider:服务提供者。它启动的时候会向 Registry 进行注册操作,将自己服务的地址和相关配置信息封装成 URL 添加到 ZooKeeper 中。
Consumer:服务消费者。启动的时候会向 Registry 进行订阅操作。订阅操作会从 ZooKeeper 中获取 Provider 注册的 URL,并在 ZooKeeper 中添加相应的监听器。获取到 Provider URL 之后,Consumer 会根据负载均衡算法从多个 Provider 中选择一个 Provider 并与其建立连接,直接发起对 Provider 的 RPC 调用。
Monitor:监控中心。非必须角色, 宕机不会影响 其他角色功能,用于统计服务的调用次数和调用时间。Provider 和 Consumer 在运行过程中,会在内存中统计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
源码环境搭建
源码编译
编译源码首先需要的当然是访问GitHub官方仓库,地址:https://github.com/apache/dubbo,这里我们是以2.7.15版本为准讲解。如果你无法访问 GitHub,也可以后台回复“Dubbo”直接下载。
1.Fork源码到自己的GitHub仓库。
2.下载项目到本地,可以选择你自己仓库的项目地址:
git clone git@github.com:apache/dubbo.git
3.Check Out 分支
git checkout -b dubbo-2.7.15 dubbo-2.7.15
4.使用mvn编译项目,并且跳过测试。
mvn clean install -Dmaven.test.skip=true
如果编译成功,可以看到如下效果:
ZooKeeper注册中心搭建
除了源码编译,dubbo 启动还需要依赖注册中心,这里我们使用 ZooKeeper。为了方便,我们在这里只是启动最简单的单实例ZK。
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
这样,单台 ZooKeeper 就启动完成了。
如果你没有虚拟机或者云服务器,可以参考这篇文章的环境搭建。
官方Demo
Dubbo 为我们提供了三种非常基础的示例程序,在 dubbo-demo 模块下。
这四个子模块分别为 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
因为三个子模块实现大同小异,这里我们以注解配置方式为例。
因为三个子模块实现大同小异,这里我们只以注解配置方式为例。
首先需要为 provider 和 consumer 配置之前启动对注册中心地址。
dubbo.registry.address=zookeeper://localhost:2181
registryConfig.setAddress("zookeeper://localhost:2181");
然后分别启动 provider 和 consumer。consumer 成功输出结果。
到这里,Dubbo 系列的第一篇就结束了,后面我们将继续学习 Dubbo。