gRPC實(shí)踐之proto及Maven插件概念及使用詳解
一. 前言
文章合集 : ??http://shouce.jb51.net/java/jdk1.6/
Github : ?? github.com/black-ant
CASE 備份 : ?? gitee.com/antblack/ca…
這一塊在學(xué)習(xí)的初期沒專門去看,導(dǎo)致寫Demo的時候一度遇到了很多的困難。
protobuf 是 gRPC 中生成 API Service 的方式,在 Maven 編譯時就可以快速生成對應(yīng)的Class類。
二. proto 的概念和使用
以 Maven 為例,在 gRPC 中通常使用 protobuf-maven-plugin 插件,根據(jù)一個 .proto 文件生成對應(yīng)的 Java 代碼。 其本身是基于 Google 的 Protocol Buffers 工具鏈實(shí)現(xiàn)的。
2.1 具體的執(zhí)行流程
- S1 : 創(chuàng)建一個 .proto 文件,按照規(guī)范要求定義元數(shù)據(jù)內(nèi)容
- S2 : 通過
protobuf-maven-plugin
插件進(jìn)行 compile 操作 - S3 :
protobuf-maven-plugin
調(diào)用protoc
命令行工具,生成對應(yīng)的 Java 代碼 - S4 : 生成的 Java 代碼會被編譯,并打包成 Jar 包 , 最終生成可執(zhí)行文件給項(xiàng)目使用
.proto 文件格式
syntax = "proto3"; // 定義使用的語法版本,這里是proto3 package com.example.grpc; // 定義package,用于在其他文件中引用該文件定義的類型 // 定義配置項(xiàng) // - java_package :定義生成的Java類的包名 // - java_outer_classname : 定義生成的Java類的類名 option java_multiple_files = true; // 接口類 service UserService { rpc query (UserRequest) returns (UserResponse); } // 定義消息類型 message UserRequest { string name = 1; // 字段類型和名稱,以及字段標(biāo)識符,用于序列化和反序列化 } // 這里的消息類型是返回值 message UserResponse { string name = 1; int32 age = 2; string address = 3; // 這里定義一個枚舉 enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } // 這里定義一個內(nèi)部對象 repeated PhoneNumber phones = 4; message PhoneNumber { string number = 1; PhoneType type = 2; } }
通常一個 proto 文件中會包含以下主要內(nèi)容
- package: 包名,用于區(qū)分不同模塊的數(shù)據(jù)結(jié)構(gòu)定義。
- message: 消息體,包含多個字段,每個字段都有一個名稱和類型。
- enum: 枚舉類型,定義多個枚舉值。
- service: 定義 RPC 服務(wù)接口,包含多個方法,每個方法都有一個請求消息和一個響應(yīng)消息。
生成一個 .class 文件
這里就可以看到,其實(shí)生成了很多文件,最重要的包括 :UserServiceGrpc ,HelloRequest 和 HelloResponse 這幾個。
使用文件
使用文件和平時使用 Class 是一致的,引入 Maven 依賴然后直接調(diào)用就行了
@Resource UserServiceGrpc.UserServiceBlockingStub userService; UserRequest userRequest = UserRequest.newBuilder().setName("test").build(); UserResponse user = userService.query(userRequest);
三. 生成Java文件的核心類
上面看了插件的使用,后面就來詳細(xì)看看它生成的幾個 Class 到底包含了什么 :
3.1 grpc 類
以 UserServiceGrpc 為例 ,整個類包含這些核心方法 :
getQueryMethod : 這個對應(yīng) proto 文件中的 query 方法
// 注解中就標(biāo)注了請求的路徑和請求體,返回體 // 沒看 grpc 源碼,但是猜測里面會通過這個注解構(gòu)建請求 @io.grpc.stub.annotations.RpcMethod( fullMethodName = SERVICE_NAME + '/' + "query", // 這個就對應(yīng)著 .proto 中配置的 message 對象 requestType = com.example.grpc.UserRequest.class, responseType = com.example.grpc.UserResponse.class, methodType = io.grpc.MethodDescriptor.MethodType.UNARY) // 這里返回的實(shí)際上是一個 MethodDescriptor 對象 public static io.grpc.MethodDescriptor getQueryMethod() { io.grpc.MethodDescriptor getQueryMethod; // Spring 注入的時候應(yīng)該是沒有多線程風(fēng)險的,這個可能是為了其他加載方式準(zhǔn)備,上了一把鎖 // PS : 這里是一個經(jīng)典的單例模式,不過代碼太長被我屏蔽了 synchronized (UserServiceGrpc.class) { // 省略具體的build邏輯,這里設(shè)置了很多內(nèi)部參數(shù),方法名等等 UserServiceGrpc.getQueryMethod = getQueryMethod = io.grpc.MethodDescriptor.<....>newBuilder() // ... 省略 .build(); } return getQueryMethod; }
MethodDescriptor 是 gRPC 中的專屬對象,用于表示一個gRPC方法的描述。 其中包含了方法名,輸入輸出,請求流和響應(yīng)流等信息。
后續(xù)很多流程中都會通過該對象進(jìn)行請求Client的創(chuàng)建,和RPC方法的注冊。
其他 stub 對象
除了核心方法,其他比較主要的就是 stub 對象了。stub 對象用于向服務(wù)端發(fā)起 RPC 調(diào)用。
使用 gRPC 時,客戶端通過創(chuàng)建一個與服務(wù)端相同的 stub 對象來發(fā)起遠(yuǎn)程調(diào)用,封裝了底層的網(wǎng)絡(luò)通信細(xì)節(jié)。
UserServiceGrpc 中有一個 newStub 方法,同時對應(yīng)的還有 newBlockingStub 和 newFutureStub 。這些對象用于創(chuàng)建阻塞,異步回調(diào)的請求類型。
// 對應(yīng)的class對象里面方法大同小異 public static final class UserServiceStub extends io.grpc.stub.AbstractAsyncStub<UserServiceStub> { // 簡單的構(gòu)造器,這里傳入了一個 channel ,表示與服務(wù)端通信的通道 private UserServiceStub( io.grpc.Channel channel, io.grpc.CallOptions callOptions) { super(channel, callOptions); } // 調(diào)用 new 完成 build protected UserServiceStub build // 發(fā)起具體的 query 請求 public void query(com.example.grpc.UserRequest request, io.grpc.stub.StreamObserver<com.example.grpc.UserResponse> responseObserver)
3.2 DTO 和 Build 類
這里倒沒太多東西,主要是 DTO 和 DTOOrBuilder 。
DTO 類 (HelloRequest)
這個類比我們想的要大的多,一般我們認(rèn)為 DTO 里面主要是 Getter / Setter 這些方法 ,但是 proto 生成的 DTO 要復(fù)雜得多
// 容易讓人迷惑的方法 :
- getUnknownFields : 獲取消息中的未知字段 (解析器未知的字段)
- internalGetFieldAccessorTable : 獲取特定protobuf消息類型的字段訪問器表格
- isInitialized : 用于判斷一個對象是否已經(jīng)被完全初始化的方法 (避免字段未被初始化完全)
- getSerializedSize : 用于計(jì)算當(dāng)前消息的序列化后的大小 , 用于預(yù)留緩沖空間
- parseFrom : 用于將二進(jìn)制數(shù)據(jù)解析成 Protobuf 消息對象
- parseFrom(byte[] data):從字節(jié)數(shù)組解析 Protobuf 消息對象
- parseFrom(byte[] data, int off, int len):從字節(jié)數(shù)組的指定位置和長度解析 Protobuf 消息對象
- parseFrom(InputStream input):從輸入流解析 Protobuf 消息對象
- parseFrom(CodedInputStream input):從 CodedInputStream 對象解析 Protobuf 消息對象
- newBuilderForType : 創(chuàng)建當(dāng)前消息類型的 Builder 對象,用于構(gòu)建該類型的消息
- toBuilder :用于返回一個可以修改消息的構(gòu)建器對象 , 配合 newBuilder 一起使用
- parser : 接收一個字節(jié)數(shù)組或者一個輸入流作為參數(shù),并返回一個對應(yīng)的消息對象
DTOOrBuilder (HelloRequestOrBuilder)
在不需要修改消息屬性的場景下,提供一種更輕量級的消息訪問方式
public interface UserRequestOrBuilder extends // @@protoc_insertion_point(interface_extends:com.example.grpc.UserRequest) com.google.protobuf.MessageOrBuilder { /** * <code>string name = 1;</code> */ java.lang.String getName(); /** * <code>string name = 1;</code> */ com.google.protobuf.ByteString getNameBytes(); }
UserServiceOuterClass
OuterClass 是 proto 文件中定義的一個特殊類,用于包含所有其他消息和服務(wù)定義,可以作為訪問對應(yīng)類的入口點(diǎn)。
example.OuterClass.Person person = example.OuterClass.Person.newBuilder() .setName("Alice") .setId(123) .build();
總結(jié)
了解 proto 插件是使用 gRPC 得基礎(chǔ)。一開始我是一直沒搞懂這個 Request 類是怎么出來了,一直引入失敗。
后續(xù)就開始一片片得深入 gRPC 的使用。
以上就是gRPC實(shí)踐之proto及Maven插件概念及使用詳解的詳細(xì)內(nèi)容,更多關(guān)于gRPC proto Maven插件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java中使用print、printf、println的示例及區(qū)別
Java?的輸出方式一般有這三種,print、println、printf,它們都是?java.long?包里的System類中的方法,本文重點(diǎn)給大家介紹Java中使用print、printf、println的示例,需要的朋友可以參考下2023-05-05使用spring框架實(shí)現(xiàn)數(shù)據(jù)庫事務(wù)處理方式
這篇文章主要介紹了使用spring框架實(shí)現(xiàn)數(shù)據(jù)庫事務(wù)處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10由淺到深帶你詳談Java實(shí)現(xiàn)數(shù)組擴(kuò)容的三種方式
這篇文章主要詳細(xì)介紹了Java實(shí)現(xiàn)數(shù)組擴(kuò)容的三種方式,新建一個數(shù)組,把原來數(shù)組的內(nèi)容搬到新數(shù)組中,使用system.arraycopy(),使用java.util.Arrays.copyOf()這三種方式,具有一定的參考價值,需要的朋友可以借鑒一下2023-06-06Java 大小寫最快轉(zhuǎn)換方式實(shí)例代碼
這篇文章主要介紹了Java 大小寫最快轉(zhuǎn)換方式實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-07-07SpringCloud實(shí)現(xiàn)全鏈路灰度發(fā)布的示例詳解
灰度發(fā)布是指在軟件或服務(wù)發(fā)布過程中,將新版本的功能或服務(wù)以較小的比例引入到生產(chǎn)環(huán)境中,僅向部分用戶或節(jié)點(diǎn)提供新功能的一種發(fā)布策略,下面我們就來學(xué)習(xí)一下SpringCloud如何實(shí)現(xiàn)全鏈路灰度發(fā)布2023-11-11java計(jì)算任意位水仙花數(shù)示例(回文數(shù))
這篇文章主要介紹了java計(jì)算任意位水仙花數(shù)示例(回文數(shù)),需要的朋友可以參考下2014-05-05