gRPC 與 Protobuf 的深度集成 —— 從服務定義到多語言交互(Go + Java 示例)

在前幾篇文章中,我們已經掌握了 Protobuf 的基礎語法、高級特性和序列化反序列化操作。本篇文章將深入講解 gRPC 與 Protobuf 的集成,重點介紹如何通過 .proto 文件定義服務接口,并在 Go 和 Java 中實現 gRPC 服務與客戶端的完整交互流程。我們將通過詳細代碼示例分步解析,幫助你徹底掌握微服務架構中的通信設計。


一、gRPC 簡介與核心概念

1. 什么是 gRPC?

gRPC 是一個高性能、開源的遠程過程調用(RPC)框架,基于 HTTP/2 協議Protobuf 數據格式 構建。它支持多種語言,并提供了同步/異步調用流式通信等特性。

2. gRPC 的核心優勢

特性描述
高效通信基于二進制協議(Protobuf),比 JSON 更快、更小
多語言支持支持 Go、Java、Python、C++、Rust 等
雙向流式通信支持客戶端/服務端流式數據傳輸
自動代碼生成通過?.proto?文件自動生成客戶端和服務端代碼
強大的工具鏈提供調試工具(如?grpcurl)、插件系統

二、通過?.proto?定義 gRPC 服務

1. 示例?.proto?文件

syntax = "proto3";package user;//新版本有了下面的option go_package 這里的pacage就可以去掉了(當然留著也不影響)
option go_package = "/user;user"; // 指定生成的 Go 包路徑(生成源碼的路徑和包名,前面是路徑后面是包名,可以自己定義)
//option go_package = ".;user"; //這個可以生成在當前目錄下// 定義服務接口
service UserService {// 1. 單向調用(Unary RPC)rpc GetUser (GetUserRequest) returns (UserResponse);// 2. 服務端流式調用(Server Streaming)rpc ListUsers (ListUsersRequest) returns (stream UserResponse);// 3. 客戶端流式調用(Client Streaming)rpc CreateUsers (stream CreateUserRequest) returns (CreateUsersResponse);// 4. 雙向流式調用(Bidirectional Streaming)rpc UpdateUsers (stream UpdateUserRequest) returns (stream UserResponse);
}// 消息定義
message GetUserRequest {int32 id = 1;
}message UserResponse {int32 id = 1;string name = 2;string email = 3;
}message ListUsersRequest {string filter = 1;
}message CreateUserRequest {string name = 1;string email = 2;
}message CreateUsersResponse {int32 count = 1;
}message UpdateUserRequest {int32 id = 1;string name = 2;
}

要注意下面這里有了變化(以后會講解為什么要用option go_package):?

package user;//新版本有了下面的option go_package 這里的pacage就可以去掉了(當然留著也不影響)

option go_package = "/user;user"; // 指定生成的 Go 包路徑(生成源碼的路徑和包名,前面是路徑后面是包名,可以自己定義)
//option go_package = ".;user"; //這個可以生成在當前目錄下
?


三、生成 gRPC 代碼

1. 安裝 gRPC 工具

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Java
protoc --java_out=. \--grpc-java_out=. \--plugin=protoc-gen-grpc-java=protoc-gen-grpc-java \user.proto

2. 生成代碼命令

Go
protoc --go_out=. --go-grpc_out=. user.proto//protoc --go_out=. --go-grpc_out=. user.proto
//這個命令使用了兩個輸出插件:--go_out=. 和 --go-grpc_out=.。它分別調用了 Go 相關的 Protobuf 插件和 gRPC Go 插件來生成對應的目標文件。其中:
//--go_out=. 表示使用 Go 的 Protobuf 編譯插件生成對應的 Go 文件。
//--go-grpc_out=. 表示使用 Go 的 gRPC 編譯插件生成 gRPC 服務相關的 Go 文件。
Java
protoc --java_out=. --grpc-java_out=. user.proto

四、Go 實現 gRPC 服務端與客戶端

