dubbo的定义是:
Apache Dubbo 是一款高性能、轻量级的开源RPC框架,用于构建分布式服务系统。
所以我们先来讲一下rpc。
RPC相关知识
什么是 RPC?
定义:
允许程序像调用本地方法一样调用远程服务,隐藏网络细节。
生活类比:
- 🏠 本地调用:直接走到隔壁办公室找同事
- 🌍 RPC:通过电话呼叫海外同事,但体验如同面对面交谈
RPC 核心思想
-
透明性
- 开发者无需处理网络通信(序列化、传输、错误)
-
简化分布式系统
- 跨机器调用 → 本地函数调用
arduino
+-------------+ +-------------+
| Client | | Server |
| (调用本地方法) | →网络→ | (执行远程方法) |
+-------------+ +-------------+
RPC 工作原理
四步流程:
-
客户端代理
- 序列化参数(如 Protobuf/JSON)
-
网络传输
- 通过 HTTP/TCP 发送请求
-
服务端处理
- 反序列化 → 执行业务 → 序列化结果
-
返回响应
- 客户端解析结果
RPC 核心组件
组件 | 作用 | 典型技术 |
---|---|---|
序列化协议 | 对象 ↔ 字节流 | Protobuf, JSON |
传输协议 | 数据传输通道 | HTTP/2, TCP |
服务发现 | 动态定位服务地址 | ZooKeeper, Consul |
负载均衡 | 请求分发策略 | 轮询, 一致性哈希 |
RPC 的优缺点
✅ 优点:
- 开发高效(隐藏网络细节)
- 高性能(二进制协议)
- 跨语言支持(如 gRPC)
❌ 缺点:
- 接口强耦合(需同步升级)
- 调试复杂(需链路追踪工具)
讨论题:
何时应避免使用 RPC? (答案:开放 API 需要高灵活性时)
常见 RPC 框架
框架 | 特点 | 适用场景 |
---|---|---|
gRPC | HTTP/2 + Protobuf,高性能 | 内部微服务 |
Dubbo | 服务治理(熔断、降级) | 企业级 Java 系统 |
Thrift | 多协议支持(TCP/HTTP) | 跨语言系统 |
RPC vs RESTful API
维度 | RPC | RESTful |
---|---|---|
协议 | 自定义二进制协议(高效) | HTTP + 文本(灵活) |
耦合性 | 高(接口强依赖) | 低(资源导向) |
场景 | 内部服务通信 | 开放 API |
思考题:
为什么微服务架构常用 RPC?
典型应用场景
-
微服务通信
- 订单服务 → 支付服务
-
分布式计算
- Spark 节点任务调度
-
实时系统
- 游戏服务器状态同步
什么是Dubbo?
- 定义:Dubbo 是一款高性能、轻量级的开源RPC框架,用于构建分布式服务系统。
- 核心目标:简化服务间的远程调用,提供服务治理能力(负载均衡、容错、服务发现等)。
- 适用场景:微服务架构、高并发分布式系统
核心特性
- 高性能:基于Netty的NIO通信,支持多种协议(默认Dubbo协议)。
- 透明化远程调用:像调用本地方法一样调用远程服务。
- 服务治理:动态配置、流量调度、服务降级、鉴权等。
角色说明
- Provider:服务提供者,暴露服务接口。
- Consumer:服务消费者,调用远程服务。
- Registry(注册中心):服务注册与发现(推荐使用Zookeeper、Nacos)。
环境搭建
创建一个这样的项目,api模块是公共模块
父项目引入公共依赖jar包
xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.10</version>
</dependency>
第一个Dubbo程序开发
这里没有使用 springboot ,用于了解 dubbo 的具体过程,如果学着比较吃力可以跳过,后面我会讲springboot下的使用
api模块开发
定义在api模块中
定义一个业务接口
java
public interface UserService {
boolean login(String name, String password);
}
Provider开发
引入api模块依赖
记得写成你自己的名字,不要复制粘贴
如果无法引入,先在 api 模块执行 install 命令
xml
<dependency>
<groupId>com.huang.dubbo</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
实现具体的业务逻辑
实现我们刚才在api模块中定义的接口,并添加业务逻辑
java
@Slf4j
public class UserServiceImpl implements UserService{
@Override
public boolean login(String name, String password) {
log.info("用户登录,姓名:{},密码:{}", name, password);
return false;
}
}
编写spring配置文件
名字随便叫什么都可以
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo">
<!-- 名称-->
<dubbo:application name="dubbo-provider"/>
<!-- 协议和端口-->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- 创建对象-->
<bean id="userService" class="com.huang.dubbo.service.UserServiceImpl"/>
<!-- 发布一个dubbo的服务,可以被消费者调用-->
<dubbo:service interface="com.huang.dubbo.service.UserService" ref="userService"/>
</beans>
编写启动类
java
public class ProviderMain {
public static void main(String[] args) {
// 创建 spring 工厂加载配置文件,这个文件名字要和你刚才写的文件名一样
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
applicationContext.start();
// 阻塞主线程
try {
new CountDownLatch(1).await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
启动之后可以在控制台看到dubbo暴露的服务信息
其中的:dubbo://192.168.1.119:20880/com.huang.dubbo.service.UserService (注意去找你自己控制台的)
在后面会用到
项目结构
Consumer开发
引入api依赖
xml
<dependency>
<groupId>com.huang.dubbo</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
创建配置文件
通过 dubbo 封装的功能获取 provider 提供的 rpc 服务
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo">
<!-- 给服务起一个名字-->
<dubbo:application name="dubbo-consumer"/>
<!--通过dubbo获取远端的 rpc 服务-->
<!-- 参数说明: id:provider 中的服务对象 id ,url:provider 控制台中打印的服务信息-->
<dubbo:reference interface="com.huang.dubbo.service.UserService" id="userService" url="dubbo://192.168.1.119:20880/com.huang.dubbo.service.UserService"/>
</beans>
创建启动类
java
public class ConsumerApplication {
public static void main(String[] args) {
// 创建 spring 工厂加载配置文件
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("application.xml");
// 获取 bean
UserService userService = (UserService) applicationContext.getBean("userService");
// 获取返回值
boolean bool = userService.login("huang", "123456");
System.out.println("ref = " + bool);
// 阻塞主线程
try {
System.in.read();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
启动程序就可以看到效果了,consumer 调用了 provider 的程序,并获得了返回值。
项目结构
解决报错问题
在 consumer 服务中,你可以找到如下的报错信息
原因:
qos是Dubbo的在线运维命令,可以对服务进行动态的配置、控制及查询,Dubbo2.5.8新版本重构了 telnet (2.0.5开始支持)模块,提供了新的 telnet 命令支持,新版本的 telnet 端口与 dubbo 协议的端口不是同一端口,默认为22222。
在同一台服务器(电脑)里面,启动 provider 时 22222 端口已经被使用,所有 consumer 启动就会报错
解决方案:
在配置文件中进行配置
xml
<dubbo:parameter key="qos.enable" value="true"/> <!-- 是否开启在线运维命令 -->
<dubbo:parameter key="qos.accept.foregin.ip" value="false"/> <!-- 不允许其他机器的访问 -->
<dubbo:parameter key="qos.port" value="33333"/> <!-- 修改port -->
springboot中的配置:
properties
dubbo.application.qos.enable=true
dubbo.application.qos.accept.foregin.ip=false
dubbo.application.qos.port=33333
修改后的 consumer 配置文件:
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo">
<!-- 给服务起一个名字-->
<dubbo:application name="dubbo-consumer">
<dubbo:parameter key="qos.enable" value="false"/>
</dubbo:application>
<!--通过dubbo获取远端的 rpc 服务-->
<!-- 参数说明: id:provider 中的服务对象 id ,url:provider 控制台中打印的服务信息-->
<dubbo:reference interface="com.huang.dubbo.service.UserService" id="userService"
url="dubbo://192.168.1.119:20880/com.huang.dubbo.service.UserService"/>
</beans>
把<dubbo:parameter key="qos.enable" value="false"/>
加到 <dubbo:application>
标签里面
可以思考一下,duboo 在这个流程中具体干了什么
基于 SpringBoot 的方式使用Dubbo
自己先创建两个springboot项目(provider和consumer),api模块可以继续使用上面的这个,也可以重新创建一个。
构建provider模块
引入依赖
xml
<dependency>
<groupId>com.huang.dubbo</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
填写配置文件
yaml
spring:
application:
name: dubbo-boot-provider #应用名称
dubbo:
protocol:
name: dubbo #配置协议
port: -1 #配置端口,-1 会自动选择
编写业务逻辑
创建一个类实现我们在 api 模块中定义的接口,并加上@DubboService
注解
java
@DubboService
public class UserServiceImpl implements UserService{
@Override
public boolean login(String name, String password) {
System.out.println("用户登录,姓名:" + name + ",密码:" + password);
return false;
}
}
编写启动类
在启动类加上@EnableDubbo
java
@EnableDubbo // 开启 Dubbo 功能
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
构建Consumer模块
引入依赖
xml
<dependency>
<groupId>com.huang.dubbo</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
填写配置文件
yaml
spring:
application:
name: dubbo-boot-consumer #配置应用名
dubbo:
application:
qos-enable: false # 关闭qos
使用 springboot 的测试类进行测试
java
@SpringBootTest
public class ConsumerTest {
// url 还是在控制台找,和上面使用 spring 时是一样的
@DubboReference(url = "dubbo://192.168.1.119:20880/com.huang.dubbo.service.UserService")
private UserService userService;
@Test
public void test() {
// 发起远程调用,获得返回值
boolean ret = userService.login("huang", "123456");
System.out.println("ret = " + ret);
}
}
直接运行单元测试就可以看到效果了
注解解析
简单介绍一下各个注解的功能
@EnableDubbo
作用:
-
@EnableDubbo
注解用于扫描@DubboService
并把对应的对象实例化,并发布成 RPC 服务。扫描路径:应用这个注解的类(启动类)所在的包及其子包
-
如果
@DubboService
注解修饰的类没有放到@EnableDubbo
注解修饰的类当前包及其子包,可以使用@DubboComponentScan("basePackages")
来指定扫描路径 -
还可以在配置文件中进行配置
yaml
dubbo:
scan:
base-packages: com.haung.dubbo # 扫描的包
@DubboService
- 使用了
@DubboService
注解修饰的类型, SpringBoot 会创建这个类型的对象,并发布成 Dubbo 服务 @DubboService
等同于@Component
等注解创建对象的作用
考虑兼容性,建议实现类不仅加上
@DubboService
注解,也加上@Service
注解
@DubboReference
- 在 Consumer 端,通过
@DubboReference
,注入远端服务的代理对象 @DubboReference
类似于原始Spring开发中@Autowired
注解的作用
等同于dubbo:reference 标签的效果
使用 Nacos 作为注册中心实现自动服务发现
演示 Nacos 作为注册中心实现自动服务发现,示例基于 Spring Boot 应用展开
添加依赖
xml
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-nacos-spring-boot-starter</artifactId>
<version>3.3.0</version>
</dependency>
配置并启用 Nacos
yaml
dubbo:
registry:
address: nacos://localhost:8848
register-mode: instance # 新用户请设置此值,表示启用应用级服务发现,可选值 interface、instance、all,默认值为 all,未来版本将切换默认值为 instance
如果需要认证:
yaml
dubbo:
registry:
address: nacos://localhost:8848?username=nacos&password=nacos
register-mode: instance
注册消费者
Dubbo 3.0.0 版本以后,增加了是否注册消费者的参数,如果需要将消费者注册到 nacos 注册中心上,需要将参数(register-consumer-url)设置为true,默认是false。
yaml
dubbo:
registry:
address: nacos://localhost:8848
register-mode: instance # 新用户请设置此值,表示启用应用级服务发现,可选值 interface、instance、all
parameters.register-consumer-url: true
其他的基本不变,还有就是@DubboReference
直接加在属性上就可以完成注入,不需要url