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的時(shí)候一度遇到了很多的困難。
protobuf 是 gRPC 中生成 API Service 的方式,在 Maven 編譯時(shí)就可以快速生成對(duì)應(yīng)的Class類。
二. proto 的概念和使用
以 Maven 為例,在 gRPC 中通常使用 protobuf-maven-plugin 插件,根據(jù)一個(gè) .proto 文件生成對(duì)應(yīng)的 Java 代碼。 其本身是基于 Google 的 Protocol Buffers 工具鏈實(shí)現(xiàn)的。
2.1 具體的執(zhí)行流程
- S1 : 創(chuàng)建一個(gè) .proto 文件,按照規(guī)范要求定義元數(shù)據(jù)內(nèi)容
- S2 : 通過
protobuf-maven-plugin插件進(jìn)行 compile 操作 - S3 :
protobuf-maven-plugin調(diào)用protoc命令行工具,生成對(duì)應(yīng)的 Java 代碼 - S4 : 生成的 Java 代碼會(huì)被編譯,并打包成 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)識(shí)符,用于序列化和反序列化
}
// 這里的消息類型是返回值
message UserResponse {
string name = 1;
int32 age = 2;
string address = 3;
// 這里定義一個(gè)枚舉
enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; }
// 這里定義一個(gè)內(nèi)部對(duì)象
repeated PhoneNumber phones = 4;
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
}
通常一個(gè) proto 文件中會(huì)包含以下主要內(nèi)容
- package: 包名,用于區(qū)分不同模塊的數(shù)據(jù)結(jié)構(gòu)定義。
- message: 消息體,包含多個(gè)字段,每個(gè)字段都有一個(gè)名稱和類型。
- enum: 枚舉類型,定義多個(gè)枚舉值。
- service: 定義 RPC 服務(wù)接口,包含多個(gè)方法,每個(gè)方法都有一個(gè)請(qǐng)求消息和一個(gè)響應(yīng)消息。
生成一個(gè) .class 文件

