1 - 快速部署微服务应用
背景
作为微服务框架,Dubbo 最重要的是为用户提供跨进程的 RPC 远程调用能力。如上图所示,Dubbo 的服务消费者(Consumer)通过一系列任务向服务提供者(Provider)发送请求。
为了实现这样的目标,Dubbo 引入了 Registry 组件。通过 Registry,服务消费者可以感知到服务提供者的连接方式,从而将请求发送到正确的服务提供者。
目标
了解微服务调用方式和 Dubbo 的能力
难度
低
环境要求
系统:Windows,Linux,MacOS
JDK 8 及以上(推荐 JDK17)
Git
Docker(可选)
动手
本章将手把手教你通过几个简单的命令部署并运行最简单的 Dubbo 使用案例。
1. 获取测试项目
在开始整个教程之前,我们需要获取测试项目的代码。Dubbo 的所有测试用例代码都存储在仓库 apache/dubbo-samples 中,以下命令可以帮助你获取 Samples 仓库中的所有代码。
git clone --depth=1 --branch master git@github.com:apache/dubbo-samples.git
2. 了解 Dubbo Samples 项目结构
在 apache/dubbo-samples 仓库本地克隆之后,本节将解释仓库的具体组织方式。
.
├── codestyle // style configuration file for development
├── 1-basic // basic introductory use cases
├── 2-advanced // advanced usage
├── 3-extensions // Example of extension usage
├── 4-governance // service governance use cases
├── 10-task // Example of Dubbo learning series
├── 99-integration // integration test use
├── test // integration test use
└── tools // three-party component quick start tool
如上表所示,apache/dubbo-samples 主要由三部分组成:代码风格文件、测试代码和集成测试。
代码风格文件可以在开发 Dubbo 代码时使用,包括 IntelliJ IDEA 的配置文件。
测试代码是本教材的核心内容,目前包括 5 个部分:基础入门用例,供初学者使用;高级进阶用法,供开发者使用;扩展 Dubbo 周边扩展使用示例,供中间件维护者使用;治理服务治理用例,供生产环境使用;Dubbo 学习系列。本文将基于基础入门用例,讲解最简单的 Dubbo API 使用方式。
集成测试是 Dubbo 质量保障体系的重要组成部分,Dubbo 的每个版本都会对所有样本进行回归验证,以确保 Dubbo 的所有变更都不会影响样本的使用。
3. 启动一个简单的注册中心
从本节开始,将正式通过三个命令部署一个微服务应用。
从 背景 部分可以看出,运行 Dubbo 应用的一个重要前提是部署注册中心。为了方便本教程的使用,我们提供了一个基于 Apache Zookeeper 注册中心的简单启动器。如果你需要在生产环境中部署注册中心。请参考 生产环境初始化 部署高可用注册中心。
Windows:
./mvnw.cmd clean compile exec:java -pl tools/embedded-zookeeper
Linux / MacOS:
./mvnw clean compile exec:java -pl tools/embedded-zookeeper
Note: You need to open an independent terminal to run, and the command will keep executing.
Docker:
docker run --name some-zookeeper --restart always -d zookeeper
执行完以上命令后,等待一段时间,直到出现下图所示的日志,就表示注册中心启动成功,可以继续执行后续任务。
4. 启动服务提供者
注册中心启动后,下一步就是启动一个对外提供服务的服务提供者。dubbo-samples 中也提供了相应的样本,可以通过以下命令快速拉起。
Windows:
./mvnw.cmd clean compile exec:java -pl 1-basic/dubbo-samples-api "-Dexec.mainClass=org.apache.dubbo.samples.provider.Application"
Linux / MacOS:
./mvnw clean compile exec:java -pl 1-basic/dubbo-samples-api -Dexec.mainClass="org.apache.dubbo.samples.provider.Application"
Note: You need to open an independent terminal to run, and the command will keep executing.
执行完以上命令后,等待一段时间,直到出现下图所示的日志(DubboBootstrap awaiting
),就表示服务提供者启动成功,表示服务提供者可以对外提供服务了。
[19/01/23 03:55:49:049 CST] org.apache.dubbo.samples.provider.Application.main() INFO bootstrap.DubboBootstrap: [DUBBO] DubboBootstrap awaiting ..., dubbo version: 3.2.0 -beta.3, current host: 169.254.44.42
5. 启动服务消费者
最后一步是启动一个服务消费者来调用服务提供者,这是 RPC 调用的核心,为服务消费者调用服务提供者提供桥梁。
Windows:
./mvnw.cmd clean compile exec:java -pl 1-basic/dubbo-samples-api "-Dexec.mainClass=org.apache.dubbo.samples.client.Application"
Linux / MacOS:
./mvnw clean compile exec:java -pl 1-basic/dubbo-samples-api -Dexec.mainClass="org.apache.dubbo.samples.client.Application"
执行完以上命令后,等待一段时间,直到出现下图所示的日志(hi, dubbo
),打印出来的数据是服务提供者处理后返回的,标志着一次服务调用成功。
Receive result ======> hi, dubbo
进一步阅读
1. 消费者如何找到服务器?
在本用例的步骤 3 中,启动了一个 Zookeeper 注册中心,服务提供者会将自己的地址写入注册中心,供服务消费者获取。
Dubbo 会将服务提供者的连接信息写入 Zookeeper 的 /dubbo/interfaceName
和 /services/appName
下。
以下是一个 Zookeeper 上数据的示例
[zk: localhost:2181(CONNECTED) 5] ls /dubbo/org.apache.dubbo.samples.api.GreetingsService/providers
[dubbo%3A%2F%2F30.221.146.35%3A20880%2Forg.apache.dubbo.samples.api.GreetingsService%3Fanyhost%3Dtrue%26application%3Dfirst-dubbo-provider%26background%3Dfalse%26deprecated%3Dfalse%26dubbo%3D2 .0.2%26dynamic%3Dtrue%26environment%3Dproduct%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.samples.api.GreetingsService%26ipv6%3Dfd00%3A1%3A5%3A5200%3A3218%3A774a%3A4f67%3A23ho1% %26pid%3D85639%26release%3D3.1.4%26service-name-mapping%3Dtrue%26side%3Dprovider%26timestamp%3D1674960780647]
[zk: localhost:2181(CONNECTED) 2] ls /services/first-dubbo-provider
[30.221.146.35:20880]
[zk: localhost:2181(CONNECTED) 3] get /services/first-dubbo-provider/30.221.146.35:20880
{"name":"first-dubbo-provider","id":"30.221.146.35:20880","address":"30.221.146.35","port":20880,"sslPort":null,"payload" :{"@class":"org.apache.dubbo.registry.zookeeper.ZookeeperInstance","id":"30.221.146.35:20880","name":"first-dubbo-provider","metadata":{ "dubbo.endpoints":"[{\"port\":20880,\"protocol\":\"dubbo\"}]", "dubbo.metadata-service.url-params": "{\"connections\ ":\"1\",\"version\":\"1.0.0\",\"dubbo\":\"2.0.2\",\"release\":\"3.1.4\", \"side\":\"provider\",\"ipv6\":\"fd00:1:5:5200:3218:774a:4f67:2341\",\"port\":\"20880\", \"protocol\":\"dubbo\"}","dubbo.metadata.revision":"871fbc9cb2730caea9b0d858852d5ede","dubbo.metadata.storage-type":"local","ipv6":"fd00:1:5 :5200:3218:774a:4f67:2341","timestamp":"1674960780647"}},"registrationTimeUTC":1674960781893,"serviceType":"DYNAMIC","uriSpec":null}
关于 Dubbo 服务发现模型的更多细节,请参考 服务发现。
2. 消费者如何发起请求?
在 Dubbo 的调用模型中,连接服务消费者和服务提供者的桥梁是接口。
服务提供者实现指定的接口,服务消费者通过 Dubbo 订阅该接口。当服务消费者调用接口时,Dubbo 会将请求封装成网络请求,然后发送到服务提供者进行实际调用。
在这个用例中,定义了一个名为 GreetingsService
的接口,它有一个名为 sayHi
的方法。
// 1-basic/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/api/GreetingsService.java
package org.apache.dubbo.samples.api;
public interface GreetingsService {
String sayHi(String name);
}
服务消费者可以通过 Dubbo 的 API 获取 GreetingsService
接口的代理,然后以正常的方式调用接口。**由于 Dubbo 的动态代理机制,这一切都像本地调用一样。**
// 1-basic/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/client/Application.java
// Get the subscribed Stub
GreetingsService service = reference. get();
// call like a normal java interface
String message = service.sayHi("dubbo");
3. 可以部署多个服务器吗?
是的,本节将演示如何启动一个服务器端的**集群**。
要启动注册中心,您可以参考动手实践第 3 节中的[教程](#3-start-a-simple-registration-center)
修改服务提供者返回的数据,使第一个启动的服务提供者返回
hi, dubbo. I am provider 1.
修改 1-basic/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/provider/GreetingsServiceImpl.java
文件的第 25 行,如下所示。
// 1-basic/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/provider/GreetingsServiceImpl.java
package org.apache.dubbo.samples.provider;
import org.apache.dubbo.samples.api.GreetingsService;
public class GreetingsServiceImpl implements GreetingsService {
@Override
public String sayHi(String name) {
return "hi, " + name + ". I am provider 1.";
}
}
要启动第一个服务提供者,您可以参考动手实践第 4 节中的[教程](#4-Start-the-service-provider)
修改服务提供者返回的数据,使第二个启动的服务提供者返回
hi, dubbo. I am provider 2.
修改 1-basic/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/provider/GreetingsServiceImpl.java
文件的第 25 行,如下所示。
// 1-basic/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/provider/GreetingsServiceImpl.java
package org.apache.dubbo.samples.provider;
import org.apache.dubbo.samples.api.GreetingsService;
public class GreetingsServiceImpl implements GreetingsService {
@Override
public String sayHi(String name) {
return "hi, " + name + ". I am provider 2.";
}
}
启动第二个服务提供者,您可以参考动手实践第 4 节中的[教程](#4-Start-the-service-provider)
要启动服务消费者,您可以参考动手实践第 5 节中的[教程](#5-Start-the-service-consumer)。如果您多次启动消费者,您会发现返回的结果是不同的。
在 dubbo-samples 中,还有一个消费者应用程序 org.apache.dubbo.samples.client.AlwaysApplication
,它将定期发起调用,可以通过以下命令启动。
Windows:
./mvnw.cmd clean compile exec:java -pl 1-basic/dubbo-samples-api -Dexec.mainClass="org.apache.dubbo.samples.client.AlwaysApplication"
Linux / MacOS:
./mvnw clean compile exec:java -pl 1-basic/dubbo-samples-api -Dexec.mainClass="org.apache.dubbo.samples.client.AlwaysApplication"
启动后,您会看到类似以下的日志。消费者将随机调用不同的服务提供者,返回的结果也是远程服务提供者认为的结果。
Sun Jan 29 11:23:37 CST 2023 Receive result ======> hi, dubbo. I am provider 1.
Sun Jan 29 11:23:38 CST 2023 Receive result ======> hi, dubbo. I am provider 2.
Sun Jan 29 11:23:39 CST 2023 Receive result ======> hi, dubbo. I am provider 2.
Sun Jan 29 11:23:40 CST 2023 Receive result ======> hi, dubbo. I am provider 1.
Sun Jan 29 11:23:41 CST 2023 Receive result ======> hi, dubbo. I am provider 1.
4. 这个用例复杂吗?
不,Dubbo 只需要简单的配置就能实现稳定高效的远程调用。
以下是一个简单的服务提供者示例,可以通过定义一些配置来启动。
// 1-basic/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/provider/Application.java
// define all services
ServiceConfig<GreetingsService> service = new ServiceConfig<>();
service.setInterface(GreetingsService.class);
service.setRef(new GreetingsServiceImpl());
// start Dubbo
DubboBootstrap. getInstance()
.application("first-dubbo-provider")
.registry(new RegistryConfig(ZOOKEEPER_ADDRESS))
.protocol(new ProtocolConfig("dubbo", -1))
.service(service)
.start();
以下是一个简单的服务消费者示例。定义几个配置后,启动后就可以获取相应的代理对象。之后,用户完全不需要感知这个对象背后的复杂实现。**一切只需要和本地调用一样。**
// 1-basic/dubbo-samples-api/src/main/java/org/apache/dubbo/samples/client/Application.java
// define all subscriptions
ReferenceConfig<GreetingsService> reference = new ReferenceConfig<>();
reference.setInterface(GreetingsService.class);
// start Dubbo
DubboBootstrap. getInstance()
.application("first-dubbo-consumer")
.registry(new RegistryConfig(ZOOKEEPER_ADDRESS))
.reference(reference)
.start();
// Get the subscribed Stub
GreetingsService service = reference. get();
// call like a normal java interface
String message = service.sayHi("dubbo");
更多
本用例介绍了 RPC 远程调用的基本流程,并通过启动三个节点:注册中心、服务提供者和服务消费者,模拟了微服务部署架构。
在下一个教程中,我们将解释服务提供者和服务消费者的配置,并告诉您如何从头开始构建一个微服务应用程序。