微服務8:通信之RPC實踐篇(附源碼)
★微服務系列
微服務1:微服務及其演進史
微服務2:微服務全景架構
微服務3:微服務拆分策略
微服務4:服務註冊與發現
微服務5:服務註冊與發現(實踐篇)
微服務6:通信之網關
微服務7:通信之RPC
微服務8:通信之RPC實踐篇(附源碼)
1 說明
上一節我們我們詳細學習了RPC的概念和原理,以及它能夠提供的能力。也對目前業內主流的RPC的框架有了一定的了解。接下來以Dobbo為例子,來學習下怎麼使用RPC框架來進行服務之間的通信。
2 Dubbo框架功能介紹
Apache Dubbo 是一款分佈式微服務開發框架,它提供了 RPC通信 與 微服務治理 兩大關鍵能力。這意味着,使用 Dubbo 開發的微服務,將具備相互之間的遠程發現與通信能力, 同時利用 Dubbo 提供的豐富服務治理能力,可以實現諸如服務發現、負載均衡、流量調度等服務治理訴求。同時 Dubbo 是高度可擴展的,用戶幾乎可以在任意功能點去定製自己的實現,以改變框架的默認行為來滿足自己的業務需求。
2.1 服務發現
服務發現,即消費端自動發現服務地址列表的能力,是微服務框架需要具備的關鍵能力,藉助於自動化的服務發現,微服務之間可以在無需感知對端部署位置與 IP 地址的情況下實現通信。
實現服務發現的方式有很多種,Dubbo 提供的是一種 Client-Based 的服務發現機制,通常還需要部署額外的第三方註冊中心組件來協調服務發現過程,如常用的 Nacos、Consul、Zookeeper 等,Dubbo 自身也提供了對多種註冊中心組件的對接,用戶可以靈活選擇。
Dubbo 基於消費端的自動服務發現能力,其基本工作原理如下圖:
2.2 RPC通信
Dubbo3 提供了 Triple(Dubbo3)、Dubbo2 協議,這是 Dubbo 框架的原生協議。除此之外,Dubbo3 也對眾多第三方協議進行了集成,並將它們納入 Dubbo 的編程與服務治理體系, 包括 gRPC、Thrift、JsonRPC、Hessian2、REST 等。
2.3 服務流量管理
通過 Dubbo 定義的路由規則,實現對流量分佈的控制,可以實現 A/B測試、金絲雀發佈、藍綠發佈等能力。
2.4 配置管理
描述 Dubbo 支持的配置,Dubbo 的動態配置能力,包含幾大類: 啟動階段配置項、服務治理規則、動態配置項。
2.5 部署架構(註冊、配置、元數據中心)
作為一個微服務框架,Dubbo sdk 跟隨着微服務組件被部署在分佈式集群各個位置,為了在分佈式環境下實現各個微服務組件間的協作, Dubbo 定義了一些中心化組件,這包括:
- 註冊中心。協調 Consumer 與 Provider 之間的地址註冊與發現
- 配置中心。
- 存儲 Dubbo 啟動階段的全局配置,保證配置的跨環境共享與全局一致性
- 負責服務治理規則(路由規則、動態配置等)的存儲與推送。
- 元數據中心。
- 接收 Provider 上報的服務接口元數據,為 Admin 等控制台提供運維能力(如服務測試、接口文檔等)
- 作為服務發現機制的補充,提供額外的接口/方法級別配置信息的同步能力,相當於註冊中心的額外擴展。
2.6 高可擴展性
Dubbo 通過 SPI 機制提供了非常靈活的可擴展性
3 Dubbo實現簡易的RPC通信
3.1 框架依賴
- Protocol, Proxy, Service, Container, Registry, Monitor 代表層或模塊,藍色的表示與業務的交互,綠色表示 Dubbo 內部的交互。
- 主模塊 RPC Consumer,RPC Provider, Registry, Monitor 代表部署邏輯拓撲節點。
- 藍色虛線為init初始化時調用,紅色虛線為async運行時異步調用,紅色實線為sync運行時同步調用。
- Remoting 過程整體都隱含在 Protocol 中。
3.2 核心角色說明
- Provider 暴露服務的服務提供方
- Consumer 調用遠程服務的服務消費方(負載均衡)
- Registry 服務註冊與發現的註冊中心(監控、心跳、踢出、重入)
- Monitor 服務消費者和提供者在內存中累計調用次數和調用時間,主動定時每分鐘發送一次統計數據到監控中心。
- Service 服務:執行遠程調用、數據序列化
3.3 在 SpringBoot 中實踐
3.3.1 父項目中引入依賴
首先引入 Zookeeper 和 Dubbo的依賴,Zookeeper的作用是註冊與發現,Dubbo的作用是引入RPC通信核心能力
<!-- 包含了Zookeeper和Dubbo依賴 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
3.3.2 創建module
父項目下面創建三個Module,一個通用模塊,一個服務提供者模塊,一個服務消費者模塊
- RpcCommon
- RpcProvider
- RpcConsume
3.3.3 通用庫 RpcCommon
提供了公用的實體和接口,比如這邊包含了一個用戶信息的實體類和用戶信息接口,後面的服務提供者和消費這都可以引用:
/**
* @author brand
* @Description: 用戶信息實體
* @Copyright: Copyright (c) 2022
* @Company: Helenlyn, Inc. All Rights Reserved.
* @date 2022/3/5 下午3:59
* @Update Time:
* @Updater:
* @Update Comments:
*/
@Getter
@Setter
public class UserInfo implements Serializable {
private Integer userId;
private String userName;
private Integer age;
private String sex;
}
/**
* @author brand
* @Description: 用戶信息接口
* @Copyright: Copyright (c) 2022
* @Company: Helenlyn, Inc. All Rights Reserved.
* @date 2022/3/5 下午5:29
* @Update Time:
* @Updater:
* @Update Comments:
*/
public interface UserInfoService {
UserInfo getUserInfo (int userId) ;
String getHello (int userId) ;
}
3.3.3 服務提供者 RpcProvider
yml文件中的配置信息如下,可以看到我配置的zookeeper地址是127.0.0.1:2181,這是我本地部署到Zookeeper服務,大家可以對應修改下。
scan屬性指的是暴露服務的位置,對應位置下的類,只要聲明 Service 註解,就會被掃描並暴露出去。
# Dubbo Provider 配置
dubbo:
application:
name: rpc-provider # 發佈的dubbo服務名稱
registry:
address: 127.0.0.1:2181 # 使用Zookeeper註冊中心提供的服務地址,這邊可以配置多個,逗號隔開
protocol: zookeeper
protocol:
name: dubbo
port: 20882 # 用dubbo協議在20882端口暴露服務
scan: # 使用註解方式暴露接口,掃描的位置
base-packages: rpcprovider.modules.service
然後編寫需要暴露出去的接口的實現,這邊需要注意類上引入的註解為com.alibaba.dubbo.config.annotation的Service,
而不是springframework包中的service,這樣Service服務才能被註冊到dubbo中:
package rpcprovider.modules.service;
import com.alibaba.dubbo.config.annotation.Service;
import rpccommon.dto.UserInfo;
import rpccommon.service.UserInfoService;
/**
* @author brand
* @Description:
* @Copyright: Copyright (c) 2022
* @Company: Helenlyn, Inc. All Rights Reserved.
* @date 2022/3/5 下午5:43
* @Update Time:
* @Updater:
* @Update Comments:
*/
@Service
public class UserInfoServiceImpl implements UserInfoService {
@Override
public UserInfo getUserInfo(int userId) {
UserInfo userInfo = new UserInfo();
userInfo.setUserId(userId);
userInfo.setAge(18);
userInfo.setSex("男");
userInfo.setUserName("Brand");
return userInfo;
}
@Override
public String getHello(int userId) {
return " Hello, " + userId;
}
}
啟動起來看看Zookeeper的效果,是不是被註冊到Dubbo中,這邊可以用看到,Provider中已經有註冊節點了。
[zk: 127.0.0.1:2181(CONNECTED) 68] ls /
[dubbo, zookeeper]
[zk: 127.0.0.1:2181(CONNECTED) 69] ls /dubbo
[rpccommon.service.UserInfoService]
[zk: 127.0.0.1:2181(CONNECTED) 70] ls /dubbo/rpccommon.service.UserInfoService
[configurators, consumers, providers, routers]
[zk: 127.0.0.1:2181(CONNECTED) 71] ls /dubbo/rpccommon.service.UserInfoService/providers
[dubbo%3A%2F%2F172.21.213.159%3A20882%2Frpccommon.service.UserInfoService%3Fanyhost%3Dtrue%26application%3Drpc-provider%26dubbo%3D2.6.2%26generic%3Dfalse%26interface%3Drpccommon.service.UserInfoService%26methods%3DgetHello%2CgetUserInfo%26pid%3D60138%26side%3Dprovider%26timestamp%3D1646535565910]
[zk: 127.0.0.1:2181(CONNECTED) 72]
3.3.4 消費者 RpcConsume
yml文件中的配置信息如下:
# Dubbo Consumer 配置文件
dubbo:
application:
name: rpc-consumer
registry:
address: 127.0.0.1:2181 # 使用Zookeeper註冊中心提供的服務地址來獲取服務,這邊可以多個逗號隔開
protocol: zookeeper
消費者使用註解方式進行RPC調用,這邊注意,通過@Reference註解, dubbo會在掃描的時候自動幫我們代理接口,然後通過rpc調用遠程服務。如下:
package rpcconsume.modules.service;
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
import rpccommon.dto.UserInfo;
import rpccommon.service.UserInfoService;
/**
* @author brand
* @Description: 消費者使用註解方式進行RPC調用
* @Copyright: Copyright (c) 2022
* @Company: Helenlyn, Inc. All Rights Reserved.
* @date 2022/3/5 下午6:49
* @Update Time:
* @Updater:
* @Update Comments:
*/
@Service
public class UserInfoConsumer implements UserInfoService {
@Reference
private UserInfoService userInfoService ;
@Override
public UserInfo getUserInfo(int userId) {
return userInfoService.getUserInfo(userId);
}
@Override
public String getHello(int userId) {
return userInfoService.getHello(userId);
}
}
寫一個Controller,通過外部調用,來測試效果
package rpcconsume.modules.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import rpccommon.dto.UserInfo;
import rpcconsume.modules.service.UserInfoConsumer;
/**
* @author brand
* @Description: 測試消費者效果
* @Copyright: Copyright (c) 2022
* @Company: Helenlyn, Inc. All Rights Reserved.
* @date 2022/3/5 下午6:48
* @Update Time:
* @Updater:
* @Update Comments:
*/
@RestController
@Slf4j
@RequestMapping("/v1.0/consumer")
public class ConsumerController {
@Autowired
private UserInfoConsumer userInfoConsumer ;
/**
* 獲取用戶信息
* @return
*/
@RequestMapping(value = "/userinfo/{user_id}", method = RequestMethod.GET)
public UserInfo getUserInfo(@PathVariable("user_id") int userId) {
return userInfoConsumer.getUserInfo(userId);
}
/**
* 獲取問候信息
* @return
*/
@RequestMapping(value = "/hello/{user_id}", method = RequestMethod.GET)
public String getHello(@PathVariable("user_id") int userId) {
return userInfoConsumer.getHello(userId);
}
}
查一下Zookeeper 上的消費者信息:
[zk: 127.0.0.1:2181(CONNECTED) 73] ls /dubbo/rpccommon.service.UserInfoService/consumers
[consumer%3A%2F%2F172.21.213.159%2Frpccommon.service.UserInfoService%3Fapplication%3Drpc-consumer%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.6.2%26interface%3Drpccommon.service.UserInfoService%26methods%3DgetHello%2CgetUserInfo%26pid%3D60884%26side%3Dconsumer%26timestamp%3D1646536367371]
調用效果:
3.3.5 源代碼參考
GitHub地址://github.com/WengZhiHua/Helenlyn.Grocery/tree/master/parent
4 總結
RPC通信只是 Dubbo 功能的一部分,像上面介紹的那樣,除了RPC通信之外,還有服務註冊與發現、配置管理、服務流量治理等,基本能達到一個微服務的基本能力要求。
另外我們還看到,除了Dubbo之外,百度的brpc和Google的gRPC也有很大的受眾,也是不錯的選擇,我們上一章有對主流RPC框架做了對比和分析,可以根據自身的業務特性進行技術選型。