1. 程式人生 > >從零寫分散式RPC框架 系列 第一版 (1)架構設計

從零寫分散式RPC框架 系列 第一版 (1)架構設計

本系列文章的目的是搭建出一個基於Netty,Zookeeper和SpringBoot的簡易分散式RPC框架,並且釋出到Maven中央倉庫以 spring-boot-starter 的形式對外提供開箱即用的服務。1.0 版本使用 protobuf 來做序列化,最終的使用形式比較接近於 Dubbo 。最終效果可見:https://github.com/linshenkx/rpc-netty-spring-boot-starter

文章目錄

一 RPC設計

如下圖,本RPC框架主要有Registry、RPC Server 和 RPC Client 3個角色。

  • Registry
    註冊中心,底層實現是Zookeeper,可藉助ZK叢集達到服務的高可用
  • RPC Server
    RPC伺服器,即服務提供方,通過自定義的協議而非http介面對外提供服務實現
    需要向Registry註冊服務資訊和自身資訊
  • RPC Client
    RPC客戶端,即服務消費者,通過向Registry訂閱服務,獲取提供對應服務實現的RPC Server資訊,從而通過自定義協議從RPC Server獲取服務實現。

注意:本框架1.0版本未完善 RPC Client 和 Registry 的釋出訂閱模型,所以每次都需要 RPC Client去 Registry 獲取資訊再 向 RPC Server 發起服務呼叫,也由此帶來極大的效能損耗和浪費。1.0版本只是實現了一個框架原型,後續版本將對效能等做優化。

設計

二 模組結構

結構圖如下,其中 examples 模組不依賴於父工程,可獨立執行,不屬於框架的一部分。
結構圖
rpc-netty-spring-boot-starter 下有5個模組,其中外部使用的是兩個 starter ,而真正工作的事另外三個模組。

  • rpc-netty-common

    封裝統一規則,如使RPC Server和RPC Client 可以基於同一協議通訊。
    內含 自定義RPC通訊物件及序列化方法,還有對應的Netty編碼器、解碼器等。
  • rpc-netty-server-spring-boot-autoconfigure
    • 核心類是RpcServer,負責提供RPC服務
    • 內含 ZKServiceRegistry 負責向zk叢集註冊服務,@RpcService 註解對外提供使用
    • 另外還有 RpcServerHandler 提供Netty 通訊處理,ZKProperties 和 RpcServerProperties 提供屬性注入。
  • rpc-netty-server-spring-boot-starter
    對 rpc-netty-server-spring-boot-autoconfigure 進行包裝,本身無功能實現
  • rpc-netty-client-spring-boot-autoconfigure
    • 核心類是RpcClient,負責獲取服務實現生成代理類
    • 內含 ZKServiceDiscovery 負責發現指定服務的RPC Server資訊
    • 另外還有 RpcClientHandler 提供Netty 通訊處理,ZKProperties 提供屬性注入。
  • rpc-netty-client-spring-boot-starter
    對rpc-netty-client-spring-boot-autoconfigure 進行包裝,本身無功能實現

三 工作流程

RPC Server

需要注意,RPC Server需一直維持與zk叢集的連線,如果關閉RPC Server,zk上註冊的資訊會隨之清除。

RPC Client

需要注意,這裡沒有使用釋出訂閱模型,沒有維持連線,所以每次使用都需要去獲取服務實現,沒有使用池化技術和快取。需要在後續版本改良。

四 功能依賴

  • rpc-netty-common 模組
    • protostuff-core :Rptostuff 核心模組,提供物件序列化和反序列化功能
    • protostuff-runtime:Protostuff 執行時模組,用於生成所需的 Protostuff Schema 物件
    • objenesis:提供比 JDK 更高效的反射功能,用於物件反序列化
  • rpc-netty-server-spring-boot-autoconfigure 和 rpc-netty-server-spring-boot-autoconfigure 模組
    • spring-boot-starter:提供spring-boot 基礎服務,如 spring-context 資訊等,該依賴scope 應為 provided ,由使用方提供依賴,避免版本衝突
    • spring-boot-configuration-processor:提供屬性注入,scope同上
    • rpc-netty-common:提供統一服務
    • zkclient :提供對zk叢集的操作,注意應使用 exclusion 將其 slf4j-log4j12 模組排除在classpath,否則會與spring-boot日誌框架重複或與Lombok的@log4j2衝突

