本文将基于 Dubbo Samples 示例示範如何快速搭建并部署一個微服務應用。
背景
Dubbo 作為一款微服務架構,最重要的是向使用者提供跨程序的 RPC 遠端調用能力。如上圖所示,Dubbo 的服務消費者(Consumer)通過一系列的工作将請求發送給服務提供者(Provider)。
為了實作這樣一個目标,Dubbo 引入了注冊中心(Registry)元件,通過注冊中心,服務消費者可以感覺到服務提供者的連接配接方式,進而将請求發送給正确的服務提供者。
目标
了解微服務調用的方式以及 Dubbo 的能力
難度
低
環境要求
- 系統:Windows、Linux、MacOS
- JDK 8 及以上(推薦使用 JDK17)
- Git
- Docker (可選)
動手實踐
本章将通過幾個簡單的指令,一步一步教你如何部署并運作一個最簡單的 Dubbo 用例。
1. 擷取測試工程
在開始整個教程之前,我們需要先擷取測試工程的代碼。Dubbo 的所有測試用例代碼都存儲在 apache/dubbo-samples 這個倉庫中,以下這個指令可以幫你擷取 Samples 倉庫的所有代碼。
git clone --depth=1 --branch master [email protected]:apache/dubbo-samples.git
2. 認識 Dubbo Samples 項目結構
在将 apache/dubbo-samples 這個倉庫 clone 到本地以後,本小節将就倉庫的具體組織方式做說明。
.
├── codestyle // 開發使用的 style 配置檔案
├── 1-basic // 基礎的入門用例
├── 2-advanced // 進階用法
├── 3-extensions // 擴充使用示例
├── 4-governance // 服務治理用例
├── 10-task // Dubbo 學習系列示例
├── 99-integration // 內建測試使用
├── test // 內建測試使用
└── tools // 三方元件快速啟動工具
如上表所示,apache/dubbo-samples 主要由三個部分組成:代碼風格檔案、測試代碼、內建測試。
- 代碼風格檔案是開發 Dubbo 代碼的時候可以使用,其中包括了 IntelliJ IDEA 的配置檔案。
- 測試代碼即本教材所需要的核心内容。目前包括了 5 個部分的内容:面向初學者的 basic 入門用例、面向開發人員的 advanced 進階用法、面向中間件維護者的 extensions Dubbo 周邊擴充使用示例、面向生産的 governance 服務治理用例以及 Dubbo 學習系列。本文将基于 basic 入門用例中最簡單的 Dubbo API 使用方式進行講解。
- 內建測試是 Dubbo 的品質保證體系中重要的一環,Dubbo 的每個版本都會對所有的 samples 進行回歸驗證,保證 Dubbo 的所有變更都不會影響 samples 的使用。
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
注:需要開一個獨立的 terminal 運作,指令将會保持一直執行的狀态。
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"
注:需要開一個獨立的 terminal 運作,指令将會保持一直執行的狀态。
在執行完上述指令以後,等待一會出現如下圖所示的日志(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%3A2341%26methods%3DsayHi%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
// 擷取訂閱到的 Stub
GreetingsService service = reference.get();
// 像普通的 java 接口一樣調用
String message = service.sayHi("dubbo");
3. 服務端可以部署多個嗎?
可以,本小節将示範如何啟動一個服務端叢集。
1)啟動一個注冊中心,可以參考動手實踐中第 3 小節的教程
2)修改服務提供者傳回的資料,讓第一個啟動的服務提供者傳回 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.";
}
}
3)啟動第一個服務提供者,可以參考動手實踐中第 4 小節的教程
4)修改服務提供者傳回的資料,讓第二個啟動的服務提供者傳回 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 小節的教程
5)啟動服務消費者,可以參考動手實踐中第 5 小節的教程。多次啟動消費者可以看到傳回的結果是不一樣的。
在 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
// 定義所有的服務
ServiceConfig<GreetingsService> service = new ServiceConfig<>();
service.setInterface(GreetingsService.class);
service.setRef(new GreetingsServiceImpl());
// 啟動 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
// 定義所有的訂閱
ReferenceConfig<GreetingsService> reference = new ReferenceConfig<>();
reference.setInterface(GreetingsService.class);
// 啟動 Dubbo
DubboBootstrap.getInstance()
.application("first-dubbo-consumer")
.registry(new RegistryConfig(ZOOKEEPER_ADDRESS))
.reference(reference)
.start();
// 擷取訂閱到的 Stub
GreetingsService service = reference.get();
// 像普通的 java 接口一樣調用
String message = service.sayHi("dubbo");
更多
本用例介紹了一個 RPC 遠端調用的基礎流程,通過啟動注冊中心、服務提供者、服務消費者三個節點來模拟一個微服務的部署架構。
下一個教程中,将就服務提供者和服務消費者分别都做了什麼配置進行講解,從零告訴你如何搭建一個微服務應用。