news 2026/6/10 10:28:15

Java gRPC 调用完全指南:从 IDL 定义到微服务实战,含单元测试与拦截器详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java gRPC 调用完全指南:从 IDL 定义到微服务实战,含单元测试与拦截器详解

🚀 前言:为什么是 gRPC?

在微服务架构中,RESTful API 虽然通用,但在内部服务调用场景下,它的效率成为了瓶颈(JSON 序列化慢、HTTP/1.1 头部冗余)。
gRPC基于HTTP/2Protobuf,提供了二进制传输、多路复用、双向流等特性,性能比 REST 高出一个数量级。

本文将带你从 0 构建一个包含普通调用流式调用的订单服务,并手写拦截器单元测试


🛠️ 第一阶段:环境搭建与 IDL 定义

gRPC 的核心是Contract First(契约优先)。我们需要先写.proto文件。

1. Maven 依赖与插件

这是最容易劝退新手的环节。你需要配置protobuf-maven-plugin来自动生成 Java 代码。

<dependencies><dependency><groupId>io.grpc</groupId><artifactId>grpc-netty-shaded</artifactId><version>1.54.0</version></dependency><dependency><groupId>io.grpc</groupId><artifactId>grpc-protobuf</artifactId><version>1.54.0</version></dependency><dependency><groupId>io.grpc</groupId><artifactId>grpc-stub</artifactId><version>1.54.0</version></dependency><dependency><groupId>org.apache.tomcat</groupId><artifactId>annotations-api</artifactId><version>6.0.53</version><scope>provided</scope></dependency></dependencies><build><extensions><extension><groupId>kr.motd.maven</groupId><artifactId>os-maven-plugin</artifactId><version>1.7.1</version></extension></extensions><plugins><plugin><groupId>org.xolstice.maven.plugins</groupId><artifactId>protobuf-maven-plugin</artifactId><version>0.6.1</version><configuration><protocArtifact>com.google.protobuf:protoc:3.21.7:exe:${os.detected.classifier}</protocArtifact><pluginId>grpc-java</pluginId><pluginArtifact>io.grpc:protoc-gen-grpc-java:1.54.0:exe:${os.detected.classifier}</pluginArtifact></configuration><executions><execution><goals><goal>compile</goal><goal>compile-custom</goal></goals></execution></executions></plugin></plugins></build>
2. 定义order.proto

我们在src/main/proto下创建文件。为了演示深度,我们定义两个接口:一个简单调用,一个服务端流式调用(Server Streaming)。