除了上述依賴,還有以上3個模組都依賴的

  • netty-all :Netty模組,提供NIO通訊所需的API,是整個RPC框架的基礎
  • lombok:其 optional 應為true ,避免傳遞依賴

另外,兩個spring-boot-starter分別只依賴各自的spring-boot-autoconfigure模組

注意,為了避免依賴衝突問題,這裡使用到了 scope設為provided,exclusion排除模組, optional設為true 三種方法。詳情請見:provided,optional 和 exclusion 最全區分指南

  1. scope設為provided:對依賴整體有效,依賴將提供編譯而不參與打包,由使用方提供
    在本專案中,則是在釋出後,由使用 spring-boot-starter 的spring-boot工程提供。
  2. exclusion排除特定元件:避免傳遞依賴
    本專案中,用於移去 zkclient 的 slf4j-log4j12 模組,避免依賴匯入
  3. optional設為true:避免傳遞依賴
    本專案中,rpc-netty-server-spring-boot-autoconfigure依賴optional為true的 Lombok ,但 rpc-netty-server-spring-boot-starter則不含 Lombok 模組,避免依賴傳出

五 父工程pom

這裡我把部署到maven 中央倉庫的內容刪去,不影響正常使用,感興趣的可以到GitHub找完整專案檢視,部署的中央倉庫的方法見:使用gpg外掛釋出jar包到Maven中央倉庫 完整實踐

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.github.linshenkx</groupId>
    <artifactId>rpc-netty-spring-boot-starter</artifactId>
    <version>1.0.5.RELEASE</version>
    <packaging>pom</packaging>

    <name>rpc-netty-spring-boot-starter</name>
    <description>基於Netty的簡易RPC框架</description>
    <url>https://github.com/linshenkx/rpc-netty-spring-boot-starter</url>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-boot.version>2.1.0.RELEASE</spring-boot.version>
        <spring-cloud.version>Finchley.SR2</spring-cloud.version>
        <netty.version>4.1.31.Final</netty.version>
        <protostuff.version>1.5.9</protostuff.version>
        <objenesis.version>3.0.1</objenesis.version>
        <zkclient.version>0.11</zkclient.version>
    </properties>

    <modules>
        <module>rpc-netty-common</module>
        <module>rpc-netty-server-spring-boot-autoconfigure</module>
        <module>rpc-netty-server-spring-boot-starter</module>
        <module>rpc-netty-client-spring-boot-autoconfigure</module>
        <module>rpc-netty-client-spring-boot-starter</module>
    </modules>
   
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
                <version>${spring-boot.version}</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <version>${spring-boot.version}</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>com.github.linshenkx</groupId>
                <artifactId>rpc-netty-common</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>com.github.linshenkx</groupId>
                <artifactId>rpc-netty-server-spring-boot-autoconfigure</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>com.github.linshenkx</groupId>
                <artifactId>rpc-netty-client-spring-boot-autoconfigure</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>io.netty</groupId>
                <artifactId>netty-all</artifactId>
                <version>${netty.version}</version>
            </dependency>
            <dependency>
                <groupId>io.protostuff</groupId>
                <artifactId>protostuff-core</artifactId>
                <version>${protostuff.version}</version>
            </dependency>
            <dependency>
                <groupId>io.protostuff</groupId>
                <artifactId>protostuff-runtime</artifactId>
                <version>${protostuff.version}</version>
            </dependency>
            <dependency>
                <groupId>org.objenesis</groupId>
                <artifactId>objenesis</artifactId>
                <version>${objenesis.version}</version>
            </dependency>
            <dependency>
                <groupId>com.101tec</groupId>
                <artifactId>zkclient</artifactId>
                <version>${zkclient.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-log4j12</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
</project>

其他

參考資料:
專案GitHub主頁:https://github.com/linshenkx/rpc-netty-spring-boot-starter
《架構探險 輕量級微服務架構 下冊》黃勇 著,第四章-微服務通訊
Dubbo GitHub主頁:https://github.com/apache/incubator-dubbo
https://github.com/luxiaoxun/NettyRpc
https://github.com/yidongnan/grpc-spring-boot-starter