1. 服務端代碼詳解

package mainimport ("context""fmt""log""net"pb "./user_go_proto""google.golang.org/grpc"
)type userService struct {pb.UnimplementedUserServiceServer
}// 單向調用
func (s *userService) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.UserResponse, error) {return &pb.UserResponse{Id:    req.Id,Name:  "Alice",Email: "alice@example.com",}, nil
}// 服務端流式調用
func (s *userService) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {users := []*pb.UserResponse{{Id: 1, Name: "Alice", Email: "alice@example.com"},{Id: 2, Name: "Bob", Email: "bob@example.com"},}for _, user := range users {if err := stream.Send(user); err != nil {return err}}return nil
}// 客戶端流式調用
func (s *userService) CreateUsers(stream pb.UserService_CreateUsersServer) error {count := 0for {req, err := stream.Recv()if err == io.EOF {break}if err != nil {return err}count++}return stream.SendAndClose(&pb.CreateUsersResponse{Count: int32(count)})
}// 雙向流式調用
func (s *userService) UpdateUsers(stream pb.UserService_UpdateUsersServer) error {for {req, err := stream.Recv()if err == io.EOF {break}if err != nil {return err}resp := &pb.UserResponse{Id:   req.Id,Name: req.Name,}if err := stream.Send(resp); err != nil {return err}}return nil
}func main() {lis, err := net.Listen("tcp", ":50051")if err != nil {log.Fatalf("failed to listen: %v", err)}s := grpc.NewServer()pb.RegisterUserServiceServer(s, &userService{})log.Printf("Server listening at %v", lis.Addr())if err := s.Serve(lis); err != nil {log.Fatalf("failed to serve: %v", err)}
}
代碼解析
  • 服務端實現:通過?pb.RegisterUserServiceServer?注冊服務。
  • 流式處理:通過?stream?接口處理雙向通信。
  • 錯誤處理:捕獲?io.EOF?結束流式調用。

2. 客戶端代碼詳解

package mainimport ("context""fmt""log"pb "./user_go_proto""google.golang.org/grpc"
)func main() {conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())if err != nil {log.Fatalf("did not connect: %v", err)}defer conn.Close()client := pb.NewUserServiceClient(conn)// 單向調用resp, err := client.GetUser(context.Background(), &pb.GetUserRequest{Id: 1})if err != nil {log.Fatalf("could not get user: %v", err)}fmt.Printf("User: %v\n", resp)// 服務端流式調用stream, err := client.ListUsers(context.Background(), &pb.ListUsersRequest{Filter: "IT"})if err != nil {log.Fatalf("could not list users: %v", err)}for {user, err := stream.Recv()if err == io.EOF {break}if err != nil {log.Fatalf("error receiving user: %v", err)}fmt.Printf("Received: %v\n", user)}// 客戶端流式調用stream2, err := client.CreateUsers(context.Background())if err != nil {log.Fatalf("could not create users: %v", err)}for i := 0; i < 3; i++ {if err := stream2.Send(&pb.CreateUserRequest{Name:  fmt.Sprintf("User %d", i),Email: fmt.Sprintf("user%d@example.com", i),}); err != nil {log.Fatalf("error sending user: %v", err)}}resp2, err := stream2.CloseAndRecv()if err != nil {log.Fatalf("error closing stream: %v", err)}fmt.Printf("Created %d users\n", resp2.Count)// 雙向流式調用stream3, err := client.UpdateUsers(context.Background())if err != nil {log.Fatalf("could not update users: %v", err)}for i := 0; i < 3; i++ {if err := stream3.Send(&pb.UpdateUserRequest{Id:   int32(i),Name: fmt.Sprintf("Updated User %d", i),}); err != nil {log.Fatalf("error sending update: %v", err)}resp3, err := stream3.Recv()if err != nil {log.Fatalf("error receiving update: %v", err)}fmt.Printf("Updated: %v\n", resp3)}
}
代碼解析
  • 連接建立:通過?grpc.Dial?連接服務端。
  • 流式調用:通過?stream.Send()?和?stream.Recv()?實現雙向通信。
  • 錯誤處理:捕獲?io.EOF?結束流式調用。