syntax = "proto3"; package com.example.grpc; option java_multiple_files = true; option java_package = "com.example.grpc.lib"; option java_outer_classname = "OrderProto"; service OrderService { // 1. 简单一元调用:根据 ID 查订单 rpc GetOrder (OrderRequest) returns (OrderResponse); // 2. 服务端流式调用:实时获取订单状态流 rpc WatchOrderStatus (OrderRequest) returns (stream OrderStatus); } message OrderRequest { int32 order_id = 1; } message OrderResponse { int32 order_id = 1; string product_name = 2; double price = 3; } message OrderStatus { string status = 1; string timestamp = 2; }

执行mvn compile,你会发现在target/generated-sources下生成了 Java 代码。


💻 第二阶段:服务端实现 (Server)

服务端需要继承生成的ImplBase类。

publicclassOrderServiceImplextendsOrderServiceGrpc.OrderServiceImplBase{// 1. 实现简单调用@OverridepublicvoidgetOrder(OrderRequestrequest,StreamObserver<OrderResponse>responseObserver){System.out.println("收到查询请求 ID: "+request.getOrderId());// 模拟数据库查询OrderResponseresponse=OrderResponse.newBuilder().setOrderId(request.getOrderId()).setProductName("MacBook Pro M3").setPrice(12999.00).build();// 返回数据responseObserver.onNext(response);// 结束调用 (必须调用,否则客户端会一直等待)responseObserver.onCompleted();}// 2. 实现服务端流式调用 (Server Streaming)@OverridepublicvoidwatchOrderStatus(OrderRequestrequest,StreamObserver<OrderStatus>responseObserver){// 模拟订单状态变化:每隔1秒推送一次String[]statuses={"已下单","支付成功","仓库配货","已发货","派送中"};for(Stringstatus:statuses){try{Thread.sleep(500);}catch(InterruptedExceptione){}OrderStatusorderStatus=OrderStatus.newBuilder().setStatus(status).setTimestamp(System.currentTimeMillis()+"").build();// 推送一条流数据responseObserver.onNext(orderStatus);}responseObserver.onCompleted();}}

启动 Server:

publicclassGrpcServer{publicstaticvoidmain(String[]args)throwsIOException,InterruptedException{// 添加拦截器 (后面讲)Serverserver=ServerBuilder.forPort(9090).addService(ServerInterceptors.intercept(newOrderServiceImpl(),newAuthInterceptor())).build();server.start();System.out.println("gRPC Server started on port 9090");server.awaitTermination();}}

🔌 第三阶段:客户端调用 (Client)

客户端通常使用Stub (存根)来调用。

publicclassGrpcClient{publicstaticvoidmain(String[]args){// 1. 创建通道 (Connection)ManagedChannelchannel=ManagedChannelBuilder.forAddress("localhost",9090).usePlaintext()// 测试环境使用明文传输.build();// 2. 创建 Stub// BlockingStub: 同步阻塞调用OrderServiceGrpc.OrderServiceBlockingStubblockingStub=OrderServiceGrpc.newBlockingStub(channel);// 3. 简单调用OrderResponseresponse=blockingStub.getOrder(OrderRequest.newBuilder().setOrderId(1001).build());System.out.println("查询结果: "+response.getProductName());// 4. 流式调用 (需要用 Iterator)Iterator<OrderStatus>statusIterator=blockingStub.watchOrderStatus(OrderRequest.newBuilder().setOrderId(1001).build());while(statusIterator.hasNext()){OrderStatusstatus=statusIterator.next();System.out.println("当前状态: "+status.getStatus());}channel.shutdown();}}

🛡️ 第四阶段:进阶 —— 拦截器 (Interceptor)

在生产环境中,我们不可能裸奔。我们需要拦截器来做Auth 鉴权日志记录
gRPC 使用Metadata(类似 HTTP Header) 来传递元数据。

服务端拦截器:验证 Token

publicclassAuthInterceptorimplementsServerInterceptor{privatestaticfinalMetadata.Key<String>TOKEN_KEY=Metadata.Key.of("auth-token",Metadata.ASCII_STRING_MARSHALLER);@Overridepublic<ReqT,RespT>ServerCall.Listener<ReqT>interceptCall(ServerCall<ReqT,RespT>call,Metadataheaders,ServerCallHandler<ReqT,RespT>next){// 获取客户端传来的 TokenStringtoken=headers.get(TOKEN_KEY);if("secret-123".equals(token)){// 验证通过,放行returnnext.startCall(call,headers);}else{// 验证失败,直接关闭连接call.close(Status.UNAUTHENTICATED.withDescription("Token无效"),headers);returnnewServerCall.Listener<>(){};}}}

客户端发送 Token:

// 客户端需要自定义一个 CallCredentials 或者使用 ClientInterceptor// 这里使用 MetadataUtils 快速注入OrderServiceGrpc.OrderServiceBlockingStubstub=OrderServiceGrpc.newBlockingStub(channel);Metadatameta=newMetadata();meta.put(Metadata.Key.of("auth-token",Metadata.ASCII_STRING_MARSHALLER),"secret-123");// 将 Metadata 附着在 Stub 上stub=MetadataUtils.attachHeaders(stub,meta);

🧪 第五阶段:单元测试 (Test)

这是区分新手和高手的地方。
不要启动真实的 Server 去测,太慢且依赖网络。
gRPC 提供了grpc-testing库,支持InProcessServer (进程内服务器),速度极快。

引入依赖:

<dependency><groupId>io.grpc</groupId><artifactId>grpc-testing</artifactId><version>1.54.0</version><scope>test</scope></dependency>

编写 JUnit 5 测试用例:

publicclassOrderServiceTest{privateServerinProcessServer;privateManagedChannelinProcessChannel;privateOrderServiceGrpc.OrderServiceBlockingStubblockingStub;@BeforeEachpublicvoidsetUp()throwsException{// 生成唯一的服务器名称StringserverName=InProcessServerBuilder.generateName();// 1. 启动进程内服务器 (绑定我们实现的 Service)inProcessServer=InProcessServerBuilder.forName(serverName).directExecutor()// 不使用线程池,直接在当前线程执行,方便测试.addService(newOrderServiceImpl()).build().start();// 2. 创建连接到该服务器的 ChannelinProcessChannel=InProcessChannelBuilder.forName(serverName).directExecutor().build();blockingStub=OrderServiceGrpc.newBlockingStub(inProcessChannel);}@AfterEachpublicvoidtearDown(){inProcessChannel.shutdown();inProcessServer.shutdown();}@TestpublicvoidtestGetOrder(){// givenOrderRequestrequest=OrderRequest.newBuilder().setOrderId(888).build();// whenOrderResponseresponse=blockingStub.getOrder(request);// thenassertEquals(888,response.getOrderId());assertEquals("MacBook Pro M3",response.getProductName());}}

📝 总结

通过这篇文章,你掌握了 gRPC 开发的完整闭环:

  1. IDL 定义proto文件是核心契约。
  2. 流式通信:用StreamObserver实现实时推送。
  3. 拦截器:用Metadata实现 Token 鉴权。
  4. InProcess 测试:不依赖网络的极速单元测试。

gRPC 不仅仅是高性能,更是规范化微服务调用的利器。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 16:33:16

豆包手机被微信「拒绝」后,阿里系 APP 被曝也禁止其登陆,如何看待这场「博弈」?核心矛盾是什么?

最近&#xff0c;豆包手机成为了全网热议的焦点。这款备受期待的 AI 手机在发布后短短几天内便遭遇了微信、淘宝等主流应用的封禁&#xff0c;不仅如此&#xff0c;其他阿里系应用也接连宣布禁止该设备登录。这一波波的封杀让无数网友感到震惊&#xff0c;更引发了关于技术、商…

作者头像 李华
网站建设 2026/6/10 14:15:05

字节跳动计算机使用智能体技术架构深度解析

字节跳动计算机使用智能体技术架构深度解析 【免费下载链接】UI-TARS-2B-SFT 项目地址: https://ai.gitcode.com/hf_mirrors/ByteDance-Seed/UI-TARS-2B-SFT 在人工智能从语言理解向自主操作演进的关键节点&#xff0c;字节跳动推出的计算机使用智能体&#xff08;Comp…

作者头像 李华
网站建设 2026/6/10 12:32:07

(新卷)产品模块算法检验(Java、Js、c\c++、python)

产品模块算法检验 在产品配置中&#xff0c;一个配置产品是由多个产品模块(CM)构成&#xff0c;每个CM有自身的算法&#xff0c;且模块间可能存在算法依赖。例如电脑产品是由主板、CPU日、显卡等CM构成。CPU模块(CM1)算法依赖主板模块(CM2)算法&#xff0c;记作CM2<-CM1,算法…

作者头像 李华
网站建设 2026/6/10 14:20:33

RTCP: 统计、同步与网络自适应

RTCP: 统计、同步与网络自适应 本文是 WebRTC 系列专栏的第十三篇,将深入剖析 RTCP 协议的工作原理,包括 Sender/Receiver Report、网络质量反馈以及音视频同步机制。 目录 RTCP 概述RTCP 包类型Sender Report (SR)Receiver Report (RR)丢包、带宽与延迟分析音视频同步 (Lip-S…

作者头像 李华
网站建设 2026/6/10 14:15:46

DuckDB嵌入式分析数据库终极指南:快速上手高性能数据处理

DuckDB嵌入式分析数据库终极指南&#xff1a;快速上手高性能数据处理 【免费下载链接】duckdb DuckDB is an in-process SQL OLAP Database Management System 项目地址: https://gitcode.com/GitHub_Trending/du/duckdb 在当今数据驱动的时代&#xff0c;嵌入式分析数据…

作者头像 李华
网站建设 2026/6/4 15:56:20

深入 Flutter 底层:自定义 RenderObject 实现高性能异形列表项

在 Flutter 开发中&#xff0c;我们常通过组合Container、ClipPath、CustomPaint等组件实现异形 UI&#xff08;如弧形背景、不规则卡片&#xff09;&#xff0c;但在列表场景下&#xff0c;这类方案往往存在重绘频繁、性能损耗大的问题。究其根本&#xff0c;是因为常规组件本…

作者头像 李华