gRPC(Google Remote Procedure Call)是一种高性能、跨语言的远程过程调用(RPC)框架,基于 HTTP/2 传输协议,使用 Protocol Buffers(Protobuf) 作为序列化协议,适用于微服务架构、高并发系统和跨语言调用场景。
核心特性
- 高性能
- 使用 HTTP/2 作为底层协议,支持多路复用,减少网络延迟。
- 采用 Protobuf 作为数据格式,比 JSON、XML 等更高效。
- 跨语言
- 支持 Java、Go、Python、C++、C#、Node.js、Ruby、PHP、Dart 等多种编程语言
- 自动生成客户端/服务端代码
- 通过 Protobuf 定义服务接口,自动生成 gRPC 客户端和服务端代码,提高开发效率。
- 支持双向流
- 除了普通的请求-响应(Unary)调用,gRPC 还支持:
- 服务器流式(Server Streaming)
- 客户端流式(Client Streaming)
- 双向流式(Bidirectional Streaming)
- 除了普通的请求-响应(Unary)调用,gRPC 还支持:
- 内置负载均衡和认证
- gRPC 内置 负载均衡(可结合 Consul、Etcd、Kubernetes)。
- 支持 TLS 加密,提供 身份验证 和 OAuth 认证。
- 与 Kubernetes & 微服务生态集成
- 天然支持 Kubernetes、Istio 等微服务架构。
基本开发流程
1. 定义 Protobuf 文件
用 .proto 文件定义服务和消息格式:
syntax = "proto3";
package example;
// 定义一个 gRPC 服务
service Greeter {
// 定义一个 RPC 方法
rpc SayHello (HelloRequest) returns (HelloReply);
}
// 请求消息
message HelloRequest {
string name = 1;
}
// 响应消息
message HelloReply {
string message = 1;
}
2. 生成 gRPC 代码
使用 protoc 命令生成对应语言的代码:
protoc --java_out=. --grpc-java_out=. example.proto
(根据不同语言配置相应的 protoc 插件)
3. 实现服务端
public class GreeterServiceImpl extends GreeterGrpc.GreeterImplBase {
@Override
public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
HelloReply reply = HelloReply.newBuilder()
.setMessage("Hello, " + request.getName())
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
4. 实现客户端
public class GreeterClient {
public static void main(String[] args) {
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)
.usePlaintext()
.build();
GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel);
HelloRequest request = HelloRequest.newBuilder().setName("gRPC User").build();
HelloReply response = stub.sayHello(request);
System.out.println(response.getMessage());
channel.shutdown();
}
}
调用类型
| 调用方式 | 客户端请求 | 服务器响应 | 适用场景 |
|---|---|---|---|
| Unary RPC | 1 个请求 | 1 个响应 | 标准请求-响应模式 |
| Server Streaming RPC | 1 个请求 | 多个响应 | 服务器主动推送数据 |
| Client Streaming RPC | 多个请求 | 1 个响应 | 需要批量提交数据 |
| Bidirectional Streaming RPC | 多个请求 | 多个响应 | 双向实时交互 |
gRPC vs REST
| 对比项 | gRPC | REST (HTTP/JSON) |
|---|---|---|
| 传输协议 | HTTP/2 | HTTP/1.1 |
| 数据格式 | Protobuf(二进制) | JSON(文本) |
| 性能 | 高效(小体积、低延迟) | 相对较慢(大数据包) |
| 双向流 | 支持 | 不支持 |
| 浏览器兼容 | 需要 gRPC-Web 代理 | 原生支持 |
适用场景
✅ 微服务架构:适用于 Kubernetes、Istio 体系
✅ 实时通信:如 IM、游戏服务器、流媒体
✅ 跨语言系统:支持多种编程语言
✅ 高性能 API:比 REST 更高效,适合低延迟、高吞吐应用
不适用于:
❌ 需要直接在 Web 浏览器访问的场景(需要 gRPC-Web 代理)
❌ 对可读性要求高的 API(如开放 API,一般更适合 REST)
总结
gRPC 是一种高性能、跨语言的 RPC 框架,特别适用于微服务和高吞吐系统。如果你的业务场景对性能要求高,并且需要支持多语言交互,gRPC 是一个很好的选择。🚀
gRPC 搭配 nacos
演示如何将 gRPC 和 Nacos 结合在一起实现微服务的通信与服务注册发现,我们将创建一个简单的示例,包括:
- 一个 gRPC 服务端,它会将自己的服务信息注册到 Nacos。
- 一个 gRPC 客户端,它会从 Nacos 获取服务地址并调用服务。
实现 gRPC 服务端
创建一个 HelloServiceImpl,实现 HelloService 的方法,并注册到 Nacos。
src/main/java/com/example/grpc/server/HelloServiceImpl.java:
package com.example.grpc.server;
import hello.HelloRequest;
import hello.HelloResponse;
import hello.HelloServiceGrpc;
import io.grpc.stub.StreamObserver;
import org.springframework.stereotype.Service;
@Service
public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {
@Override
public void sayHello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
String name = request.getName();
HelloResponse response = HelloResponse.newBuilder()
.setMessage("Hello, " + name)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
注册服务到 Nacos
服务端启动时,注册服务到 Nacos 注册中心。
src/main/java/com/example/grpc/server/GrpcServerApplication.java:
package com.example.grpc.server;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.NamingFactory;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GrpcServerApplication implements CommandLineRunner {
@Value("${nacos.server-addr}")
private String nacosServerAddr;
public static void main(String[] args) {
SpringApplication.run(GrpcServerApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
// Start gRPC server
Server server = ServerBuilder.forPort(50051)
.addService(new HelloServiceImpl())
.build();
server.start();
System.out.println("gRPC Server started on port 50051");
// Register the service with Nacos
NamingService namingService = NamingFactory.createNamingService(nacosServerAddr);
namingService.registerInstance("HelloService", "127.0.0.1", 50051);
server.awaitTermination();
}
}
实现 gRPC 客户端
客户端会从 Nacos 获取服务地址并调用远程服务。
src/main/java/com/example/grpc/client/GrpcClientApplication.java:
package com.example.grpc.client;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.NamingFactory;
import hello.HelloRequest;
import hello.HelloResponse;
import hello.HelloServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GrpcClientApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(GrpcClientApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
// Get service instance address from Nacos
NamingService namingService = NamingFactory.createNamingService("127.0.0.1:8848");
String serviceAddr = namingService.getAllInstances("HelloService").get(0).getIp() + ":50051";
// Create a gRPC channel
ManagedChannel channel = ManagedChannelBuilder.forTarget(serviceAddr)
.usePlaintext()
.build();
// Create a blocking stub
HelloServiceGrpc.HelloServiceBlockingStub stub = HelloServiceGrpc.newBlockingStub(channel);
// Call the service
HelloRequest request = HelloRequest.newBuilder().setName("World").build();
HelloResponse response = stub.sayHello(request);
System.out.println("Received response: " + response.getMessage());
channel.shutdown();
}
}
5043

被折叠的 条评论
为什么被折叠?