五、Java 實現 gRPC 服務端與客戶端

1. 服務端代碼詳解

import user.UserServiceGrpc;
import user.GetUserRequest;
import user.UserResponse;
import user.ListUsersRequest;
import user.CreateUserRequest;
import user.CreateUsersResponse;
import user.UpdateUserRequest;import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;public class UserServiceServer {public static void main(String[] args) throws IOException, InterruptedException {final Server server = ServerBuilder.forPort(50051).addService(new UserServiceImpl()).build();server.start();System.out.println("Server started at port 50051");final CountDownLatch latch = new CountDownLatch(1);server.awaitTermination();}static class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {// 單向調用@Overridepublic void getUser(GetUserRequest request, StreamObserver<UserResponse> responseObserver) {UserResponse response = UserResponse.newBuilder().setId(request.getId()).setName("Alice").setEmail("alice@example.com").build();responseObserver.onNext(response);responseObserver.onCompleted();}// 服務端流式調用@Overridepublic void listUsers(ListUsersRequest request, StreamObserver<UserResponse> responseObserver) {UserResponse user1 = UserResponse.newBuilder().setId(1).setName("Alice").setEmail("alice@example.com").build();UserResponse user2 = UserResponse.newBuilder().setId(2).setName("Bob").setEmail("bob@example.com").build();responseObserver.onNext(user1);responseObserver.onNext(user2);responseObserver.onCompleted();}// 客戶端流式調用@Overridepublic StreamObserver<CreateUserRequest> createUsers(StreamObserver<CreateUsersResponse> responseObserver) {return new StreamObserver<>() {int count = 0;@Overridepublic void onNext(CreateUserRequest request) {count++;}@Overridepublic void onError(Throwable throwable) {throwable.printStackTrace();}@Overridepublic void onCompleted() {CreateUsersResponse response = CreateUsersResponse.newBuilder().setCount(count).build();responseObserver.onNext(response);responseObserver.onCompleted();}};}// 雙向流式調用@Overridepublic StreamObserver<UpdateUserRequest> updateUsers(StreamObserver<UserResponse> responseObserver) {return new StreamObserver<>() {@Overridepublic void onNext(UpdateUserRequest request) {UserResponse response = UserResponse.newBuilder().setId(request.getId()).setName(request.getName()).build();responseObserver.onNext(response);}@Overridepublic void onError(Throwable throwable) {throwable.printStackTrace();}@Overridepublic void onCompleted() {responseObserver.onCompleted();}};}}
}
代碼解析
  • 服務端實現:通過繼承?UserServiceGrpc.UserServiceImplBase?實現接口。
  • 流式處理:通過?StreamObserver?處理雙向通信。
  • 錯誤處理:通過?onError?捕獲異常。

2. 客戶端代碼詳解

import user.UserServiceGrpc;
import user.GetUserRequest;
import user.UserResponse;
import user.ListUsersRequest;
import user.CreateUserRequest;
import user.CreateUsersResponse;
import user.UpdateUserRequest;import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;import java.util.concurrent.TimeUnit;public class UserServiceClient {public static void main(String[] args) {ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051).usePlaintext().build();UserServiceGrpc.UserServiceBlockingStub blockingStub = UserServiceGrpc.newBlockingStub(channel);// 單向調用GetUserRequest request = GetUserRequest.newBuilder().setId(1).build();try {UserResponse response = blockingStub.getUser(request);System.out.println("User: " + response.getName());} catch (StatusRuntimeException e) {e.printStackTrace();}// 服務端流式調用ListUsersRequest listRequest = ListUsersRequest.newBuilder().setFilter("IT").build();UserServiceGrpc.UserServiceStub asyncStub = UserServiceGrpc.newStub(channel);asyncStub.listUsers(listRequest, new StreamObserver<>() {@Overridepublic void onNext(UserResponse user) {System.out.println("Received: " + user.getName());}@Overridepublic void onError(Throwable throwable) {throwable.printStackTrace();}@Overridepublic void onCompleted() {System.out.println("Stream completed.");}});// 客戶端流式調用UserServiceGrpc.UserServiceStub createStub = UserServiceGrpc.newStub(channel);createStub.createUsers(new StreamObserver<>() {@Overridepublic void onNext(CreateUsersResponse response) {System.out.println("Created " + response.getCount() + " users");}@Overridepublic void onError(Throwable throwable) {throwable.printStackTrace();}@Overridepublic void onCompleted() {System.out.println("Create stream completed.");}}).forEachRemaining(user -> {if (user != null) {System.out.println("Sending: " + user.getName());}});// 雙向流式調用UserServiceGrpc.UserServiceStub updateStub = UserServiceGrpc.newStub(channel);StreamObserver<UpdateUserRequest> requestStream = updateStub.updateUsers(new StreamObserver<>() {@Overridepublic void onNext(UserResponse response) {System.out.println("Updated: " + response.getName());}@Overridepublic void onError(Throwable throwable) {throwable.printStackTrace();}@Overridepublic void onCompleted() {System.out.println("Update stream completed.");}});for (int i = 0; i < 3; i++) {UpdateUserRequest updateRequest = UpdateUserRequest.newBuilder().setId(i).setName("Updated User " + i).build();requestStream.onNext(updateRequest);}requestStream.onCompleted();try {channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);} catch (InterruptedException e) {e.printStackTrace();}}
}
代碼解析
  • 連接建立:通過?ManagedChannelBuilder?連接服務端。
  • 流式調用:通過?StreamObserver?實現雙向通信。
  • 錯誤處理:通過?onError?捕獲異常。

六、多語言交互的最佳實踐

1. 保持?.proto?文件統一

  • 所有語言共享同一個?.proto?文件,確保接口定義一致。
  • 使用?protoc?生成對應語言的代碼。

2. 版本控制

  • 在?.proto?文件中添加版本注釋:
    // Version 1.0.0
    message User {string name = 1;
    }

3. 依賴管理

  • 使用?go mod?或?Maven?管理依賴,確保不同語言的代碼版本一致。

注意:

這篇文章中使用的Go和Java 實現 gRPC 服務端與客戶端的例子是二者分開用的,而不是混合語言,其實在這里我更想做的是Go和Java放在一起使用,比如Go做服務端,Java做客戶端。原因是我覺得Go更適合grpc,所以大家著重看Go的講解即可。如果要混合的話也是以Go為主,Java為輔。

這次沒有使用多語言的原因是,突然混合在一起的話怕大家不好理解,我在其他文章中也有講解跨語言使用的例子,大家有興趣的可以去看看。


七、總結

在本文中,我們詳細講解了 gRPC 與 Protobuf 的深度集成,包括:

  1. 通過?.proto?文件定義服務接口
  2. 在 Go 和 Java 中實現服務端與客戶端
  3. 單向、流式通信的完整代碼示例
  4. 多語言交互的最佳實踐

通過這些內容,你已經能夠構建高性能、可擴展的微服務系統,并在不同語言之間實現無縫通信。gRPC 與 Protobuf 的結合是現代分布式系統的基石,希望這篇文章能幫助你更自信地在項目中應用這些技術。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/84841.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/84841.shtml
英文地址,請注明出處:http://en.pswp.cn/web/84841.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

可信計算的基石:TPM技術深度解析與應用實踐

可信計算的基石&#xff1a;TPM技術深度解析與應用實踐 引言&#xff1a;數字世界的"信任之錨" 在數據泄露事件頻發的時代&#xff0c;傳統軟件級安全防護已力不從心。TPM&#xff08;可信平臺模塊&#xff09;作為硬件級安全解決方案&#xff0c;正成為現代計算設…

「ECG信號處理——(18)基于時空特征的心率變異性分析」2025年6月23日

一、HRV概述 心率變異性&#xff08;Heart rate variability ,HRV&#xff09;分析是通過測量分析連續正常R-R間期的時間變化來反映心率的變化程度的&#xff0c;根據計算RR 序列的統計指標&#xff0c;或者是畫出RR間期的直方圖和散點圖來反映HRV的大小情況。下面我們從男性與…

【學習筆記】深入理解Java虛擬機學習筆記——第10章 前端編譯與優化

第10章 前端編譯與優化 10.1 概述 1>前端編譯器&#xff1a;Javac命令。 【.java文件->.class文件】 2>即時編譯器&#xff1a;Hotspot.C1.C2 【.class文件->機器碼】 3>提前編譯器&#xff1a;JDK的Jaotc等【.java->機器碼】 10.2 Javac 編譯器 10.2.1 …

Python 區塊鏈與Web3開發指南

https://www.python.org/static/community_logos/python-logo-master-v3-TM.png 區塊鏈基礎概念 區塊鏈核心特性 python 復制 下載 class Block:def __init__(self, index, timestamp, data, previous_hash):self.index indexself.timestamp timestampself.data datas…

工業智能體調參閉環:從物料感知到智慧工藝的落地路徑

用戶定義目標&#xff1a;智能工藝的起點不是機器&#xff0c;而是人 在智能制造系統中&#xff0c;工藝調優的第一步并非直接依賴AI或自動化設備&#xff0c;而是始于用戶的明確輸入。用戶需要在系統中定義產品的工藝要求&#xff0c;包括目標尺寸與規格&#xff08;如長寬高…

【Linux學習筆記】進程間通信之共享內存

【Linux學習筆記】進程間通信之共享內存 &#x1f525;個人主頁&#xff1a;大白的編程日記 &#x1f525;專欄&#xff1a;Linux學習筆記 文章目錄 【Linux學習筆記】進程間通信之共享內存前言一. system V共享內存1.1 共享內存數據結構1.2 共享內存函數1.3 共享內存實現通信…

郭碧婷闖入女團賽道 與劉忻張予曦蔡詩蕓組成ROLLING SISTERS

近日&#xff0c;郭碧婷與劉忻、張予曦、蔡詩蕓組成的女團ROLLING SISTERS正式官宣&#xff0c;并發布《Rolling Life》《Alpha》兩首單曲&#xff01; 此次幾位姐姐的組合讓大家眼前一亮&#xff0c;尤其是郭碧婷造型顛覆以往。銀灰色挑染短發搭配棱角分明的黑色煙熏妝&#x…

2025再升級:醫療數智立體化體系V2.0架構簡介

在醫療數智立體化體系第一版基礎上,融入量子物理的第一性原理計算、人工智能(AI)、高性能云計算(HPC)和標準化機器人自動化整合成“醫療數智立體化體系2.0”,代表了醫療研發未來的重要發展方向。這個體系的核心在于深度融合物理世界規律、智能計算與自動化執行,為醫療AI…

Day40 訓練和測試的規范寫法

目錄 一、彩色和灰度圖片測試和訓練的規范寫法&#xff1a;封裝在函數中 單通道圖片的規范寫法 彩色圖片的規范寫法 二、展平操作&#xff1a;除第一個維度batchsize外全部展平 圖像任務中的張量形狀 NLP任務中的張量形狀 1. Flatten操作 2. view/reshape操作 總結 三…

Linux 文件 I/O 與標準 I/O 緩沖機制詳解

一、什么是標準 I/O&#xff1f;&#xff08;FILE* 接口&#xff09; 標準 I/O 是 C 標準庫為我們提供的一套高級文件操作接口&#xff0c;核心基于結構體 FILE&#xff0c;常見函數如&#xff1a; fopen() / fclose() fread() / fwrite() fprintf() / fscanf() fflush() /…

C++的前世今生-C++11

C98&#xff08;ISO/IEC 14882:1998&#xff09; C98 是 C 的第一個標準化版本&#xff08;ISO/IEC 14882:1998&#xff09;&#xff0c;它正式確立了 C 的核心語言特性和標準庫。以下是 C98 的主要特性總結&#xff1a; 一、核心語言特性** 模板&#xff08;Templates&…

詞編碼模型怎么進行訓練的,輸出輸入是什么,標簽是什么

詞編碼模型怎么進行訓練的,輸出輸入是什么,標簽是什么 詞編碼模型的訓練本質是通過數據驅動的方式,將離散的文本符號映射為連續的語義向量。 一、訓練機制:從符號到向量的映射邏輯 1. 核心目標 將單詞/子詞(Token)映射為低維向量,使語義相關的詞在向量空間中距離更近…

【Linux指南】文件管理高級操作(復制、移動、查找)

引言 在Linux系統管理中&#xff0c;文件的復制、移動與查找是比基礎操作更進階的核心技能&#xff0c;它們構成了高效管理文件系統的"三駕馬車"。當我們需要備份重要數據、重構目錄結構或在龐大的文件系統中定位目標文件時&#xff0c;cp、mv、find等命令將成為最得…

【棧】-----【小C的記事本】

小C的記事本 題目描述 小C最近學會了 Java 小程序的開發&#xff0c;他很開心&#xff0c;于是想做一個簡單的記事本程序練練手。 他希望他的記事本包含以下功能&#xff1a; append(str)&#xff1a;向記事本插入字符串 str&#xff08;英文字符&#xff09;。delete(k)&am…

技能系統詳解(2)——特效表現

特效會有個EffectManager用于統一管理所有特效&#xff0c;技能特效只是各類特效中的一種 EffectManager需要提供特效的創建&#xff0c;返回被封裝為EffectHandle 每類特效都有各種不同的配置參數&#xff0c;這些配置參數會傳遞給EffectManager用于生成EffectHandler 為支…

12.OpenCV—基礎入門

01讀取圖像 02創建空白圖像 03保存圖像 04更改圖像亮度 05更改圖像對比度 06灰度直方圖均衡 07彩色直方圖均衡 08五種濾波方式 09形態學操作 10仿射變換 11角度縮放仿射變換 12透視變換 13坐標映射 14模板匹配 15多模板匹配 16查找輪廓線 17輪廓線匹配 17繪制…

【Python】Python之什么是生成器?什么是迭代器?

目錄 專欄導讀前言什么是迭代器&#xff08;Iterator&#xff09;&#xff1f;迭代器的定義迭代器協議可迭代對象 vs 迭代器自定義迭代器迭代器的優勢 什么是生成器&#xff08;Generator&#xff09;&#xff1f;生成器的定義生成器函數生成器表達式復雜的生成器示例生成器的狀…

Python中實現簡單爬蟲并處理數據

在當今數據驅動的時代&#xff0c;能夠從互聯網上高效地抓取信息變得越來越重要。Python因其簡潔易學的特性&#xff0c;成為了編寫網絡爬蟲的首選語言之一。接下來&#xff0c;我將介紹如何使用Python來實現一個基礎的網絡爬蟲&#xff0c;并對收集到的數據進行初步處理。 首先…

免費wordpress主題網

免費WordPress主題網 WP模板牛 WP模板牛是一個提供免費WordPress主題的網站&#xff0c;用戶可以在這里找到大量高質量的模板&#xff0c;適用于各種網站類型。該網站致力于為用戶提供簡單、高效的建站體驗。 官網鏈接&#xff1a; https://wpniu.com 建站哥模板 建站哥模板…

為什么需要MyBatis-Plus條件構造器?

目錄 前言 一、傳統SQL編寫的痛點 二、條件構造器的核心優勢 1. 防SQL注入&#xff08;安全性&#xff09; 2. 面向對象編程&#xff08;可讀性&#xff09; 3. 動態條件構建&#xff08;靈活性&#xff09; 4. 數據庫無關性&#xff08;可移植性&#xff09; 三、典型應…