前段时间因为用到了Dubbo的接口,所以学习了一下Zookeeper+Dubbo的RPC调用方式,总结了一篇Blog【SpringBoot学习笔记 十四】SpringBoot+Dubbo+Zookeeper集成开发,之前写这篇Blog的时候用的还是Dubbo2.7 ,今天就来学习下Dubbo的最新版本Dubbo3 ,实践部分还是基于Dubbo2.7 。
- 是什么:Dubbo是什么,基本概念是什么
- 为什么:为什么选择用Dubbo3,在什么场景下用Dubbo3比较好,相比Dubbo2.7的优势是什么
- 怎么用:基本的使用方法,怎么使用
- 底层原理:是什么样的底层原理和源码支撑Dubbo3的功能
依然按照这样的线路来进行一个深度理解,由于Dubbo整体架构比较复杂,这篇Blog仅从基本概念和使用层面上进行了解。本文部分内容参考自:APACHE DUBBO
Dubbo基本概念
Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
服务发现架构
服务发现,即消费端自动发现服务地址列表的能力,是微服务框架需要具备的关键能力,借助于自动化的服务发现,微服务之间可以在无需感知对端部署位置与 IP 地址的情况下实现通信
实现服务发现的方式有很多种,Dubbo 提供的是一种 Client-Based 的服务发现机制,通常还需要部署额外的第三方注册中心组件来协调服务发现过程,如常用的 Nacos、Consul、Zookeeper 等,Dubbo 自身也提供了对多种注册中心组件的对接,用户可以灵活选择 Dubbo的五种角色说明如下:
- 服务容器(Container):负责启动,加载,运行服务生产者。
- 服务生产者(Provider):服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
- 监控中心(Monitor):在内存中统计累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
调用关系说明如下:
- 服务容器负责启动,加载,运行服务提供者。服务生产者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。Dubbo一般使用zookeeper作为注册中心
- 服务消费者从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 监控中心在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
需要特别关注:服务发现的一个核心组件是注册中心,Provider 注册地址到注册中心,Consumer 从注册中心读取和订阅 Provider 地址列表。 因此,要启用服务发现,需要为 Dubbo 增加注册中心配置
RPC通信协议
Dubbo3 提供了 Triple(Dubbo3)、Dubbo2 协议,这是 Dubbo 框架的原生协议。除此之外,Dubbo3 也对众多第三方协议进行了集成,并将它们纳入 Dubbo 的编程与服务治理体系, 包括 gRPC、Thrift、JsonRPC、Hessian2、REST 等。
RPC协议范式
协议是 RPC 的核心,它规范了数据在网络中的传输内容和格式。除必须的请求、响应数据外,通常还会包含额外控制数据,如单次请求的序列化方式、超时时间、压缩方式和鉴权信息等,协议的内容包含三部分
- 数据交换格式: 定义 RPC 的请求和响应对象在网络传输中的字节流内容,也叫作序列化方式
- 协议结构: 定义包含字段列表和各字段语义以及不同字段的排列方式
- 协议通过定义规则、格式和语义来约定数据如何在网络间传输。一次成功的 RPC 需要通信的两端都能够按照协议约定进行网络字节流的读写和对象转换。如果两端对使用的协议不能达成一致,就会出现鸡同鸭讲,无法满足远程通信的需求
其实对应于之前提到的RPC的分层模型:序列化层,代理层以及网络层,RPC 协议的设计需要考虑以下内容:
- 通用性: 统一的二进制格式,跨语言、跨平台、多传输层协议支持
- 扩展性: 协议增加字段、升级、支持用户扩展和附加业务元数据
- 性能:As fast as it can be
- 穿透性:能够被各种终端设备识别和转发:网关、代理服务器等
通用性和高性能通常无法同时达到,需要协议设计者进行一定的取舍,这和HTTP和RPC分别对通用及高性能做取舍是一样的。
Triple协议
Triple协议是兼容gRPC,构建于HTTP2.0之上的协议,解决了如下问题:
- 性能上: Triple 协议采取了 metadata 和 payload 分离的策略,这样就可以避免中间设备,如网关进行 payload 的解析和反序列化,从而降低响应时间。
- 路由支持上,由于 metadata 支持用户添加自定义 header ,用户可以根据 header 更方便的划分集群或者进行路由,这样发布的时候切流灰度或容灾都有了更高的灵活性。
- 安全性上,支持双向TLS认证(mTLS)等加密传输能力。
- 易用性上,Triple 除了支持原生 gRPC 所推荐的 Protobuf 序列化外,使用通用的方式支持了 Hessian / JSON 等其他序列化,能让用户更方便的升级到 Triple 协议。对原有的 Dubbo 服务而言,修改或增加 Triple 协议 只需要在声明服务的代码块添加一行协议配置即可,改造成本几乎为 0。
有如下的功能和特点:
- 具备跨语言互通的能力,传统的多语言多 SDK 模式和 Mesh 化跨语言模式都需要一种更通用易扩展的数据传输格式。
- 提供更完善的请求模型,除了 Request/Response 模型,还应该支持 Streaming 和 Bidirectional。
- 易扩展、穿透性高,包括但不限于 Tracing / Monitoring 等支持,也应该能被各层设备识别,网关设施等可以识别数据报文,对 Service Mesh 部署友好,降低用户理解难度。
- 多种序列化方式支持、平滑升级
- 支持 Java 用户无感知升级,不需要定义繁琐的 IDL 文件,仅需要简单的修改协议名便可以轻松升级到 Triple 协议
主要好处是Java可以无感升级,既能多语言又能插件式适配。
SpringBoot整合Dubbo&Zookeeper环境搭建
我们按照如下的步骤进行环境搭建和做必要的准备工作:
1 安装zookeeper
首先我们需要下载一个zookeeper,下载地址为:zookeeper下载地址,这里我们选择现在(20211024)的最新版本3.6.3进行下载。 下载好后为了防止闪退,运行/bin/zkServer.cmd ,初次运行会报闪退,编辑zkServer.cmd 文件末尾添加pause 。这样运行出错就不会退出,会提示错误信息,方便找到原因 初次运行会报错,没有zoo.cfg配置文件,将conf文件夹下面的zoo_sample.cfg复制一份改名为zoo.cfg即可:
zoo.cfg
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/tmp/zookeeper
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
## Metrics Providers
#
# https://prometheus.io Metrics Exporter
#metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider
#metricsProvider.httpPort=7000
#metricsProvider.exportJvmInfo=true
正常运行后Zookeeper启动: 可以做一个简单测试,创建一个KV键值对:
2 安装dubbo-admin
dubbo本身并不是一个服务软件。它其实就是一个jar包,能够帮你的java程序连接到zookeeper,并利用zookeeper消费、提供服务。但是为了让用户更好的管理监控众多的dubbo服务,官方提供了一个可视化的监控程序dubbo-admin,不过这个监控即使不装也不影响使用,我们从这里下载:dubbo-admin下载地址,同样也下载最新版 为了避免端口冲突,修改配置文件如下:
server.port=7001
admin.registry.address=zookeeper://127.0.0.1:2181
admin.config-center=zookeeper://127.0.0.1:2181
admin.metadata-report.address=zookeeper://127.0.0.1:2181
admin.root.user.name=root
admin.root.user.password=root
admin.check.sessionTimeoutMilli=3600000
server.compression.enabled=true
server.compression.mime-types=text/css,text/javascript,application/javascript
server.compression.min-response-size=10240
修改好后按照步骤执行命令 执行命令:mvn clean package -Dmaven.test.skip=true ,可能会遇到标签报错问题,调整下即可
然后在控制中心执行如下命令:
cd dubbo-admin-distribution/target
java -jar dubbo-admin-0.3.0.jar
命令执行后dubbo服务启动,我们再将zookeeper服务启动后注册中心才可用: 我们访问地址http://localhost:7001/ ,结果如下: 输入配置文件中的账号密码即可登录:
3 创建服务提供者模块
首先我们创建一个服务提供者,也就是服务生产者模块。 选择如下依赖即可:
1 pom.xml依赖
在pom.xml文件中配置坐标如下:
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-common</artifactId>
<version>2.7.12</version>
</dependency>
</dependencies>
2 生产者Dubbo配置
在配置文件中进行Dubbo的配置
application.yml
server:
port: 8081
dubbo:
application:
name: provider-server
scan:
base-packages: com.example.providerserver.service
registry:
address: 127.0.0.1:2181
protocol: zookeeper
protocol:
name: dubbo
port: 20880
3 接口服务代码配置
BookService
package com.example.providerserver.service;
public interface BookService {
String getBookByUserId(Integer id);
}
BookServiceImpl
package com.example.providerserver.service;
import org.springframework.stereotype.Component;
import org.apache.dubbo.config.annotation.Service;
@Service
@Component
public class BookServiceImpl implements BookService {
@Override
public String getBookByUserId(Integer id) {
if(id==1){
return "tml很帅,他喜欢《围城》";
}
return "查无此人";
}
}
创建完成后整体布局如下:
4 创建服务消费者模块
首先我们创建一个服务消费者模块:
选择如下依赖即可:
1 pom.xml
在pom.xml中配置坐标如下,和生产者的依赖一样:
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>provider-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2 消费者Dubbo配置
在配置文件中进行Dubbo的配置
server:
port: 8082
dubbo:
application:
name: consumer-server
registry:
address: 127.0.0.1:2181
protocol: zookeeper
protocol:
name: dubbo
port: 20880
3 消费者接口代码配置
本来正常步骤是需要将服务提供者的接口打包,然后用pom文件导入,我们这里使用简单的方式,直接将服务的接口拿过来,路径必须保证正确,即和服务提供者相同
UserInfoService
package com.example.consumerserver.service;
public interface UserInfoService {
String getBook();
}
UserInfoServiceImpl
package com.example.consumerserver.service;
import com.example.providerserver.service.BookService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
@Service
public class UserInfoServiceImpl implements UserInfoService {
@Reference
BookService bookService;
@Override
public String getBook() {
String bookName = bookService.getBookByUserId(1);
System.out.println("在注册中心买到"+bookName);
return bookName;
}
}
UserController
package com.example.consumerserver.controller;
import com.example.consumerserver.service.UserInfoService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class UserController {
@Resource
UserInfoService userInfoService;
@GetMapping("/userinfo")
public String userinfo(){
String book= userInfoService.getBook();
return book;
}
}
创建完成后整体布局如下:
5 测试Dubbo远程RPC调用
按照如下步骤进行测试,首先我们整体看下模块划分如下:整体模块划分如下
1 开启zookeeper
直接双击zkServer.cmd 即可
2 打开dubbo-admin实现监控
我们先启动监控服务: 然后访问网址:http://localhost:7001/
3 启动服务者
然后我们启动服务者:
4 启动消费者
启动完服务者后,我们再启动下消费者:
5 页面请求测试
我们访问消费者的Controller,间接的调用远程服务: 同时观察dubbo-admin也体现出了注册信息: 服务关系:
总结一下
Dubbo3框架在21年3月发布,依旧保持持续更新的势头,写这篇Blog的时候应用的还是Dubbo2.7,并且在一些高级控制:例如流量分发、配置以及架构等层面均没有深入,核心原因还是感觉无处着手,因为还是没有在工作实践中倒逼自己去学,用产品的话说也就是不痛吧。所以就没有新瓶装旧酒,暂时准备把注意力转移到devOps上,包括Docker这些云原生组件,感觉对这部分的了解太少了,痛点较为集中,学习完获得感也更强吧!
|