微服務8:通訊之RPC實踐篇(附原始碼)

Brand發表於2022-03-22

★微服務系列

微服務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 基於消費端的自動服務發現能力,其基本工作原理如下圖:
image

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 等控制檯提供運維能力(如服務測試、介面文件等)
    • 作為服務發現機制的補充,提供額外的介面/方法級別配置資訊的同步能力,相當於註冊中心的額外擴充套件。
      image

2.6 高可擴充套件性

Dubbo 通過 SPI 機制提供了非常靈活的可擴充套件性

3 Dubbo實現簡易的RPC通訊

3.1 框架依賴

image

  • 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
    image

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]

呼叫效果:
image

3.3.5 原始碼參考

GitHub地址:https://github.com/WengZhiHua/Helenlyn.Grocery/tree/master/parent

4 總結

RPC通訊只是 Dubbo 功能的一部分,像上面介紹的那樣,除了RPC通訊之外,還有服務註冊與發現、配置管理、服務流量治理等,基本能達到一個微服務的基本能力要求。
另外我們還看到,除了Dubbo之外,百度的brpc和Google的gRPC也有很大的受眾,也是不錯的選擇,我們上一章有對主流RPC框架做了對比和分析,可以根據自身的業務特性進行技術選型。

相關文章