开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 11 天,点击查看活动详情
分布式RPC框架Apache Dubbo
学习目标
1、了解软件架构的演进过程
2、熟悉Dubbo框架的架构执行流程
3、了解RPC远程调用过程
1. 软件架构的演进过程
软件架构的发展经历了由单体架构、垂直架构、SOA架构到微服务架构的演进过程,下面我们分别了解一下这几个架构。
1.1 单体架构
特点: all in one
①所有功能集中在一个项目中
②所有功能都要达成war包部署到服务器
③ 通过集群(session共享集群)来提高服务器的性能
优点: ①.项目架构简单,前期开发的成本低,周期短,小型企业首先.
缺点: ①全部的功能都集中在一个项目中完成,对于大型项目来说,开发难度高,不容易开发及扩展和维护
1.2 垂直架构
架构说明:
按照业务进行切割,形成小的单体项目。
特点:
①.以单体架构为单位进行系统的划分,划分成一个个系统.
②.项目与项目之间独立开发,开发效率高.
③.项目是以接口调用为主
优点:
①.项目架构简单,前期开发的成本低,周期短,小型企业首先.
②.垂直架构进行mvc分层设计,针对分层做相应的处理做到集群(10~1000)
③.不同的项目采用不同的技术实现.
缺点:
①.全部的功能都集中在一个项目中完成,对于大型项目来说,开发难度高,不容易开发及扩展和维护.
②.集群扩展有瓶颈集群
②.项目与项目之间存在数据冗余,耦合度高.
②.项目是以接口调用为主,存在数据同步问题.
1.3 SOA架构
SOA全称为Service-Oriented Architecture,即面向服务的架构。
它可以根据需求通过网络对松散耦合的粗粒度应用组件(服务)进行分布式部署、组合和使用。
一个服务通常以独立的形式存在于操作系统进程中。
站在功能的角度,把业务逻辑抽象成可复用的服务,通过服务的编排实现业务的快速再生,
目的:把原先固有的业务功能转变为通用的业务服务,实现业务逻辑的快速复用。
架构说明:
将重复功能或模块抽取成组件的形式,对外提供服务,在项目与服务之间使用ESB(企业服务总线)的形式作为通信的桥梁。
特点: ①.基于soa服务思想进行功能的抽取(重复代码问题解决),以服务为中心来管理项目. ②.各个系统之间要进行调用,所以出现ESB来管理项目(可以使用各种技术实现:webservice,rpc等) ③.ESB是作为系统与系统之间桥梁,进行统一管理.
优点: ①.重复代码进行了抽取,提高了开发效率,提高了系统的可维护性. ②.可以针对某个系统进行扩展,做集群更容易. ③.采用ESB来管理服务组件,有利于降低企业开发项目难度
缺点: ①.系统与服务的界限模糊的,不利于设计. ②.ESB是作为系统与系统之间桥梁,没有统一标准,种类很多,不利于维护!
常见的企业服务总线:
- IBM WebSphere ESB
- Microsoft ESB
- JBOSS SOA Platform等等
1.4 微服务架构
架构说明:
- 将系统服务层完全独立出来,抽取为一个一个的微服务。
- 抽取的粒度更细,遵循单一原则。
- 采用轻量级框架协议传输。
特点:
①.把系统的服务层完全独立出来,有利于资源的重复利用,提高开发效率.
②.微服务遵守单一原则
③.微服务与微服务之间的调用使用restful轻量级调用.
优点:
①.微服务拆分更细,有利于资源的重复利用,提高开发效率
②.可以更加精准针对某个服务做方案
③.微服务去中心化,使用restful轻量级通信协议比使用ESB企业服务总线更容易维护
④.适应市场更容易,产品迭代周期更短.
缺点:
微服务量多,服务治理成本高,不利于系统维护
分布式系统架构且是微服务架构,技术成本高(容错,分布式事务等),对团队高挑战
2. Apache Dubbo概述
2.1 Dubbo简介
- 2.7之前alibaba,从2.7之后是apache
- Apache Dubbo是一款高性能(NIO)的Java RPC框架。其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。
- 2018年2月9日,Apache 基金会的邮件列表上发起了讨论是否接纳阿里的 Dubbo 项目进入 Apache 孵化器的投票。
- 2018年2月15日,邮件列表显示,Dubbo 获得了 14 张赞成票,在无弃权和反对票的情况下,正式通过投票,顺利成为 Apache 基金会孵化项目。
dubbo的版本说明
Apache Dubbo是一款应用广泛的高性能轻量级的Java 远程调用分布式服务框架,支持多种通信协议。当网站安装了Apache Dubbo并且启用http协议进行通信时,攻击者可以向网站发送POST请求,在请求里可以执行一个反序列化的操作,由于没有任何安全校验,这个反序列化过程可以执行任意代码。这里,序列化是指把某个编程对象转换为字节序列的过程,而反序列化是指把字节序列恢复为某个编程对象的过程。
漏洞影响的Apache Dubbo产品版本包括:2.7.0~2.7.4、2.6.0~2.6.7、2.5.x 的所有版本。
- Apache Dubbo官方建议用户网站升级到安全的2.7.5版本。
什么是RPC?
RPC全称为remote procedure call,即远程过程调用。
比如两台服务器A和B,A服务器上部署一个应用,B服务器上部署一个应用,A服务器上的应用想调用B服务器上的应用提供的方法,由于两个应用不在一个内存空间,不能直接调用,所以需要通过网络来表达调用的语义和传达调用的数据。
需要注意的是RPC并不是一个具体的技术,而是指整个网络远程调用过程。
RPC是一个泛化的概念,严格来说一切远程过程调用手段都属于RPC范畴。各种开发语言都有自己的RPC框架。Java中的RPC框架比较多,广泛使用的有RMI、Hessian、Dubbo等。
Dubbo官网地址:dubbo.apache.org
Dubbo提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
本章节我们会先学习dubbo的远程调用技术。负载均衡和容错后续项目2继续学习!
Dubbo高性能的底层实现技术:
性能提升NIO
- 标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中
- Java NIO可以让你异步的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。
- Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。
Netty框架
Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。
Dubbo基于 Netty框架开发的高性能分布式RPC框架
2.2 Dubbo架构
Dubbo 框架
Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:
-
面向接口的远程方法调用,
-
智能容错和负载均衡
-
服务自动注册和发现
Dubbo框架的通信层 - 利用Netty的实现过程
Dubbo架构图(Dubbo官方提供)如下:
节点角色说明:
| 节点 | 角色名称 |
|---|---|
| Provider | 服务提供方 - 服务的提供者 类似 商家 |
| Consumer | 服务消费方 - 使用服务的一方 类似买家 |
| Registry | 服务注册与发现的注册中心 - 类似京东 |
| Monitor | 统计服务的调用次数和调用时间的监控中心 - 类似京东后台 |
| Container | 服务运行容器 |
虚线都是异步访问,实线都是同步访问 蓝色虚线:在启动时完成的功能 红色虚线(实线)都是程序运行过程中执行的功能
调用关系说明:
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
3. Dubbo快速入门开发(了解)
参照官网文档: API代码可以从官方直接拷贝
3.1 基于API接口的快速入门案例
目标:理解rpc调用以及注册中心
实现步骤说明
1.服务提供方独立jvm运行,暴露服务接口
开发步骤:
a.创建应用配置
b.创建协议配置
c.服务接口暴露
2.服务消费方找到服务接口,并进行调用
开发步骤:
a.创建应用配置
b.引用调用接口
打开下发资料,使用idea工具打开
模块说明:
- father 父模块
- api 服务接口模块
- consumer 服务消费模块
- provider 服务提供者模块
father模块:
father:pom引入maven依赖:
说明: 初始案例采用服务器直连数据传输,没有注册中心,先不导入zookeeper依赖
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.5</version>
</dependency>
</dependencies>
子模块:api模块
新建接口:HelloService
public interface HelloService {
public String sayHello(String hello);
}
子模块:provider
pom 依赖通用服务接口模块 api
<artifactId>provider</artifactId>
<dependencies>
<dependency>
<groupId>cn.itcast.dubbo</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
新建接口实现类:
public class HelloServiceImpl implements HelloService {
public String sayHello(String hello) {
return "provider :"+hello;
}
}
导入日志文件: resources目录下
3.1服务提供方开发
创建一个服务启动类: 根据官方文档 api 代码示例: 直接拷贝
package cn.itcast.provider;
import cn.itcast.service.HelloService;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;
import java.io.IOException;
public class App {
public static void main(String[] args) throws Exception {
// 服务实现
HelloService helloService= new HelloServiceImpl();
// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("provider-dubbo");
// 连接注册中心配置 - 无配置中心 点对点连接
RegistryConfig registry = new RegistryConfig(RegistryConfig.NO_AVAILABLE);
// 服务提供者协议配置
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName("dubbo");
protocol.setPort(20880);
// 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口
// 服务提供者暴露服务配置
ServiceConfig<HelloService> service = new ServiceConfig<HelloService>(); // 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏
service.setApplication(application);
service.setRegistry(registry); // 多个注册中心可以用setRegistries()
service.setProtocol(protocol); // 多个协议可以用setProtocols()
service.setInterface(HelloService.class);
service.setRef(helloService);
service.setVersion("1.0.0");
// 暴露及注册服务
service.export();
// 模拟服务启动-- 当前线程阻塞
System.in.read();
}
}
log4j.properties日志打印:
#############
# 输出到控制台
#############
log4j.rootLogger=INFO,CONSOLE,logfile
# 配置CONSOLE输出到控制台
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
# 配置CONSOLE设置为自定义布局模式
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
# 配置CONSOLE日志的输出格式 [frame] 2019-08-22 22:52:12,000 %r耗费毫秒数 %p日志的优先级 %t线程名 %C所属类名通常为全类名 %L代码中的行号 %x线程相关联的NDC %m日志 %n换行
log4j.appender.CONSOLE.layout.ConversionPattern=[frame] %d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n
运行main方法:
暴露的url地址为:dubbo://192.168.148.1:20880/com.itheima.dubbo.HelloService?anyhost=true&application=app-provider&bind.ip=192.168.148.1&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.itheima.dubbo.HelloService&methods=sayHello&pid=7716&release=2.7.6&side=provider×tamp=1597373499709
3.2服务消费方开发
pom
依赖服务接口模块 api
<dependencies>
<dependency>
<groupId>cn.itcast.dubbo</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
新建消费方测试类:
package cn.itcast.consumer;
import cn.itcast.service.HelloService;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
public class App {
public static void main(String[] args) {
// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("consumer-dubbo");
// 连接注册中心配置 - 不适用注册中心
RegistryConfig registry = new RegistryConfig(RegistryConfig.NO_AVAILABLE);
// 注意:ReferenceConfig为重对象,内部封装了与注册中心的连接,以及与服务提供方的连接
// 引用远程服务
ReferenceConfig<HelloService> reference = new ReferenceConfig<HelloService>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
reference.setApplication(application);
reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
reference.setInterface(HelloService.class);
reference.setVersion("1.0.0");
// 如果点对点直连,可以用reference.setUrl()指定目标地址,设置url后将绕过注册中心,
// 将服务端日志发布的url地址拷贝到setUrl方法参数中
reference.setUrl("dubbo://192.168.3.12:20880/cn.itcast.service.HelloService");
// 和本地bean一样使用xxxService
HelloService helloService = reference.get();
// 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用
String heima = helloService.sayHello("heima");
System.out.println(heima+"===============");
}
}
调用结果:
3.3开发流程小结
3.3.1 RPC基本概念
RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务。
两个独立进程间的相互调用, 解决:服务和服务之间的数据传输问题!rpc调用网络通讯部分根据协议不同,采用的数据传输报文不同。
远程调用过程:
- RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务
通俗的描述是:客户端调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样
3.3.2api接口核心类
| 类名 | 描述 |
|---|---|
| ApplicationConfig | 应用配置 |
| ProtocolConfig | 协议配置 |
| RegistryConfig | 注册中心配置 |
| ServiceConfig | 服务暴露配置,通过方法export暴露服务接口 |
| ReferenceConfig | 引用调用配置,通过get方法获取对应的代理对象,调用接口就和本地方法调用一样 |
3.3.3 思考
问:消费端直接就使用接口调用,没有任何的实现类去实现可以调用成功吗?
- 答:java程序中如果遇到直接使用接口调用,除了有具体实现类外,还可以通过动态代理来实现。那么在dubbo中就是采用了动态代理机制,源码跟踪:ReferenceConfig.get() --> init() ---> ref = createProxy(map);
如下图:
消费端通过生成代理类: 将消费端的配置参数封装到map中,通过底层netty 发送给服务端,
当调用服务接口方法时,获取服务端回送的数据!
问:刚才的案例中我们在客户端通过硬编码的方式指定url地址,如果我们暴露的服务有多个,同时线上运行的服务提供者有多个,那么我们这种在代码里面写死url地址会引来一些问题,
如:1.服务提供者可能新增,也可能删除,2.服务端提供的接口不可能这么简单,线上环境是批量的多个接口,那么我们如果要每一个接口引用都去写死地址那开发效率就很低了。有没有什么好的解决方案吗?
- 答:引入注册中心来对我们的服务进行统一管理维护,让消费端自己主动发现服务。
3.4 使用zookeeper注册中心
我们接着在项目的maven 父工程的配置文件pom.xml中引入zkclient依赖:
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
3.4.1 服务提供方
我们前面在注册中心配置属性设置的是不使用注册中心,接下来我们使用zookeeper作为我们的服务注册中心,配置如:
RegistryConfig registryConfig = new RegistryConfig("zookeeper://127.0.0.1:2181");
修改后的完整代码:
package cn.itcast.provider;
import cn.itcast.service.HelloService;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;
import java.io.IOException;
public class ServiceApp {
public static void main(String[] args) throws Exception {
// 服务实现
HelloService helloService= new HelloServiceImpl();
// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("provider-dubbo");
// 连接注册中心配置
// RegistryConfig registry = new RegistryConfig(RegistryConfig.NO_AVAILABLE);
RegistryConfig registry = new RegistryConfig("zookeeper://127.0.0.1:2181");
// 服务提供者协议配置
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName("dubbo");
protocol.setPort(20880);
// 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口
// 服务提供者暴露服务配置
ServiceConfig<HelloService> service = new ServiceConfig<HelloService>(); // 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏
service.setApplication(application);
service.setRegistry(registry); // 多个注册中心可以用setRegistries()
service.setProtocol(protocol); // 多个协议可以用setProtocols()
service.setInterface(HelloService.class);
service.setRef(helloService);
service.setVersion("1.0.0");
// 暴露及注册服务
service.export();
// 模拟服务启动-- 当前线程阻塞
System.in.read();
}
}
dubbo发布服务源码说明:
扩展内容: 关于dubbo服务注册zookeeper源码出处:
- 从 service.export(); 方法跟踪:
找到实现者: ListenerRegistryWrapper.java 实现 register方法,继续跟进
找到发布服务具体实现: FailbackRegistry接口的 具体实现者完成服务发布!
断点跟踪: ZookeeperRegistry 调用 ZkClient完成节点的创建!
其中:
create 方法 创建临时节点
toUrlPath(url) 对地址进行编码 特殊字符处理
URLEncoder.encode(url, "UTF-8");// 自动对特殊字符进行编码转义
url.getParameter() -- true 表示创建:临时节点
3.4.2 服务消费方
package cn.itcast.consumer;
import cn.itcast.service.HelloService;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import java.io.IOException;
public class ConsumerApp {
public static void main(String[] args) throws IOException {
// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("consumer-dubbo");
// 连接注册中心配置 - 不适用注册中心
// RegistryConfig registry = new RegistryConfig(RegistryConfig.NO_AVAILABLE);
RegistryConfig registry = new RegistryConfig("zookeeper://127.0.0.1:2181");
// 注意:ReferenceConfig为重对象,内部封装了与注册中心的连接,以及与服务提供方的连接
// 引用远程服务
ReferenceConfig<HelloService> reference = new ReferenceConfig<HelloService>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
reference.setApplication(application);
reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
reference.setInterface(HelloService.class);
reference.setVersion("1.0.0");
// 如果点对点直连,可以用reference.setUrl()指定目标地址,设置url后将绕过注册中心,
// 将服务端日志发布的url地址拷贝到setUrl方法参数中
// reference.setUrl("dubbo://192.168.3.12:20880/cn.itcast.service.HelloService");
// 和本地bean一样使用xxxService
HelloService helloService = reference.get(); // 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用
String heima = helloService.sayHello("heima");
System.out.println(heima+"===============");
System.in.read();
}
}
扩展内容: 服务消费:从注册中心获取服务地址
源码: doInvoke() 方法处添加断点:
DubboInvoker 作用:获取zookepper中心发布的服务节点并且调用底层对象完成数据传输!
在该构造方法中,参数URL中包含了服务端节点数据,
追踪。。。。是由ZookeeperRegister对象doSubscribe方法,获取zookeeper节点数据!
ZookeeperRegister.java找到 doSubscribe方法 中:--- 对象 中: 获取 服务端发布的服务节点信息:
源码: 191行处: 获取zookeeper中心服务端临时节点:
193行代码: 将服务端的地址 特殊字符进行转换
toUrlsWithoutEmpty方法: 将获取服务节点地址进行地址解析!
至此: 消费端源码解析完成!
接下来我们只需要测试 服务端和消费端数据交互即可!
3.4.3 启动zookeeper
先后重新启动 生产者和消费者
在客户端 zkCli.cmd 端 执行查询命令:
ls /dubbo -R 发现生产者和消费已经注册到zookeeper中心上
3.5 dubbo官方架构图(扩展)
如果想深入学习dubbo,可以参照dubbo官方框架流程图:
图例说明
- 图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。
- 图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。
- 图中绿色小块的为扩展接口,蓝色小块为实现类,图中只显示用于关联各层的实现类。
- 图中蓝色虚线为初始化过程,即启动时组装链,红色实线为方法调用过程,即运行时调时链,紫色三角箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用的方法。
各层说明
- config 配置层:对外配置接口,以
ServiceConfig,ReferenceConfig为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类 - proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以
ServiceProxy为中心,扩展接口为ProxyFactory - registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为
RegistryFactory,Registry,RegistryService - cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以
Invoker为中心,扩展接口为Cluster,Directory,Router,LoadBalance - monitor 监控层:RPC 调用次数和调用时间监控,以
Statistics为中心,扩展接口为MonitorFactory,Monitor,MonitorService - protocol 远程调用层:封装 RPC 调用,以
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
3.6 阶段小结
-
dubbo和zookeeper各自的作用:
- dubbo底层通过rpc实现系统之间的数据通信
- zookeeper实现服务端和消费端节点的存储