Dubbo 入門系列之快速部署一個微服務應用

2023-02-01 15:00:42

本文將基於 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 主要由三個部分組成:程式碼風格檔案、測試程式碼、整合測試。

  1. 程式碼風格檔案是開發 Dubbo 程式碼的時候可以使用,其中包括了 IntelliJ IDEA 的組態檔。

  2. 測試程式碼即本教材所需要的核心內容。目前包括了 5 個部分的內容:面向初學者的 basic 入門用例、面向開發人員的 advanced 高階用法、面向中介軟體維護者的 extensions Dubbo 周邊擴充套件使用範例、面向生產的 governance 服務治理用例以及 Dubbo 學習系列。本文將基於 basic 入門用例中最簡單的 Dubbo API 使用方式進行講解。

  3. 整合測試是 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 遠端呼叫的基礎流程,通過啟動註冊中心、服務提供者、服務消費者三個節點來模擬一個微服務的部署架構。

下一個教學中,將就服務提供者和服務消費者分別都做了什麼設定進行講解,從零告訴你如何搭建一個微服務應用。

歡迎在 https://github.com/apache/dubbo 給 Dubbo Star。
搜尋關注官方微信公眾號:Apache Dubbo,瞭解更多業界最新動態,掌握大廠面試必備 Dubbo 技能