第一個 Dubbo 應用
Java RMI 簡介
Java RMI (Remote Method Invocation)- 遠端方法呼叫,能夠讓客戶端像使用本地呼叫一樣呼叫服務端 Java 虛擬機器中的物件方法。RMI 是面嚮物件語言領域對 RPC (Remote Procedure Call)的完善,使用者無需依靠 IDL 的幫助來完成分散式呼叫,而是通過依賴介面這種更簡單自然的方式。
Java RMI 工作原理
一個典型的 RMI 呼叫如下圖所示:
- 服務端向 RMI 註冊服務繫結自己的地址,
- 客戶端通過 RMI 註冊服務獲取目標地址,
- 客戶端呼叫本地的 Stub 物件上的方法,和呼叫本地物件上的方法一致,
- 本地存根物件將呼叫資訊打包,通過網路傳送到服務端,
- 服務端的 Skeleton 物件收到網路請求之後,將呼叫資訊解包,
- 然後找到真正的服務物件發起呼叫,並將返回結果打包通過網路傳送回客戶端。
Java RMI 基本概念
Java RMI 是 Java 領域建立分散式應用的技術基石。後續的 EJB 技術,以及現代的分散式服務框架,其中的基本理念依舊是 Java RMI 的延續。在 RMI 呼叫中,有以下幾個核心的概念:
- 通過介面進行遠端呼叫
- 通過客戶端的 Stub 物件和服務端的 Skeleton 物件的幫助將遠端呼叫偽裝成本地呼叫
- 通過 RMI 註冊服務完成服務的註冊和發現
對於第一點,客戶端需要依賴介面,而服務端需要提供該介面的實現。
對於第二點,在 J2SE 1.5 版本之前需要通過 rmic 預先編譯好客戶端的 Stub 物件和服務端的 Skeleton 物件。在之後的版本中,不再需要事先生成 Stub 和 Skeleton 物件。
下面通過示例程式碼簡單的展示 RMI 中的服務註冊和發現
服務端的服務註冊
|
說明:
- 初始化服務物件例項,
- 通過 UnicastRemoteObject.exportObject 生成可以與服務端通訊的 Stub 物件,
- 建立一個本地的 RMI 註冊服務,監聽埠為 1099。該註冊服務執行在服務端,也可以單獨啟動一個註冊服務的程序,
- 將 Stub 物件繫結到註冊服務上,這樣,客戶端可以通過 Hello 這個名字查詢到該遠端物件。
客戶端的服務發現
|
說明:
- 獲取註冊服務例項,在本例中,由於沒有傳入任何引數,假定要獲取的註冊服務例項部署在本機,並監聽在 1099 埠上,
- 從註冊服務中查詢服務名為 Hello 的遠端物件,
- 通過獲取的 Stub 物件發起一次 RMI 呼叫並獲得結果。
理解 RMI 的工作原理和基本概念,對掌握現代分散式服務框架很有幫助,建議進一步的閱讀 RMI 官方教材 [1]。
Dubbo 基本概念
現代的分散式服務框架的基本概念與 RMI 是類似的,同樣是使用 Java 的 Interface 作為服務契約,通過註冊中心來完成服務的註冊和發現,遠端通訊的細節也是通過代理類來遮蔽。具體來說,Dubbo 在工作時有以下四個角色參與:
- 服務提供者 - 啟動時在指定埠上暴露服務,並將服務地址和埠註冊到註冊中心上
- 服務消費者 - 啟動時向註冊中心訂閱自己感興趣的服務,以便獲得服務提供方的地址列表
- 註冊中心 - 負責服務的註冊和發現,負責儲存服務提供方上報的地址資訊,並向服務消費方推送
- 監控中心 - 負責收集服務提供方和消費方的執行狀態,比如服務呼叫次數、延遲等,用於監控
- 執行容器 - 負責服務提供方的初始化、載入以及執行的生命週期管理
dubbo-architecture.png | left | 827x516
部署階段
- 服務提供者在指定埠暴露服務,並向註冊中心註冊服務資訊。
- 服務消費者向註冊中心發起服務地址列表的訂閱。
執行階段
- 註冊中心向服務消費者推送地址列表資訊。
- 服務消費者收到地址列表後,從其中選取一個向目標服務發起呼叫。
- 呼叫過程服務消費者和服務提供者的執行狀態上報給監控中心。
基於 API 的 Dubbo 應用
Dubbo 的應用一般都是通過 Spring 來組裝的。為了快速獲得一個可以工作的 Dubbo 應用,這裡的示例摒棄了複雜的配置,而改用面向 Dubbo API 的方式來構建服務提供者和消費者,另外,註冊中心和監控中心在本示例中也不需要安裝和配置。
在生產環境,Dubbo 的服務需要一個分散式的服務註冊中心與之配合,比如,ZooKeeper。為了方便開發,Dubbo 提供了直連[2]以及組播[3]兩種方式,從而避免額外搭建註冊中心的工作。在本例中,將使用組播的方式來完成服務的註冊和發現。
定義服務契約
|
說明:
- 定義了一個簡單的服務契約 GreetingsService,其中只有一個方法 sayHi 可供呼叫,入參是 String 型別,返回值也是 String 型別。
提供契約的實現
|
說明:
- 服務提供者需要實現服務契約 GreetingsService 介面。
- 該實現簡單的返回一個歡迎資訊,如果入參是 dubbo,則返回 hi, dubbo。
實現 Dubbo 服務提供方
|
說明:
- 建立一個 ServiceConfig 的例項,泛型引數資訊是服務介面型別,即 GreetingsService。
- 生成一個 AplicatonConfig 的例項,並將其裝配進 ServiceConfig。
- 生成一個 RegistryConfig 例項,並將其裝配進 ServiceConfig,這裡使用的是組播方式,引數是
multicast://224.5.6.7:1234
。合法的組播地址範圍為:224.0.0.0 - 239.255.255.255 - 將服務契約 GreetingsService 裝配進 ServiceConfig。
- 將服務提供者提供的實現 GreetingsServiceImpl 的例項裝配進 ServiceConfig。
- ServiceConfig 已經具備足夠的資訊,開始對外暴露服務,預設監聽埠是 20880。
- 為了防止服務端退出,按任意鍵或者 ctrl-c 退出。
實現 Dubbo 服務呼叫方
|
說明:
- 建立一個 ReferenceConfig 的例項,同樣,泛型引數資訊是服務介面型別,即 GreetingService。
- 生成一個 AplicatonConfig 的例項,並將其裝配進 ReferenceConfig。
- 生成一個 RegistryConfig 例項,並將其裝配進 ReferenceConfig,注意這裡的組播地址資訊需要與服務提供方的相同。
- 將服務契約 GreetingsService 裝配進 ReferenceConfig。
- 從 ReferenceConfig 中獲取到 GreetingService 的代理。
- 通過 GreetingService 的代理髮起遠端呼叫,傳入的引數為 dubbo。
- 列印返回結果 hi, dubbo。
執行
完整的示例在 https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-api 上提供。在完整的示例中,由於配置了 exec-maven-plugin,可以很方便的在命令列下通過 maven 的方式執行。當然,您也可以在 IDE 裡直接執行,但是需要注意的是,由於使用了組播的方式來發現服務,執行時需要指定 -Djava.net.preferIPv4Stack=true。
構建示例
通過以下的命令來同步示例程式碼並完成構建:
|
當看到 BUILD SUCCESS 的時候表明構建完成,下面就可以開始進入執行階段了。
執行服務端
通過執行以下的 maven 命令來啟動服務提供者:
|
當 first-dubbo-provider is running. 出現時,代表服務提供者已經啟動就緒,等待客戶端的呼叫。
執行客戶端
通過執行以下的 maven 命令來呼叫服務:
|
可以看到, hi, dubbo 是從服務提供者返回的執行結果。
快速生成 Dubbo 應用
Dubbo 還提供了一個公共服務快速搭建基於 Spring Boot 的 Dubbo 應用。訪問 http://start.dubbo.io 並按照下圖所示來生成示例工程:
dubbo initializr.png | left | 827x835
說明:
- 在 Group 中提供 maven groupId,預設值是 com.example。
- 在 Artifact 中提供 maven artifactId,預設值是 demo。
- 在 DubboServiceName 中提供服務名,預設值是 com.example.HelloService。
- 在 DubboServiceVersion 中提供服務的版本,預設值是 1.0.0。
- 在 Client/Server 中選取本次構建的工程是服務提供者 (Server) 還是服務消費者 (Client),預設值是 server。
- 使用 embeddedZookeeper 作為服務註冊發現,預設為勾選。
- 是否啟用 qos 埠,預設為不勾選,如果勾選可以通過 22222 埠訪問。
- 點選 Generate Project 即可下載生成好的工程。
在本例中展示的是服務提供者,同樣的,通過在生成介面選取 client 來生成對應的服務消費者。
執行
用 IDE 開啟生成好的工程,可以發現應用是一個典型的 Spring Boot 應用。程式的入口如下所示:
|
說明:
- 在 2181 埠上啟動嵌入式 ZooKeeper。
- 啟動 Spring Boot 上下文。
可以直接在 IDE 中執行,輸出結果如下:
bash
2018-05-28 16:59:38.072 INFO 59943 --- [ main] a.b.d.c.e.WelcomeLogoApplicationListener :
████████▄ ███ █▄ ▀█████████▄ ▀█████████▄ ▄██████▄
███ ▀███ ███ ███ ███ ███ ███ ███ ███ ███
███ ███ ███ ███ ███ ███ ███ ███ ███ ███
███ ███ ███ ███ ▄███▄▄▄██▀ ▄███▄▄▄██▀ ███ ███
███ ███ ███ ███ ▀▀███▀▀▀██▄ ▀▀███▀▀▀██▄ ███ ███
███ ███ ███ ███ ███ ██▄ ███ ██▄ ███ ███
███ ▄███ ███ ███ ███ ███ ███ ███ ███ ███
████████▀ ████████▀ ▄█████████▀ ▄█████████▀ ▀██████▀
:: Dubbo Spring Boot (v0.1.0) : https://github.com/dubbo/dubbo-spring-boot-project
:: Dubbo (v2.0.1) : https://github.com/alibaba/dubbo
:: Google group : http://groups.google.com/group/dubbo
2018-05-28 16:59:38.079 INFO 59943 --- [ main] e.OverrideDubboConfigApplicationListener : Dubbo Config was overridden by externalized configuration {dubbo.application.name=dubbo-demo-server, dubbo.application.qosAcceptForeignIp=false, dubbo.application.qosEnable=true, dubbo.application.qosPort=22222, dubbo.registry.address=zookeeper://localhost:2181?client=curator, dubbo.registry.id=my-registry, dubbo.scan.basePackages=com.example} #1
...
2018-05-28 16:59:39.624 INFO 59943 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.746 seconds (JVM running for 2.963)
說明:
- 輸出中列印的以 dubbo. 開頭的配置資訊,定義在 main/resources/application.properties 中。
通過 Telnet 管理服務
生成工程的時候如果選擇了啟用 qos 的話,就可以通過 telnet 或者 nc 來管理服務、檢視服務狀態。
bash
$ telnet localhost 22222
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
████████▄ ███ █▄ ▀█████████▄ ▀█████████▄ ▄██████▄
███ ▀███ ███ ███ ███ ███ ███ ███ ███ ███
███ ███ ███ ███ ███ ███ ███ ███ ███ ███
███ ███ ███ ███ ▄███▄▄▄██▀ ▄███▄▄▄██▀ ███ ███
███ ███ ███ ███ ▀▀███▀▀▀██▄ ▀▀███▀▀▀██▄ ███ ███
███ ███ ███ ███ ███ ██▄ ███ ██▄ ███ ███
███ ▄███ ███ ███ ███ ███ ███ ███ ███ ███
████████▀ ████████▀ ▄█████████▀ ▄█████████▀ ▀██████▀
dubbo>
dubbo>ls
As Provider side:
+------------------------------+---+
| Provider Service Name |PUB|
+------------------------------+---+
|com.example.HelloService:1.0.0| Y |
+------------------------------+---+
As Consumer side:
+---------------------+---+
|Consumer Service Name|NUM|
+---------------------+---+
目前 qos 支援以下幾個命令,更詳細的資訊請查閱官方文件[4]:
- ls:列出消費者、提供者資訊
- online:上線服務
- offline:下線服務
- help:聯機幫助
總結
在本文中,從 RMI 開始,介紹了 Java 領域分散式呼叫的基本概念,也就是基於介面程式設計、通過代理將遠端呼叫偽裝成本地、通過註冊中心完成服務的註冊和發現。
然後為了簡單起見,使用簡單的組播註冊方式和直接面向 Dubbo API 程式設計的方式介紹瞭如何開發一個 Dubbo 的完整應用。深入的瞭解 ServiceConfig 和 ReferenceConfig 的用法,對於進一步的使用 Spring XML 配置、乃至 Spring Boot 的程式設計方式有這很大的幫助。
最後,簡單的介紹瞭如何通過 Dubbo 團隊提供的公共服務 start.dubbo.io 快速搭建基於 Spring Boot 的 Dubbo 應用,並通過 qos 來做 Dubbo 服務的簡單運維。
----轉自