🪁🍁 希望本文能给您带来帮助,如果有任何问题,欢迎批评指正!🐅🐾🍁🐥
文章目录
- 一、背景
- 二、Dubbo概述
- 三、Dubbo与SpringCloud的关系
- 四、Dubbo技术架构
- 五、Docker安装Zookeeper
- 六、Docker安装Dubbo Admin
- 七、SpringBoot项目整合Dubbo
- 7.1 创建生产者服务
- 7.1.1 项目路径总览
- 7.1.2 pom.xml依赖引入
- 7.1.2.1 生产者父pom依赖
- 7.1.2.2 api 模块pom依赖
- 7.1.2.3 center 模块pom依赖
- 7.1.3 不同配置实现方式
- 7.1.3.1 xml配置
- 7.1.3.2 注解配置
- 7.2 创建消费者服务
- 7.2.1 项目路径总览
- 7.2.2 pom.xml依赖引入
- 7.2.3 不同配置实现方式
- 7.2.3.1 xml配置
- 7.2.3.2 注解配置
- 7.3 测试验证
- 7.3.1 云服务器中zookeeper客户端验证
- 7.3.2 Dubbo Admin控制台验证
- 八、其他高级特性
- 8.1 启动时检查
- 8.2 集群容错
- 8.3 负载均衡
- 8.4 直连提供者
- 8.5 多协议机制
- 8.5.1 多端口多协议
- 8.5.2 单端口多协议
- 8.6 多注册中心
- 8.7 多版本
- 8.8 日志管理
- 九、总结
一、背景
Dubbo作为阿里巴巴开源的微服务框架,提供了高性能的RPC调用。同时因为有阿里的背书,在国内市场得到了广泛应用,Dubbo的开源工作在2018年2月阿里将项目捐献给apache基金会后,得到了更加广大的发展。而最近因为在实际工作中有需要使用到Dubbo,故而专门开一篇专栏去记录并总结一下Dubbo相关的知识,在此,希望专栏里的内容能够帮到您。
二、Dubbo概述
Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力, 利用 Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户可以方便的实现流量拦截、选址的各种定制逻辑。
按照微服务架构的定义,采用它的组织能够很好的提高业务迭代效率与系统稳定性,但前提是要先能保证微服务按照期望的方式运行,要做到这一点需要解决服务拆分与定义、数据通信、地址发现、流量管理、数据一致性、系统容错能力等一系列问题。
Dubbo 可以帮助解决如下微服务实践问题:
微服务编程范式和工具
Dubbo 支持基于 IDL 或语言特定方式的服务定义,提供多种形式的服务调用形式(如同步、异步、流式等)
高性能的 RPC 通信
Dubbo 帮助解决微服务组件之间的通信问题,提供了基于 HTTP、HTTP/2、TCP 等的多种高性能通信协议实现,并支持序列化协议扩展,在实现上解决网络连接管理、数据传输等基础问题。
微服务监控与治理
Dubbo 官方提供的服务发现、动态配置、负载均衡、流量路由等基础组件可以很好的帮助解决微服务基础实践的问题。除此之外,您还可以用 Admin 控制台监控微服务状态,通过周边生态完成限流降级、数据一致性、链路追踪等能力。
部署在多种环境
Dubbo 服务可以直接部署在容器、Kubernetes、Service Mesh等多种架构下。
三、Dubbo与SpringCloud的关系
Dubbo 和 Spring Cloud 有很多相似之处,它们都在整个架构图的相同位置并提供一些相似的功能。
相同点:
- Dubbo 和 Spring Cloud 都侧重在对分布式系统中常见问题模式的抽象(如服务发现、负载均衡、动态配置等),同时对每一个问题都提供了配套组件实现,形成了一套微服务整体解决方案,让使用 Dubbo 及 Spring Cloud 的用户在开发微服务应用时可以专注在业务逻辑开发上。
- Dubbo 和 Spring Cloud 都完全兼容 Spring 体系的应用开发模式,Dubbo 对 Spring 应用开发框架、Spring Boot 微服务框架都做了很好的适配,由于 Spring Cloud 出自 Spring 体系,在这一点上自然更不必多说。
不同点:
Dubbo
使用的是RPC
通信( 支持HTTP、HTTP/2(Triple、gRPC)、TCP 多种协议),而Spring Cloud
使用的是HTTP RESTFul
方式。
四、Dubbo技术架构
第一节已经介绍了Dubbo的功能非常强大,但是本文主要介绍Dubbo的两大主要核心特性:服务发现
与远程调用
, 而从上图架构图能发现其实现原理非常明了,它其实很像生产者-消费者模型。只是在这种模型上,加上了注册中心和监控中心,用于管理提供方提供的url,以及管理整个过程。我们一起来看一下Dubbo官网提供的架构图:
节点角色说明
节点 | 角色说明 |
---|---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器,负责启动、加载、运行服务提供者 |
调用关系说明
1、服务容器负责启动,加载,运行服务提供者。
2、服务提供者在启动时,向注册中心注册自己提供的服务。
3、服务消费者在启动时,向注册中心订阅自己所需的服务。
4、注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
5、服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
6、服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
五、Docker安装Zookeeper
在本地测试时,我们可以在代码中使用ip直连来进行Dubbo接口的调用,但在工作中,实际微服务开发时,我们更多的是会使用注册中心来进行Dubbo接口的调用,而注册中心有多种选择:Nacos、Zookeeper等,本文选择的是Dubbo+zookeeper
的组合。而为了方便使用,本文选择使用Dokcer来部署运行Zookeeper,下文就是使用Docker安装Zookeeper的具体细节步骤。
- 查看本地镜像和检索拉取Zookeeper 镜像
# 查看本地镜像
docker images
# 检索ZooKeeper 镜像
docker search zookeeper
# 拉取ZooKeeper镜像最新版本
docker pull zookeeper:latest
# 我使用的版本
docker pull zookeeper:3.5.7
- 启动ZooKeeper容器
docker run -d --name mynewzookeeper --privileged=true -p 2181:2181 -v /mydata/zookeeper/data:/data -v /mydata/zookeeper/conf:/conf -v /mydata/zookeeper/logs:/datalog zookeeper:3.5.7
运行MySQL时参数说明:
docker run:
在docker中启动一个容器实例。
-d:
该容器在后台运行。
--name mynewzookeeper:
给容器指定一个名字叫做 mynewzookeeper。这可以帮助你更容易地识别和管理这个容器。
-p 2181:2181:
这是一个端口映射选项,它将宿主机的 2181 端口映射到容器内的 2181 端口。这意味着在宿主机上,你可以通过访问 localhost:2181 来连接到运行在容器内的 ZooKeeper 服务。
-v /mydata/zookeeper/data:/data:
将容器/data目录下的数据,备份到主机的/mydata/zookeeper/data目录下。
-v /mydata/zookeeper/conf:/conf:
将容器/conf目录下的数据,备份到主机的 /mydata/zookeeper/conf目录下。
-v /mydata/zookeeper/logs:/datalog:
将容器/datalog目录下的数据,备份到主机的 /mydata/zookeeper/logs目录下。
zookeeper:3.5.7:
需要运行的容器名称以及版本号。
- 验证 Zookeeper 是否启动成功
docker ps
- 添加ZooKeeper配置文件,在挂载配置文件目录(/mydata/zookeeper/conf)下,新增zoo.cfg 配置文件,配置内容如下:
dataDir=/data # 保存zookeeper中的数据
clientPort=2181 # 客户端连接端口,通常不做修改
dataLogDir=/datalog
tickTime=2000 # 通信心跳时间
initLimit=5 # LF(leader - follower)初始通信时限
syncLimit=2 # LF 同步通信时限
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
maxClientCnxns=60
standaloneEnabled=true
admin.enableServer=true
server.1=localhost:2888:3888;2181
- 进入容器内部,验证容器状态
# 方式一:这样的话,直接登录到容器时,进入到 zkCli中
docker run -it --rm --link zookeeper:zookeeper zookeeper zkCli.sh -server zookeeper# 方式二:先进入容器,再进入到 zkCli中
docker exec -it zookeeper bash // 只登录容器,不登录 zkCli
./bin/zkCli.sh // 执行脚本新建一个Client,即进入容器
六、Docker安装Dubbo Admin
我们在搭建Dubbo框架时,需要安装一个可视化工具——Dubbo Admin来管理服务。Dubbo Admin 管理平台,是一个前后端分离的项目,前端技术使用vue,后端技术使用springboot。Dubbo Admin会从注册中心中获取到所有的提供者 / 消费者进行配置管理,它具有路由规则、动态配置、服务降级、访问控制、权重调整、负载均衡等管理功能。
- 首先到dockerhub上搜索dubbo-admin的镜像
2. 可以看到两个引用较高的镜像源,第一个是apache官方的,我们知道Dubbo是阿里开发的,但是现在已经捐献给apache,第二个镜像源就是Dubbo阿里原作者提供的,这里因为第一个更新更及时,我们选择第一个镜像源。
# 拉取dubbo-admin的镜像
docker pull apache/dubbo-admin
- 因为dubbo-admin要连接zookeeper,这里我两者都是使用docker安装的,为了保证他们能够通过名字久能正常通信,要将其连接到同一个网络下
首先建立一个桥接网络zk
docker network create -d bridge zk
- 因为上一节已经运行了zookeeper容器,这里直接将zookeeper加入到网络为zk中
# 将容器加入到网络中
docker network connect [网络] [容器]
- 查看zookeeper容器ip
docker network inspect zk
- 创建dubbo-admin容器,指定网络为zk,并设置zookeeper为上述查询出来的地址
# 运行dubbo-admin容器
docker run -d --name dubbo-admin --network zk -p 8083:38080 -e admin.registry.address=zookeeper://172.18.0.2:2181 -e admin.config-center=zookeeper://172.18.0.2:2181 -e admin.metadata-report.address=zookeeper://172.18.0.2:2181 apache/dubbo-admin# 验证容器是否正常运行
dokcer ps
- 因为我已经把端口映射为8083了,所以直接访问ip+port,输入账号密码登录即可,初始密码默认为root/root。
登陆跳转这个页面,说明安装成功。
在安装过程中,我们很有可能会遇到各种问题,这里作者总结几点安装过程中踩坑的点,大家需要注意一下。
注意1:
大家拉取不同的镜像时,docker运行dubbo-admin容器情况是不一样的,建议直接拉取apache的,以免容器运行后直接退出,不方便排查问题.。
注意2:
若容器正常运行,建议利用docker logs 容器id命令去看一下日志,确认一下这个dubbo-admin里的tomcat容器监听的端口号。
注意3:
使用浏览器访问时,若访问不通,请确认防火墙中对应端口是否放开,请确认云服务器控制台中的安全组里的端口是否放开。
七、SpringBoot项目整合Dubbo
7.1 创建生产者服务
dubbo-provider代表服务生产者,首先,我们先把服务端的接口写好,因为其实dubbo的作用简单来说就是给消费端提供接口。
7.1.1 项目路径总览
项目结构:
- api 模块——接口、实体等定义
- center 模块——真正的服务提供者
注意:
api模块和center模块是最基本的模块,api模块就是服务暴露出去的dubbo接口的契约jar包,就是一个SDK包,它通常由接口定义和相应的实体类组成;而center模块是api模块中定义的接口的真正的实现逻辑,除此之外,我们在实际开发中可能还有其他模块,比如common模块用来提供公共的通用的类,比如web模块用来提供前端可以调用的restful接口。
7.1.2 pom.xml依赖引入
7.1.2.1 生产者父pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.wasteland.blogsourcecode</groupId><artifactId>dubbo-provider</artifactId><version>0.0.1-SNAPSHOT</version><name>dubbo-provider</name><description>dubbo-provider</description><!--打包方式:pom--><packaging>pom</packaging><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.4.2</spring-boot.version></properties><!--子模块引用--><modules><module>dubbo-provider-api</module><module>dubbo-provider-center</module></modules><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>com.wasteland.blogsourcecode.DubboProviderApplication</mainClass><skip>true</skip></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>
7.1.2.2 api 模块pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.wasteland.blogsourcecode</groupId><artifactId>dubbo-provider-api</artifactId><version>0.0.1-SNAPSHOT</version><name>dubbo-provider-api</name><description>dubbo-provider-api</description><!--打包方式:jar--><packaging>jar</packaging><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.4.2</spring-boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version></plugin></plugins></build></project>
7.1.2.3 center 模块pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.wasteland.blogsourcecode</groupId><artifactId>dubbo-provider-center</artifactId><version>0.0.1-SNAPSHOT</version><name>dubbo-provider-center</name><description>dubbo-provider-center</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.4.2</spring-boot.version></properties><dependencies><!-- dubbo provider 启动不成功的主要问题在这里,没有添加 spring-boot-starter-web 依赖,所以启动日志里一直没有显示“o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8087 (http)” 这行日志输出 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 这个是定义的接口包,在 provider 和 consumer 都需要引用的 --><dependency><groupId>com.wasteland.blogsourcecode</groupId><artifactId>dubbo-provider-api</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-dependencies-zookeeper</artifactId><version>2.7.4.1</version><type>pom</type><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency><!-- dubbo 2.7.x引入--><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.4.1</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>com.wasteland.blogsourcecode.DubboProviderCenterApplication</mainClass><skip>true</skip></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>
7.1.3 不同配置实现方式
我们在把Dubbo接口注册到注册中心时,有很多种不同的使用方式,这里就只介绍两种常用的方式:xml配置
和注解配置
两种。而在使用不同的配置进行Dubbo接口注册时,服务定义阶段(即定义SDK包)都是一样的,api模块是通用的,它不仅会被自己服务所使用,它也会被外部依赖方所使用。因此,在编写完api模块后,需要使用mvn install
命令去把api模块的jar包发布到仓库里(自己本地测试就是本地中心仓库,在公司里实战开发时,会需要将其发布到公司的中央仓库里)。
服务定义: 定义一个名为 UserService的标准 Java 接口作为 Dubbo 服务。
创建UserService接口,并创建sayHello方法
package com.wasteland.blogsourcecode.service;/*** @author wasteland* @create 2025-05-06*/
public interface UserService {public String sayHello();
}
7.1.3.1 xml配置
@ImportResource注解和XML配置文件暴露服务: 使用@ImportResource注解去加载定义在类路径下的xml文件。
服务实现: 定义了服务接口之后,可以在服务端这一侧定义对应的业务逻辑实现,这里实现 UserService 接口。
创建UserServiceImpl实现类,并实现sayHello方法
package com.wasteland.blogsourcecode.dubboprovider.impl;import com.wasteland.blogsourcecode.dubboprovider.UserService;
import org.apache.dubbo.config.annotation.Service;/*** @author wasteland* @create 2025-05-06*/
public class UserServiceImpl implements UserService {@Overridepublic String sayHello() {return "Hello Wasteland!";}
}
package com.wasteland.blogsourcecode;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;@SpringBootApplication
@ImportResource("classpath:/provider.xml")
public class DubboProviderCenterApplication {public static void main(String[] args) {SpringApplication.run(DubboProviderCenterApplication.class, args);}}
xml文件配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"><!--当前项目在整个分布式架构里面的唯一名称,计算依赖关系的标签--><dubbo:application name="dubbo-zookeeper-producer1" owner="wasteland"></dubbo:application><dubbo:monitor protocol="registry"/><!--dubbo这个服务所要暴露的服务地址所对应的注册中心--><!--<dubbo:registry address="N/A"/>--><dubbo:registry address="zookeeper://zookeeper的ip:2181" check="false"/><!--当前服务发布所依赖的协议;webservice、Thrift、Hessain、http--><dubbo:protocol name="dubbo" port="-1"/><!--服务发布的配置,需要暴露的服务接口--><dubbo:serviceinterface="com.wasteland.blogsourcecode.service.UserService"ref="providerService"/><!--Bean bean定义--><bean id="providerService" class="com.wasteland.blogsourcecode.provider.UserServiceImpl"/></beans>
说明:
- 上面的文件其实就是类似 spring 的配置文件,而且,dubbo 底层就是 spring。
- 节点:
dubbo:application
就是整个项目在分布式架构中的唯一名称,可以在name属性中配置,另外还可以配置owner字段,表示属于谁。
下面的参数是可以不配置的,这里配置是因为出现了端口的冲突,所以配置。 - 节点:
dubbo:monitor
监控中心配置, 用于配置连接监控中心相关信息,可以不配置,不是必须的参数。 - 节点:
dubbo:registry
配置注册中心的信息,比如,这里我们可以配置 zookeeper 作为我们的注册中心。address 是注册中心的地址,这里我们配置的是 N/A 表示由 dubbo 自动分配地址。或者说是一种直连的方式,不通过注册中心。 - 节点:
dubbo:protocol
服务发布的时候 dubbo 依赖什么协议,可以配置 dubbo、webservice、http等协议。 - 节点:
dubbo:service
这个节点就是我们的重点了,当我们服务发布的时候,我们就是通过这个配置将我们的服务发布出去的。interface 是接口的包路径,ref 是第 ⑦ 点配置的接口的 bean。 - 最后,我们需要像配置spring的接口一样,配置接口的 bean。
7.1.3.2 注解配置
@EnableDubbo 注解和YML配置文件暴露服务: @EnableDubbo 注解必须配置,否则将无法加载 Dubbo 注解定义的服务。
服务实现: 定义了服务接口之后,可以在服务端这一侧定义对应的业务逻辑实现,这里实现 UserService 接口。
创建UserServiceImpl实现类,并实现sayHello方法
package com.wasteland.blogsourcecode.dubboprovider.impl;import com.wasteland.blogsourcecode.dubboprovider.UserService;
import org.apache.dubbo.config.annotation.Service;/*** 导入dubbo的service注解,将这个类提供的方法对外发布,注册到注册中心中* @author wasteland* @create 2025-05-06*/
@Service
public class UserServiceImpl implements UserService {@Overridepublic String sayHello() {return "Hello Wasteland!";}
}
package com.wasteland.blogsourcecode;import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@EnableDubbo
public class DubboProviderApplication {public static void main(String[] args) {SpringApplication.run(DubboProviderApplication.class, args);}
}
注意:
Dubbo3.x之前是使用的@Service,注意这个@Service注解是dubbo包下的,不是spring包下的,而在Dubbo3.x之后为作区分,注解被更改为@DubboService,下面消费者中使用的@Reference和@Reference也同样如此。Spring Boot 注解默认只会扫描 main 类所在的 package,如果服务定义在其它 package 中,需要增加配置 EnableDubbo(scanBasePackages = {“org.apache.dubbo.springboot.demo.provider”})。
yml文件配置:
dubbo:registry:address: zookeeper://zookeeper的ip:2181 #自己的zookeeper服务器的IP:默认端口号application:name: dubbo-zookeeper-producer1 #注册进去的名字protocol:name: dubbo #设置类型port: -1 #因为dubbo的服务器端口号是不能唯一的,所以,设置为-1会帮我们自动改变端口号config-center:timeout: 120000 #超时时间 (毫秒)server:port: 9001
7.2 创建消费者服务
7.2.1 项目路径总览
在公司实战开发中,因为消费者服务同样也有可能是生产者服务,能够提供dubbo接口给外部调用,因此完整版模块组成和前面的生产者模块是一样的,而这里只是为了测试简单,就只写了一个模块。
7.2.2 pom.xml依赖引入
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.wasteland.blogsourcecode</groupId><artifactId>dubbo-consumer</artifactId><version>0.0.1-SNAPSHOT</version><name>dubbo-consumer</name><description>dubbo-consumer</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.4.2</spring-boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 这个是定义的接口包,在 provider 和 consumer 都需要引用的 --><dependency><groupId>com.wasteland.blogsourcecode</groupId><artifactId>dubbo-provider-api</artifactId><version>0.0.1-SNAPSHOT</version><scope>compile</scope></dependency><!-- dubbo-zookeeper--><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-dependencies-zookeeper</artifactId><version>2.7.4.1</version><type>pom</type><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency><!-- dubbo 2.7.x引入--><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>2.7.4.1</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>com.wasteland.blogsourcecode.DubboConsumerApplication</mainClass><skip>true</skip></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>
7.2.3 不同配置实现方式
7.2.3.1 xml配置
@ImportResource注解和XML配置文件引用远程服务: 使用@ImportResource注解去加载定义在类路径下的xml文件。
服务调用: 由于引入了dubbo接口的SDK包,现在就可以进行远程调用了。
创建UserController控制器类,直接调用UserService里的方法。
package com.wasteland.blogsourcecode.dubboconsumer;import com.wasteland.blogsourcecode.service.UserService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** @author wasteland* @create 2025-05-06*/
@RestController
@RequestMapping("/user")
public class UserController {@Resourceprivate UserService userService;@RequestMapping("/sayHello")public String sayHello() {return userService.sayHello();}
}
package com.wasteland.blogsourcecode;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;@SpringBootApplication
@ImportResource("classpath:/consumer.xml")
public class DubboConsumerApplication {public static void main(String[] args) {SpringApplication.run(DubboConsumerApplication.class, args);}}
xml文件配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"><!--当前项目在整个分布式架构里面的唯一名称,计算依赖关系的标签--><dubbo:application name="dubbo-zookeeper-consumer1" owner="wasteland"/><!--dubbo这个服务所要暴露的服务地址所对应的注册中心--><dubbo:registry address="zookeeper://118.145.205.205:2181" check="false"/><!--生成一个远程服务的调用代理--><dubbo:reference id="providerService"interface="com.wasteland.blogsourcecode.service.UserService"check="false"/>
</beans>
7.2.3.2 注解配置
@EnableDubbo 注解和YML配置文件引用远程服务:@EnableDubbo 注解必须配置,否则将无法加载 Dubbo 注解定义的服务。
服务调用: 由于引入了dubbo接口的SDK包,现在就可以进行远程调用了。
创建UserController控制器类,直接调用UserService里的方法。
package com.wasteland.blogsourcecode.dubboconsumer;import com.wasteland.blogsourcecode.service.UserService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author wasteland* @create 2025-05-06*/
@RestController
@RequestMapping("/user")
public class UserController {@Referenceprivate UserService userService;@RequestMapping("/sayHello")public String sayHello() {return userService.sayHello();}}
package com.wasteland.blogsourcecode;import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@EnableDubbo
public class DubboProviderApplication {public static void main(String[] args) {SpringApplication.run(DubboProviderApplication.class, args);}
}
yml文件配置:
dubbo:registry:address: zookeeper://zookeeper的ip:2181 #自己的zookeeper服务器的IP:默认端口号application:name: dubbo-zookeeper-consumer1 #注册进去的名字protocol:name: dubbo #设置类型port: -1 #因为dubbo的服务器端口号是不能唯一的,所以,设置为-1会帮我们自动改变端口号config-center:timeout: 120000 #超时时间 (毫秒)server:port: 8083
7.3 测试验证
- 先启动zookeeper,因为前面是使用的Docker部署的,直接运行Docker容器即可
- 再启动服务生产者
- 再启动服务消费者
- 最后访问http://localhost:8083/user/sayHello查看效果
7.3.1 云服务器中zookeeper客户端验证
我们可以连接到云服务器进入到zookeeper容器内部中进行验证,命令如下:
# 1.进入容器内部
docker exec -it zookeeper bash // 只登录容器,不登录 zkCli# 2.连接客户端
./bin/zkCli.sh # 3.用以下命令来查询Dubbo服务
ls /dubbo# 4.查看特定服务的消费者
ls /dubbo/<服务名>/consumers# 5.查看特定服务的生产者
ls /dubbo/<服务名>/providers
这里有意思的是它暴露的url
,我们可以来分析一下。因为暴露出的是已经经过URL编码后的,不好分析,我们对其进行一个解码,解码后如下:
Dubbo 暴露的 URL
dubbo://192.168.1.3:20880/com.wasteland.blogsourcecode.service.UserService?anyhost=true&application=dubbo-zookeeper-producer1&bean.name=com.wasteland.blogsourcecode.service.UserService&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.wasteland.blogsourcecode.service.UserService&methods=sayHello&owner=wasteland&pid=10392&release=2.7.4.1&side=provider×tamp=1747064723475
分析如下:
- 首先,在形式上我们发现,其实这么牛逼的
dubbo
也是用类似于http
的协议发布自己的服务的,只是这里我们用的是dubbo协议。 dubbo://192.168.1.3:20880/com.wasteland.blogsourcecode.service.UserService
上面这段链接就是 ? 之前的链接,构成:协议://ip:端口/接口,发现是不是也没有什么神秘的。anyhost=true&application=dubbo-zookeeper-producer1&bean.name=com.wasteland.blogsourcecode.service.UserService&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.wasteland.blogsourcecode.service.UserService&methods=sayHello&owner=wasteland&pid=10392&release=2.7.4.1&side=provider×tamp=1747064723475
? 之后的字符串,分析后你发现,这些都是在前文provider.xml中配置的字段,然后通过 & 拼接而成的,闻到了 http 的香味了吗?
Dubbo 数据在Zookeeper中的组织形式
Dubbo 使用 Zookeeper 用于服务的注册发现和配置管理,在 Zookeeper 中数据的组织由下图所示:
首先,所有 Dubbo 相关的数据都组织在 /dubbo 的根节点下。
二级目录是服务名,如 com.wasteland.blogsourcecode.service.UserService。
三级目录有两个子节点,分别是 providers 和 consumers,表示该服务的提供者和消费者。
四级目录记录了与该服务相关的每一个应用实例的 URL 信息,在 providers 下的表示该服务的所有提供者,而在 consumers 下的表示该服务的所有消费者。举例说明,com.wasteland.blogsourcecode.service.UserService 的服务提供者在启动时将自己的 URL 信息注册到 /dubbo/com.wasteland.blogsourcecode.service.UserService/providers 下;同样的,服务消费者将自己的信息注册到相应的 consumers 下,同时,服务消费者会订阅其所对应的 providers 节点,以便能够感知到服务提供方地址列表的变化。
7.3.2 Dubbo Admin控制台验证
因为前文中已经在云服务器上利用Docker安装了Dubbo Admin,这里直接进入到Dubbo Admin管理页面上确认对应dubbo接口是否注册成功。
八、其他高级特性
8.1 启动时检查
Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check=“true”。但是,有的时候,我们并不是都需要启动时就检查的,比如测试的时候,我们是需要更快速的启动,所以,这种场景的时候,我们是需要关闭这个功能的。下面,我们看看如何使用这个功能。
在服务端注册的时候(客户端注册时同样适用);
<dubbo:registry protocol="zookeeper" address="localhost:2181,localhost:2182,localhost:2183" check="false"/>
在客户端引用服务端服务的时候;
<dubbo:reference check="false" id="providerService"interface="com.wasteland.blogsourcecode.service.UserService"/>
8.2 集群容错
dubbo 也是支持集群容错的,同时也有很多可选的方案,其中,默认的方案是 failover,也就是重试机制。
首先,我们先把所有的容错机制都整理一遍,然后再看看使用。
集群模式 | 说明 | 使用方法 |
---|---|---|
Failover Cluster | 失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries=“2” 来设置重试次数(不含第一次)。 | cluster=“xxx” xxx:集群模式名称 ,例如cluster=“failover” |
Failfast Cluster | 快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。 | |
Failsafe Cluster | 失败安全,出现异常时,直接忽略。 | |
Failback Cluster | 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。 | |
Forking Cluster | 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=“2” 来设置最大并行数。 | |
Broadcast Cluster | 广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。 |
使用实例
在发布服务或者引用服务的时候设置
<!--服务发布的配置,需要暴露的服务接口-->
<dubbo:service cluster="failover" retries="2"interface="com.wasteland.blogsourcecode.service.UserService"ref="providerService"/>
<dubbo:reference cluster="failover" retries="2" check="false" id="providerService"interface="com.wasteland.blogsourcecode.service.UserService"/>
8.3 负载均衡
dubbo调用是支持负载均衡的,这里总结一下dubbo支持的负载均衡的一些方案及使用方法。
负载均衡模式 | 说明 | 使用方法 |
---|---|---|
Random LoadBalance | 随机 按权重设置随机概率 | <dubbo:service loadbalance="xxx"/> xxx:负载均衡方法 |
RoundRobin LoadBalance | 轮询 按公约后的权重设置轮询比率。 | |
LeastActive LoadBalance | 最少活跃调用数 相同活跃数的随机,活跃数指调用前后计数差。 | |
ConsistentHash LoadBalance | 一致性 Hash 相同参数的请求总是发到同一提供者。 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。 |
8.4 直连提供者
在开发和测试时,我们可以绕过注册中心,直接指定服务提供者,所以这种情况下,我们需要直接连接服务端的地址。
<dubbo:reference id="providerService"interface="com.wasteland.blogsourcecode.service.UserService"url="dubbo://服务端ip:20880/com.wasteland.blogsourcecode.service.UserService"/>
说明:可以看到,只要在消费端在dubbo:reference
节点使用url给出服务端的方法即可。
8.5 多协议机制
在前面我们使用的协议都是 dubbo
协议,但是 dubbo 除了支持这种协议外还支持其他的协议,比如,rmi
、hessian
、triple
等,另外,而且还可以用多种协议同时暴露一种服务。
8.5.1 多端口多协议
先声明多种协议
<!--当前服务发布所依赖的协议;webserovice、Thrift、Hessain、http--><dubbo:protocol name="dubbo" port="20880"/><dubbo:protocol name="rmi" port="1099" />
然后在发布接口的时候使用具体协议
<!--服务发布的配置,需要暴露的服务接口--><dubbo:service cluster="failover" retries="2"interface="com.wasteland.blogsourcecode.service.UserService"ref="providerService" protocol="dubbo"/><dubbo:service cluster="failover" retries="2"interface="com.wasteland.blogsourcecode.service.UserService"ref="providerService" protocol="rmi"/>
对于消费端而言,如果用户没有明确配置,默认情况下框架会自动选择 dubbo 协议调用。
8.5.2 单端口多协议
声明协议和上面的方式一样,在发布接口的时候有一点不一样。
<dubbo:service cluster="failover" retries="2"interface="com.wasteland.blogsourcecode.service.UserService"ref="providerService" protocol="rmi,dubbo"/>
说明:protocol
属性,可以用,
隔开,使用多种协议。
8.6 多注册中心
Dubbo 支持同一服务向多注册中心同时注册,或者不同服务分别注册到不同的注册中心上去,甚至可以同时引用注册在不同注册中心上的同名服务。
服务端多注册中心发布服务
一个服务可以在不同的注册中心注册,当一个注册中心出现问题时,可以用其他的注册中心。
注册
<!--多注册中心-->
<dubbo:registry protocol="zookeeper" id="reg1" timeout="10000" address="localhost:2181"/>
<dubbo:registry protocol="zookeeper" id="reg2" timeout="10000" address="localhost:2182"/>
<dubbo:registry protocol="zookeeper" id="reg3" timeout="10000" address="localhost:2183"/>
发布服务
<!--服务发布的配置,需要暴露的服务接口-->
<dubbo:service cluster="failover" retries="2"interface="com.wasteland.blogsourcecode.service.UserService"ref="providerService" registry="reg1"/>
<dubbo:service cluster="failover" retries="2"interface="com.wasteland.blogsourcecode.service.UserService"ref="providerService" protocol="rmi" registry="reg2"/>
说明:使用registry="reg2"
指定该接口使用的注册中心,同时也可以使用多个,
隔开,例如registry="reg1,,reg2"
。
消费端多注册中心引用服务
首先,先向不同注册中心注册;
<!--多注册中心-->
<dubbo:registry protocol="zookeeper" id="reg1" timeout="10000" address="localhost:2181"/>
<dubbo:registry protocol="zookeeper" id="reg2" timeout="10000" address="localhost:2182"/>
<dubbo:registry protocol="zookeeper" id="reg3" timeout="10000" address="localhost:2183"/>
其次,不同的消费端服务引用使用不同的注册中心;
<!--不同的服务使用不同的注册中心-->
<dubbo:reference cluster="failover" retries="2" check="false" id="providerService"interface="com.wasteland.blogsourcecode.service.UserService" registry="reg1"/>
<dubbo:reference cluster="failover" retries="2" check="false" id="providerService2"interface="com.wasteland.blogsourcecode.service.UserService" registry="reg2"/>
说明:上面分别使用注册中心1和注册中心2。
8.7 多版本
不同的服务是有版本不同的,版本可以更新并且升级,同时,不同的版本之间是不可以调用的。
<!--服务发布的配置,需要暴露的服务接口--><dubbo:service cluster="failover" retries="2"interface="com.wasteland.blogsourcecode.service.UserService"ref="providerService" registry="reg1" version="1.0.0"/><dubbo:service cluster="failover" retries="2"interface="com.wasteland.blogsourcecode.service.UserService"ref="providerService" protocol="rmi" registry="reg2" version="1.0.0"/>
8.8 日志管理
dubbo
也可以将日志信息记录或者保存到文件中的。
1、使用accesslog
输出到log4j
<dubbo:protocol accesslog="true" name="dubbo" port="20880"/><dubbo:protocol accesslog="true" name="rmi" port="1099" />
2、输出到文件
<dubbo:protocol accesslog="http://localhost/log.txt" name="dubbo" port="20880"/><dubbo:protocol accesslog="http://localhost/log2.txt" name="rmi" port="1099" />
九、总结
本文介绍了Dubbo的基本概念、Dubbo与Spring Cloud的关系及SpringBoot结合Dubbo的实战演练。除此之外,因为本文中选择的是
Dubbo与Zookeeper相结合来实现服务发现的功能,本文中还介绍了如何使用Docker部署Zookeeper容器。相信本篇文章已经能够让你对Dubbo有基本的了解与使用,在后面的文章中会详细分析Dubbo实现远程调用的原理。