1. 什麼是 gRPC?
gRPC 是一個高性能、開源的通用 RPC 框架,由 Google 開發。它基於 HTTP/2 協議,使用 Protocol Buffers 作為接口定義語言和序列化工具。
為什麼選擇 gRPC?
- 高性能:基於 HTTP/2,支持多路複用和流式傳輸
- 跨語言:支持多種編程語言
- 強類型:通過 proto 文件明確定義接口
- 代碼生成:自動生成客户端和服務端代碼
- 雙向流:支持客户端流、服務端流和雙向流
2. gRPC 核心組件
2.1 Protocol Buffers (.proto 文件)
Protocol Buffers(簡稱 Protobuf)是 Google 開發的一種高效、跨語言、跨平台的數據序列化機制。.proto 文件就是 Protobuf 的接口定義文件,用於描述數據結構(message)和服務接口(service)。通過編譯 .proto 文件,可以自動生成多種語言的代碼(Go、Java、Python、C++ 等),從而在不同系統之間高效傳輸結構化數據
syntax = "proto3";
package user;
option go_package = ".;user";
//📝 Powered by Moshow 鄭鍇 | 更多技術乾貨:
// 定義服務
service UserService {
rpc GetUser (UserRequest) returns (UserResponse);
rpc CreateUser (CreateUserRequest) returns (UserResponse);
rpc StreamUsers (StreamUsersRequest) returns (stream UserResponse);
}
// 定義消息類型
message UserRequest {
string user_id = 1;
}
message CreateUserRequest {
string name = 1;
string email = 2;
int32 age = 3;
}
message UserResponse {
string user_id = 1;
string name = 2;
string email = 3;
int32 age = 4;
string created_at = 5;
}
message StreamUsersRequest {
int32 batch_size = 1;
}
2.2 gRPC Server
gRPC Server 就是運行在服務端的 遠程過程調用服務實現。它負責監聽網絡端口,接收客户端通過 gRPC 協議發來的請求,調用本地實現的業務邏輯(Handler),並將結果序列化後返回給客户端。 gRPC Server = 服務端容器 + 接口實現。它負責 接收請求 → 調用業務邏輯 → 返回響應。
- 服務端實現(Handler): 在
.proto文件中定義了服務接口和方法,服務端需要在代碼中實現這些方法。例如SayHello方法在 Go 中會對應一個函數實現。 - 監聽與調度: gRPC Server 會在指定端口(如
:50051)監聽請求,接收到請求後根據方法名路由到對應的 Handler。 - 序列化與反序列化: 請求和響應數據會通過 Protocol Buffers(Protobuf)進行高效的二進制序列化和反序列化。
- 通信協議: gRPC Server 基於 HTTP/2,支持多路複用、頭部壓縮和流式通信(客户端流、服務端流、雙向流)。
package main
import (
"context"
"log"
"net"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
user "your-module-path/user" // 根據實際路徑修改
)
//📝 Powered by Moshow 鄭鍇 | 更多技術乾貨:
// 實現服務接口
type userServer struct {
user.UnimplementedUserServiceServer
users map[string]*user.UserResponse
}
func (s *userServer) GetUser(ctx context.Context, req *user.UserRequest) (*user.UserResponse, error) {
log.Printf("GetUser called with ID: %s", req.UserId)
if user, exists := s.users[req.UserId]; exists {
return user, nil
}
return nil, grpc.Errorf(grpc.Code(nil), "user not found")
}
func (s *userServer) CreateUser(ctx context.Context, req *user.CreateUserRequest) (*user.UserResponse, error) {
log.Printf("CreateUser called: %s, %s", req.Name, req.Email)
newUser := &user.UserResponse{
UserId: generateID(),
Name: req.Name,
Email: req.Email,
Age: req.Age,
CreatedAt: time.Now().Format(time.RFC3339),
}
s.users[newUser.UserId] = newUser
return newUser, nil
}
func (s *userServer) StreamUsers(req *user.StreamUsersRequest, stream user.UserService_StreamUsersServer) error {
log.Printf("StreamUsers called with batch size: %d", req.BatchSize)
for _, user := range s.users {
if err := stream.Send(user); err != nil nil {
return err
}
time.Sleep(100 * time.Millisecond) // 模擬處理延遲
}
return nil
}
func generateID() string {
return fmt.Sprintf("user-%d", time.Now().UnixNano())
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
user.RegisterUserServiceServer(s, &userServer{
users: make(map[string]*user.UserResponse),
})
// 啓用反射,便於調試和工具使用
reflection.Register(s)
log.Printf("gRPC server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
2.3 gRPC Client
- gRPC Client = Stub + 網絡通信層,它把本地調用轉化為遠程請求。
- 藉助 Protobuf 和 HTTP/2,gRPC Client 能實現 高性能、跨語言、強類型 的遠程調用。
- 在 Go、Java、Node.js、Postman 等環境中,客户端都能通過相同的
.proto文件與服務端交互
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
user "your-module-path/user" // 根據實際路徑修改
)
func main() {
//📝 Powered by Moshow 鄭鍇 | 更多技術乾貨:
conn, err := grpc.Dial("localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
client := user.NewUserServiceClient(conn)
// 創建用户
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
createResp, err := client.CreateUser(ctx, &user.CreateUserRequest{
Name: "John Doe",
Email: "john@example.com",
Age: 30,
})
if err != nil {
log.Fatalf("could not create user: %v", err)
}
log.Printf("Created user: %v", createResp)
// 獲取用户
getUserResp, err := client.GetUser(ctx, &user.UserRequest{
UserId: createResp.UserId,
})
if err != nil {
log.Fatalf("could not get user: %v", err)
}
log.Printf("Got user: %v", getUserResp)
// 流式用户
streamCtx, streamCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer streamCancel()
stream, err := client.StreamUsers(streamCtx, &user.StreamUsersRequest{
BatchSize: 5,
})
if err != nil {
log.Fatalf("could not stream users: %v", err)
}
for {
userResp, err := stream.Recv()
if err != nil {
break
}
log.Printf("Streamed user: %v", userResp)
}
}
3. 項目結構和依賴
3.1 項目結構
my-grpc-project/
├── proto/
│ └── user.proto
├── user/
│ ├── user.pb.go
│ └── user_grpc.pb.go
├── server/
│ └── main.go
├── client/
│ └── main.go
└── go.mod
3.2 依賴安裝
# 安裝 Protocol Buffers 編譯器
# macOS
brew install protobuf
# Ubuntu
sudo apt-get install protobuf-compiler
# 安裝 Go 插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# 生成代碼
protoc --go_out=. --go-grpc_out=. proto/user.proto
3.3 go.mod 依賴
module my-grpc-project
go 1.19
require (
google.golang.org/grpc v1.50.0
google.golang.org/protobuf v1.28.1
)
4. 其他語言調用 gRPC
4.1 Java 調用
Maven 依賴
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.50.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.50.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.50.0</version>
</dependency>
</dependencies>
Java 客户端代碼
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import user.UserServiceGrpc;
import user.User;
public class JavaClient {
public static void main(String[] args) {
ManagedChannel channel = ManagedChannelBuilder
.forAddress("localhost", 50051)
.usePlaintext()
.build();
UserServiceGrpc.UserServiceBlockingStub stub =
UserServiceGrpc.newBlockingStub(channel);
// 創建用户
User.CreateUserRequest createRequest = User.CreateUserRequest.newBuilder()
.setName("Jane Smith")
.setEmail("jane@example.com")
.setAge(25)
.build();
User.UserResponse createResponse = stub.createUser(createRequest);
System.out.println("Created user: " + createResponse);
// 獲取用户
User.UserRequest getRequest = User.UserRequest.newBuilder()
.setUserId(createResponse.getUserId())
.build();
User.UserResponse getResponse = stub.getUser(getRequest);
System.out.println("Got user: " + getResponse);
channel.shutdown();
}
}
4.2 Node.js 調用
安裝依賴
npm install @grpc/grpc-js @grpc/proto-loader
Node.js 客户端代碼
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const PROTO_PATH = __dirname + '/user.proto';
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true
});
const userProto = grpc.loadPackageDefinition(packageDefinition).user;
const client = new userProto.UserService(
'localhost:50051',
grpc.credentials.createInsecure()
);
// 創建用户
const createRequest = {
name: 'Bob Johnson',
email: 'bob@example.com',
age: 35
};
client.createUser(createRequest, (err, response) => {
if (err) {
console.error('Error:', err);
return;
}
console.log('Created user:', response);
// 獲取用户
const getRequest = { user_id: response.user_id };
client.getUser(getRequest, (err, userResponse) => {
if (err) {
console.error('Error:', err);
return;
}
console.log('Got user:', userResponse);
});
});
// 流式調用
const streamRequest = { batch_size: 3 };
const stream = client.streamUsers(streamRequest);
stream.on('data', (user) => {
console.log('Streamed user:', user);
});
stream.on('end', () => {
console.log('Stream ended');
});
4.3 Postman 調用 gRPC
Postman 從版本 8.0 開始支持 gRPC 請求:
步驟 1:創建 gRPC 請求
- 打開 Postman → New → gRPC Request
- 輸入服務器地址:
localhost:50051
步驟 2:導入 proto 文件
- 點擊 "Import a .proto file"
- 選擇你的
user.proto文件 - 選擇要調用的服務和方法
步驟 3:構造請求消息
{
28
}
步驟 4:發送請求
- 點擊 "Invoke" 發送請求
- 查看響應結果
5. 高級特性
5.1 攔截器(中間件)
// 服務端攔截器
func loggingInterceptor(ctx context.Context, req interface{},
info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
start := time.Now()
resp, err := handler(ctx, req)
log.Printf("Method: %s, Duration: %v, Error: %v",
info.FullMethod, time.Since(start), err)
return resp, err
}
// 使用攔截器
s := grpc.NewServer(
grpc.UnaryInterceptor(loggingInterceptor),
)
5.2 錯誤處理
import "google.golang.org/grpc/codes"
import "google.golang.org/grpc/status"
func (s *userServer) GetUser(ctx context.Context, req *user.UserRequest) (*user.UserResponse, error) {
if req.UserId == "" {
return nil, status.Errorf(codes.InvalidArgument, "user_id is required")
}
user, exists := s.users[req.UserId]
if !exists {
return nil, status.Errorf(codes.NotFound, "user not found: %s", req.UserId)
}
return user, nil
}
6. 總結
gRPC 提供了強大的跨語言 RPC 能力,主要優勢包括:
- 性能優異:基於 HTTP/2,支持流式傳輸
- 開發效率:通過 proto 文件自動生成代碼
- 類型安全:強類型接口定義
- 跨語言支持:Java、Node.js、Python、Go 等
- 工具生態:Postman、grpcurl 等調試工具