引言:REST已死?不,只是有了更多选择
\n\n每位Java开发者至少都编写过REST API。它方便、熟悉,而且JSON可读性强。但当负载增加、延迟变得关键时,REST开始显得力不从心。这时,gRPC——这项来自Google的技术——登上了舞台,它承诺像本地调用一样快速,序列化速度比JSON快10倍,并内置支持流式处理。
\n\n在本文中,我们将探讨Java中gRPC何时真正合理,以及何时最好继续使用REST。没有废话——只有真实场景、代码示例和诚实的基准测试。
\n\n什么是gRPC,为什么大家都在谈论它?
\n\ngRPC是一个远程过程调用(RPC)框架。它不使用JSON,而是采用Protocol Buffers (protobuf)——一种二进制序列化格式。它不使用HTTP/1.1,而是采用HTTP/2,支持多路复用和头部压缩。
\n\n对于Java来说,这意味着:
\n- \n
- 通过
.proto文件实现强类型契约 \n - 自动生成客户端和服务器代码 \n
- 四种交互模式:一元、服务器流式、客户端流式、双向流式 \n
- 内置支持超时、截止时间和负载均衡 \n
典型的.proto文件长什么样?
\n\nsyntax = "proto3";\n\npackage com.pythonlib.example;\n\nservice UserService {\n rpc GetUser (UserRequest) returns (UserResponse);\n rpc ListUsers (UserListRequest) returns (stream UserResponse);\n rpc UpdateUser (stream UserUpdateRequest) returns (UserResponse);\n rpc Chat (stream ChatMessage) returns (stream ChatMessage);\n}\n\nmessage UserRequest {\n int32 id = 1;\n}\n\nmessage UserResponse {\n int32 id = 1;\n string name = 2;\n string email = 3;\n}\n\nmessage UserListRequest {\n int32 page = 1;\n int32 size = 2;\n}\n\nmessage UserUpdateRequest {\n int32 id = 1;\n string name = 2;\n}\n\nmessage ChatMessage {\n string user = 1;\n string text = 2;\n}\n\n\n通过protoc编译后,您将获得一个带有构建器的现成Java类——无需手动映射。
Java中gRPC何时是最佳选择?5种场景
\n\n1. 高负载微服务
\n\n如果您有50多个微服务每秒相互通信数千次,gRPC相比REST可将延迟降低30-50%。Protobuf占用空间更小,HTTP/2允许为所有请求保持单一连接。
\n\n实际案例:我们将内部推荐服务从REST迁移到gRPC。响应时间从12毫秒降至4毫秒,流量减少了3倍。
\n\n2. 实时数据流
\n\ngRPC原生支持服务器端流式、客户端流式和双向流式。这非常适合:
\n- \n
- 日志和指标(代理发送数据流) \n
- 聊天系统和通知 \n
- 分块处理大文件 \n
Java中实现服务器端流式的服务器:
\n\nimport io.grpc.stub.StreamObserver;\n\npublic class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {\n\n @Override\n public void listUsers(UserListRequest request,\n StreamObserver<UserResponse> responseObserver) {\n for (int i = 0; i < 10; i++) {\n UserResponse user = UserResponse.newBuilder()\n .setId(i)\n .setName("User_" + i)\n .setEmail("user" + i + "@pythonlib.ru")\n .build();\n responseObserver.onNext(user);\n try {\n Thread.sleep(200); // 模拟延迟\n } catch (InterruptedException e) {\n responseObserver.onError(e);\n }\n }\