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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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; 三、典型应…