這里就可以看到,其實(shí)生成了很多文件,最重要的包括 :UserServiceGrpc ,HelloRequest 和 HelloResponse 這幾個(gè)。
使用文件
使用文件和平時(shí)使用 Class 是一致的,引入 Maven 依賴然后直接調(diào)用就行了
@Resource
UserServiceGrpc.UserServiceBlockingStub userService;
UserRequest userRequest = UserRequest.newBuilder().setName("test").build();
UserResponse user = userService.query(userRequest);
三. 生成Java文件的核心類
上面看了插件的使用,后面就來詳細(xì)看看它生成的幾個(gè) Class 到底包含了什么 :
3.1 grpc 類
以 UserServiceGrpc 為例 ,整個(gè)類包含這些核心方法 :
getQueryMethod : 這個(gè)對(duì)應(yīng) proto 文件中的 query 方法
// 注解中就標(biāo)注了請(qǐng)求的路徑和請(qǐng)求體,返回體
// 沒看 grpc 源碼,但是猜測(cè)里面會(huì)通過這個(gè)注解構(gòu)建請(qǐng)求
@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "query",
// 這個(gè)就對(duì)應(yīng)著 .proto 中配置的 message 對(duì)象
requestType = com.example.grpc.UserRequest.class,
responseType = com.example.grpc.UserResponse.class,
methodType = io.grpc.MethodDescriptor.MethodType.UNARY)
// 這里返回的實(shí)際上是一個(gè) MethodDescriptor 對(duì)象
public static io.grpc.MethodDescriptor getQueryMethod() {
io.grpc.MethodDescriptor getQueryMethod;
// Spring 注入的時(shí)候應(yīng)該是沒有多線程風(fēng)險(xiǎn)的,這個(gè)可能是為了其他加載方式準(zhǔn)備,上了一把鎖
// PS : 這里是一個(gè)經(jīng)典的單例模式,不過代碼太長(zhǎng)被我屏蔽了
synchronized (UserServiceGrpc.class) {
// 省略具體的build邏輯,這里設(shè)置了很多內(nèi)部參數(shù),方法名等等
UserServiceGrpc.getQueryMethod = getQueryMethod =
io.grpc.MethodDescriptor.<....>newBuilder()
// ... 省略
.build();
}
return getQueryMethod;
}
MethodDescriptor 是 gRPC 中的專屬對(duì)象,用于表示一個(gè)gRPC方法的描述。 其中包含了方法名,輸入輸出,請(qǐng)求流和響應(yīng)流等信息。
后續(xù)很多流程中都會(huì)通過該對(duì)象進(jìn)行請(qǐng)求Client的創(chuàng)建,和RPC方法的注冊(cè)。
其他 stub 對(duì)象
除了核心方法,其他比較主要的就是 stub 對(duì)象了。stub 對(duì)象用于向服務(wù)端發(fā)起 RPC 調(diào)用。
使用 gRPC 時(shí),客戶端通過創(chuàng)建一個(gè)與服務(wù)端相同的 stub 對(duì)象來發(fā)起遠(yuǎn)程調(diào)用,封裝了底層的網(wǎng)絡(luò)通信細(xì)節(jié)。
UserServiceGrpc 中有一個(gè) newStub 方法,同時(shí)對(duì)應(yīng)的還有 newBlockingStub 和 newFutureStub 。這些對(duì)象用于創(chuàng)建阻塞,異步回調(diào)的請(qǐng)求類型。
// 對(duì)應(yīng)的class對(duì)象里面方法大同小異
public static final class UserServiceStub extends io.grpc.stub.AbstractAsyncStub<UserServiceStub> {
// 簡(jiǎn)單的構(gòu)造器,這里傳入了一個(gè) channel ,表示與服務(wù)端通信的通道
private UserServiceStub(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
// 調(diào)用 new 完成 build
protected UserServiceStub build
// 發(fā)起具體的 query 請(qǐng)求
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)
這個(gè)類比我們想的要大的多,一般我們認(rèn)為 DTO 里面主要是 Getter / Setter 這些方法 ,但是 proto 生成的 DTO 要復(fù)雜得多

// 容易讓人迷惑的方法 :
- getUnknownFields : 獲取消息中的未知字段 (解析器未知的字段)
- internalGetFieldAccessorTable : 獲取特定protobuf消息類型的字段訪問器表格
- isInitialized : 用于判斷一個(gè)對(duì)象是否已經(jīng)被完全初始化的方法 (避免字段未被初始化完全)
- getSerializedSize : 用于計(jì)算當(dāng)前消息的序列化后的大小 , 用于預(yù)留緩沖空間
- parseFrom : 用于將二進(jìn)制數(shù)據(jù)解析成 Protobuf 消息對(duì)象
- parseFrom(byte[] data):從字節(jié)數(shù)組解析 Protobuf 消息對(duì)象
- parseFrom(byte[] data, int off, int len):從字節(jié)數(shù)組的指定位置和長(zhǎng)度解析 Protobuf 消息對(duì)象
- parseFrom(InputStream input):從輸入流解析 Protobuf 消息對(duì)象
- parseFrom(CodedInputStream input):從 CodedInputStream 對(duì)象解析 Protobuf 消息對(duì)象
- newBuilderForType : 創(chuàng)建當(dāng)前消息類型的 Builder 對(duì)象,用于構(gòu)建該類型的消息
- toBuilder :用于返回一個(gè)可以修改消息的構(gòu)建器對(duì)象 , 配合 newBuilder 一起使用
- parser : 接收一個(gè)字節(jié)數(shù)組或者一個(gè)輸入流作為參數(shù),并返回一個(gè)對(duì)應(yīng)的消息對(duì)象
DTOOrBuilder (HelloRequestOrBuilder)
在不需要修改消息屬性的場(chǎng)景下,提供一種更輕量級(jí)的消息訪問方式
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 文件中定義的一個(gè)特殊類,用于包含所有其他消息和服務(wù)定義,可以作為訪問對(duì)應(yīng)類的入口點(diǎn)。
example.OuterClass.Person person = example.OuterClass.Person.newBuilder() .setName("Alice") .setId(123) .build();
總結(jié)
了解 proto 插件是使用 gRPC 得基礎(chǔ)。一開始我是一直沒搞懂這個(gè) Request 類是怎么出來了,一直引入失敗。
后續(xù)就開始一片片得深入 gRPC 的使用。
以上就是gRPC實(shí)踐之proto及Maven插件概念及使用詳解的詳細(xì)內(nèi)容,更多關(guān)于gRPC proto Maven插件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java數(shù)據(jù)結(jié)構(gòu)之鏈表(動(dòng)力節(jié)點(diǎn)之Java學(xué)院整理)
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之鏈表(動(dòng)力節(jié)點(diǎn)之Java學(xué)院整理)的相關(guān)資料,需要的朋友可以參考下2017-04-04
每天練一練Java函數(shù)與算法Math函數(shù)總結(jié)與字符串轉(zhuǎn)換整數(shù)
這篇文章主要介紹了Java函數(shù)與算法Math函數(shù)總結(jié)與字符串轉(zhuǎn)換整數(shù),每天練一練,水平在不知不覺中提高,需要的朋友快過來看看吧2021-08-08
IntelliJ?IDEA社區(qū)版2021.3配置SpringBoot項(xiàng)目詳細(xì)教程及失敗案例
IntelliJ?IDEA?2021.3.3是一款集成開發(fā)環(huán)境,用于Java和其他編程語言的開發(fā),下面這篇文章主要給大家介紹了關(guān)于IntelliJ?IDEA社區(qū)版2021.3配置SpringBoot項(xiàng)目詳細(xì)教程及失敗案例的相關(guān)資料,需要的朋友可以參考下2024-03-03
關(guān)于Springboot2.x集成lettuce連接redis集群報(bào)超時(shí)異常Command timed out afte
這篇文章主要介紹了Springboot2.x集成lettuce連接redis集群報(bào)超時(shí)異常Command timed out after 6 second(s),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-03-03
教你如何用Eclipse創(chuàng)建一個(gè)Maven項(xiàng)目
這篇文章主要介紹了教你如何用Eclipse創(chuàng)建一個(gè)Maven項(xiàng)目,文中有非常詳細(xì)的代碼示例,對(duì)正在入門Java的小伙伴們是非常有幫助的喲,需要的朋友可以參考下2021-05-05
mybatis中foreach報(bào)錯(cuò):_frch_item_0 not found的解決方法
這篇文章主要給大家介紹了mybatis中foreach報(bào)錯(cuò):_frch_item_0 not found的解決方法,文章通過示例代碼介紹了詳細(xì)的解決方法,對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-06-06
詳解一個(gè)簡(jiǎn)單的Servlet容器的設(shè)計(jì)與實(shí)現(xiàn)
Servlet算是Java Web開發(fā)請(qǐng)求鏈路調(diào)用棧中底層的一個(gè)技術(shù),而了解一個(gè)Servlet容器的實(shí)現(xiàn)有助于更好的理解JavaWeb開發(fā),所以下面就來看看如何設(shè)計(jì)與實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Servlet容器吧2023-07-07

