news 2026/8/7 6:30:56

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/8/7 21:28:36

Apache Mesos集群运维实战:故障恢复与版本管理完全指南

Apache Mesos集群运维实战&#xff1a;故障恢复与版本管理完全指南 【免费下载链接】mesos Apache Mesos 项目地址: https://gitcode.com/gh_mirrors/mesos2/mesos 在现代分布式系统中&#xff0c;Mesos集群维护是确保业务连续性的关键环节。本指南将深入解析Mesos集群运…

作者头像 李华
网站建设 2026/8/7 21:29:40

探索式测试的Session管理法:提升软件测试效率与可追溯性

探索式测试作为一种强调测试者自主性与创造性的软件测试方法&#xff0c;在快速迭代的现代开发环境中日益重要。然而&#xff0c;其非结构化的特性可能导致测试过程难以追踪和评估。Session管理法应运而生&#xff0c;它通过引入时间盒&#xff08;Time-boxed&#xff09;会话和…

作者头像 李华
网站建设 2026/8/7 22:05:42

购物省钱参考:爱创猫电商优惠券领取方式

外卖网购“隐形开支”太多&#xff1f;这份极致省钱手册&#xff0c;让你每月轻松多省几百块你有没有算过&#xff0c;自己每个月花在外卖和网购上的钱有多少&#xff1f;打开手机账单&#xff0c;那些十几二十块的外卖订单&#xff0c;几十上百的“凑单”商品&#xff0c;看似…

作者头像 李华
网站建设 2026/8/7 22:04:12

12、Red Hat Enterprise Linux硬件分析与管理指南

Red Hat Enterprise Linux硬件分析与管理指南 1. RPM包安装与信息查看 在安装示例包时,如果未安装 vpnc 包,会显示如下错误: error: Failed dependencies: vpnc is needed by startvpn-1.1-1.noarch若要强制安装该包以测试从示例中构建的软件包,可使用以下命令: r…

作者头像 李华
网站建设 2026/8/7 22:04:26

35、Linux 内核监控与调试:NUMA、AltSysRq 及 Kdump 全解析

Linux 内核监控与调试:NUMA、AltSysRq 及 Kdump 全解析 在 Linux 系统的运维和管理中,对内核的监控与调试至关重要。本文将深入探讨 NUMA 统计信息、AltSysRq 系统请求以及 Kdump 内核转储工具的使用,帮助你更好地理解和管理 Linux 内核。 1. NUMA 统计信息 NUMA(Non-Un…

作者头像 李华
网站建设 2026/8/7 22:12:01

DataEase开源BI工具完整指南:从零开始的数据可视化之旅

DataEase开源BI工具完整指南&#xff1a;从零开始的数据可视化之旅 【免费下载链接】DataEase 人人可用的开源 BI 工具 项目地址: https://gitcode.com/feizhiyun/dataease DataEase是一款人人可用的开源BI工具&#xff0c;让数据分析变得简单直观。作为一款基于GPLv3协…

作者头像 李华