开发gRPC在程序之间实现远程服务调用
本教程将指导您从零开始搭建一个完整的 gRPC 通信系统,使用 TypeScript 作为客户端,Go 作为服务端。
目录
环境准备
必需软件
在开始之前,请确保已安装以下软件:
1. Node.js 和 npm
# 检查是否已安装
node --version # 需要 v18 或更高版本
npm --version
如未安装,请访问 Node.js 官网 下载安装。
2. Go
# 检查是否已安装
go version # 需要 1.20 或更高版本
如未安装,请访问 Go 官网 下载安装。
3. Protocol Buffers 编译器 (protoc)
# 检查是否已安装
protoc --version # 需要 3.0 或更高版本
macOS 安装:
brew install protobuf
Ubuntu/Debian 安装:
sudo apt-get update
sudo apt-get install -y protobuf-compiler
Windows 安装: 从 GitHub Releases 下载并配置环境变量。
4. Go 的 protoc 插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# 将 Go bin 目录添加到 PATH
export PATH="$PATH:$(go env GOPATH)/bin"
# 建议将上述命令添加到 ~/.bashrc 或 ~/.zshrc
echo 'export PATH="$PATH:$(go env GOPATH)/bin"' >> ~/.zshrc
source ~/.zshrc
5. TypeScript 的 grpc-tools
npm install -g grpc-tools grpc_tools_node_protoc_ts
验证安装
运行以下命令验证所有工具已正确安装:
node --version
npm --version
go version
protoc --version
protoc-gen-go --version
grpc_tools_node_protoc --version
项目结构
我们将创建以下目录结构:
grpc-demo/
├── proto/
│ └── demo.proto # Protocol Buffers 定义文件
├── go-server/ # Go 服务端
│ ├── main.go # 服务端主程序
│ ├── go.mod # Go 模块定义
│ └── proto/ # 生成的 Go proto 文件
│ ├── demo.pb.go
│ └── demo_grpc.pb.go
└── ts-client/ # TypeScript 客户端
├── package.json # npm 配置文件
├── tsconfig.json # TypeScript 配置
├── src/
│ ├── client.ts # 客户端主程序
│ └── test.ts # 测试程序
└── generated/ # 生成的 TypeScript proto 文件
├── demo_pb.js
├── demo_pb.d.ts
├── demo_grpc_pb.js
└── demo_grpc_pb.d.ts
步骤一:创建 Proto 文件
1. 创建项目根目录
mkdir grpc-demo
cd grpc-demo
2. 创建 proto 目录和文件
mkdir proto
创建文件 proto/demo.proto:
syntax = "proto3";
package demo;
option go_package = "./proto;demo";
// CatService 定义了管理猫咪的服务
service CatService {
// GetCat 根据 ID 获取猫咪信息
rpc GetCat(CatRequest) returns (CatResponse);
// ListCats 返回所有猫咪列表
rpc ListCats(ListCatsRequest) returns (ListCatsResponse);
// CreateCat 创建新的猫咪记录
rpc CreateCat(CreateCatRequest) returns (CatResponse);
}
// Cat 表示猫咪实体
message Cat {
int64 id = 1;
string name = 2;
string breed = 3;
int32 age = 4;
string color = 5;
}
// CatRequest 用于请求特定 ID 的猫咪
message CatRequest {
int64 id = 1;
}
// CatResponse 返回猫咪信息
message CatResponse {
Cat cat = 1;
string message = 2;
}
// ListCatsRequest 用于请求猫咪列表
message ListCatsRequest {
int32 limit = 1;
int32 offset = 2;
}
// ListCatsResponse 返回猫咪列表
message ListCatsResponse {
repeated Cat cats = 1;
int32 total = 2;
}
// CreateCatRequest 用于创建新猫咪
message CreateCatRequest {
string name = 1;
string breed = 2;
int32 age = 3;
string color = 4;
}
步骤二:搭建 Go 服务端
1. 创建 Go 服务端目录
mkdir go-server
cd go-server
2. 初始化 Go 模块
go mod init grpc-demo
3. 安装依赖
go get google.golang.org/grpc
go get google.golang.org/protobuf
4. 生成 Go 代码
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
../proto/demo.proto
执行后会在当前目录生成 proto/ 文件夹,包含:
demo.pb.godemo_grpc.pb.go
5. 创建服务端主程序
创建文件 go-server/main.go:
package main
import (
"context"
"fmt"
"log"
"net"
"sync"
pb "grpc-demo/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// server 实现 CatService gRPC 服务
type server struct {
pb.UnimplementedCatServiceServer
cats map[int64]*pb.Cat
nextID int64
mu sync.RWMutex
}
// newServer 创建并初始化一个新的服务器,带有示例数据
func newServer() *server {
s := &server{
cats: make(map[int64]*pb.Cat),
nextID: 1,
}
// 添加一些示例猫咪数据
s.cats[1] = &pb.Cat{
Id: 1,
Name: "Whiskers",
Breed: "波斯猫",
Age: 3,
Color: "白色",
}
s.cats[2] = &pb.Cat{
Id: 2,
Name: "Shadow",
Breed: "暹罗猫",
Age: 5,
Color: "黑色",
}
s.nextID = 3
return s
}
// GetCat 根据 ID 获取猫咪
func (s *server) GetCat(ctx context.Context, req *pb.CatRequest) (*pb.CatResponse, error) {
s.mu.RLock()
defer s.mu.RUnlock()
log.Printf("收到 GetCat 请求,ID: %d", req.GetId())
cat, exists := s.cats[req.GetId()]
if !exists {
return nil, status.Errorf(codes.NotFound, "未找到 ID 为 %d 的猫咪", req.GetId())
}
return &pb.CatResponse{
Cat: cat,
Message: "成功找到猫咪",
}, nil
}
// ListCats 返回猫咪列表,支持分页
func (s *server) ListCats(ctx context.Context, req *pb.ListCatsRequest) (*pb.ListCatsResponse, error) {
s.mu.RLock()
defer s.mu.RUnlock()
log.Printf("收到 ListCats 请求 - Limit: %d, Offset: %d", req.GetLimit(), req.GetOffset())
var cats []*pb.Cat
for _, cat := range s.cats {
cats = append(cats, cat)
}
// 应用分页
offset := int(req.GetOffset())
limit := int(req.GetLimit())
if limit == 0 {
limit = 10 // 默认限制
}
total := len(cats)
if offset >= total {
return &pb.ListCatsResponse{
Cats: []*pb.Cat{},
Total: int32(total),
}, nil
}
end := offset + limit
if end > total {
end = total
}
paginatedCats := cats[offset:end]
return &pb.ListCatsResponse{
Cats: paginatedCats,
Total: int32(total),
}, nil
}
// CreateCat 创建新的猫咪
func (s *server) CreateCat(ctx context.Context, req *pb.CreateCatRequest) (*pb.CatResponse, error) {
s.mu.Lock()
defer s.mu.Unlock()
log.Printf("收到 CreateCat 请求 - Name: %s, Breed: %s", req.GetName(), req.GetBreed())
// 验证输入
if req.GetName() == "" {
return nil, status.Error(codes.InvalidArgument, "猫咪名称不能为空")
}
newCat := &pb.Cat{
Id: s.nextID,
Name: req.GetName(),
Breed: req.GetBreed(),
Age: req.GetAge(),
Color: req.GetColor(),
}
s.cats[s.nextID] = newCat
s.nextID++
return &pb.CatResponse{
Cat: newCat,
Message: "成功创建猫咪",
}, nil
}
func main() {
// 在 50051 端口创建 TCP 监听器
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("监听失败: %v", err)
}
// 创建新的 gRPC 服务器
grpcServer := grpc.NewServer()
// 注册 CatService 实现
pb.RegisterCatServiceServer(grpcServer, newServer())
log.Println("🚀 gRPC 服务器正在 50051 端口运行...")
fmt.Println("服务器准备接受连接")
// 开始服务
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("服务启动失败: %v", err)
}
}
6. 整理依赖
go mod tidy
7. 测试运行服务端
go run main.go
如果看到以下输出,说明服务端启动成功:
🚀 gRPC 服务器正在 50051 端口运行...
服务器准备接受连接
保持服务端运行,打开新的终端窗口继续下一步。
步骤三:搭建 TypeScript 客户端
1. 创建 TypeScript 客户端目录
在新的终端窗口中:
cd grpc-demo # 确保在项目根目录
mkdir ts-client
cd ts-client
2. 初始化 npm 项目
npm init -y
3. 创建 package.json
编辑 package.json 文件,替换为以下内容:
{
"name": "grpc-typescript-demo",
"version": "1.0.0",
"description": "TypeScript gRPC 客户端示例",
"main": "dist/src/client.js",
"scripts": {
"proto:generate": "npm run proto:js && npm run proto:ts",
"proto:js": "grpc_tools_node_protoc --plugin=protoc-gen-grpc=./node_modules/.bin/grpc_tools_node_protoc_plugin --js_out=import_style=commonjs,binary:./generated --grpc_out=grpc_js:./generated --proto_path=../proto ../proto/*.proto",
"proto:ts": "grpc_tools_node_protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --ts_out=grpc_js:./generated --proto_path=../proto ../proto/*.proto",
"build": "tsc",
"start": "node dist/src/client.js",
"test": "node dist/src/test.js",
"dev": "ts-node src/client.ts",
"dev:test": "ts-node src/test.ts"
},
"keywords": ["grpc", "typescript", "demo"],
"author": "",
"license": "MIT",
"dependencies": {
"@grpc/grpc-js": "^1.13.4",
"@grpc/proto-loader": "^0.7.15",
"@types/node": "^24.0.12",
"typescript": "^5.8.3"
},
"devDependencies": {
"grpc-tools": "^1.13.0",
"grpc_tools_node_protoc_ts": "^5.3.3",
"ts-node": "^10.9.2"
}
}
4. 安装依赖
npm install
5. 创建 TypeScript 配置
创建文件 tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"moduleResolution": "node"
},
"include": ["src/**/*", "generated/**/*"],
"exclude": ["node_modules", "dist"]
}
6. 生成 TypeScript 代码
npm run proto:generate
执行后会在 generated/ 目录生成四个文件:
demo_pb.jsdemo_pb.d.tsdemo_grpc_pb.jsdemo_grpc_pb.d.ts
7. 创建客户端源代码目录
mkdir src
8. 创建客户端主程序
创建文件 src/client.ts:
import * as grpc from '@grpc/grpc-js';
import { CatServiceClient } from '../generated/demo_grpc_pb';
import {
CatRequest,
CatResponse,
ListCatsRequest,
ListCatsResponse,
CreateCatRequest,
Cat
} from '../generated/demo_pb';
/**
* CatClient 封装 gRPC 客户端以便更容易使用
*/
export class CatClient {
private client: CatServiceClient;
constructor(serverAddress: string, credentials?: grpc.ChannelCredentials) {
this.client = new CatServiceClient(
serverAddress,
credentials ?? grpc.ChannelCredentials.createInsecure()
);
}
/**
* 根据 ID 获取猫咪
*/
public async getCat(id: number): Promise<Cat.AsObject> {
return new Promise((resolve, reject) => {
const request = new CatRequest();
request.setId(id);
this.client.getCat(request, (err, response) => {
if (err) {
reject(err);
return;
}
const cat = response.getCat();
if (!cat) {
reject(new Error('响应中没有找到猫咪'));
return;
}
console.log(`✅ ${response.getMessage()}`);
resolve(cat.toObject());
});
});
}
/**
* 获取所有猫咪列表,支持分页
*/
public async listCats(limit: number = 10, offset: number = 0): Promise<ListCatsResponse.AsObject> {
return new Promise((resolve, reject) => {
const request = new ListCatsRequest();
request.setLimit(limit);
request.setOffset(offset);
this.client.listCats(request, (err, response) => {
if (err) {
reject(err);
return;
}
resolve(response.toObject());
});
});
}
/**
* 创建新的猫咪
*/
public async createCat(
name: string,
breed: string,
age: number,
color: string
): Promise<Cat.AsObject> {
return new Promise((resolve, reject) => {
const request = new CreateCatRequest();
request.setName(name);
request.setBreed(breed);
request.setAge(age);
request.setColor(color);
this.client.createCat(request, (err, response) => {
if (err) {
reject(err);
return;
}
const cat = response.getCat();
if (!cat) {
reject(new Error('响应中没有找到猫咪'));
return;
}
console.log(`✅ ${response.getMessage()}`);
resolve(cat.toObject());
});
});
}
/**
* 关闭 gRPC 连接
*/
public close(): void {
this.client.close();
}
}
// 示例用法
async function main() {
const client = new CatClient('localhost:50051');
try {
console.log('🐱 === 猫咪服务演示 ===\n');
// 测试 1: 根据 ID 获取猫咪
console.log('📋 测试 1: 根据 ID 获取猫咪');
const cat = await client.getCat(1);
console.log('猫咪详情:', cat);
console.log('');
// 测试 2: 列出所有猫咪
console.log('📋 测试 2: 列出所有猫咪');
const catsList = await client.listCats(10, 0);
console.log(`猫咪总数: ${catsList.total}`);
catsList.catsList.forEach((c) => {
console.log(` - ${c.name} (${c.breed}), 年龄: ${c.age}, 颜色: ${c.color}`);
});
console.log('');
// 测试 3: 创建新猫咪
console.log('📋 测试 3: 创建新猫咪');
const newCat = await client.createCat('Fluffy', '缅因猫', 2, '橙色');
console.log('新创建的猫咪:', newCat);
console.log('');
// 测试 4: 验证新猫咪已创建
console.log('📋 测试 4: 验证新猫咪在列表中');
const updatedList = await client.listCats(10, 0);
console.log(`创建后的猫咪总数: ${updatedList.total}`);
console.log('');
console.log('✅ 所有测试完成!');
} catch (error: any) {
console.error('❌ 错误:', error.message);
if (error.code) {
console.error('错误代码:', error.code);
}
} finally {
client.close();
}
}
// 运行示例
if (require.main === module) {
main();
}
9. 创建测试程序
创建文件 src/test.ts:
import { CatClient } from './client';
/**
* CatClient 综合测试套件
*/
class CatServiceTester {
private client: CatClient;
constructor(serverAddress: string) {
this.client = new CatClient(serverAddress);
}
async runAllTests(): Promise<void> {
console.log('🧪 开始猫咪服务测试套件\n');
console.log('=' .repeat(50));
try {
await this.testGetCat();
await this.testListCats();
await this.testCreateCat();
await this.testErrorHandling();
await this.testPagination();
console.log('\n' + '='.repeat(50));
console.log('✅ 所有测试通过!');
} catch (error: any) {
console.error('\n❌ 测试套件失败:', error.message);
throw error;
} finally {
this.client.close();
}
}
private async testGetCat(): Promise<void> {
console.log('\n🧪 测试: GetCat');
console.log('-'.repeat(50));
try {
const cat = await this.client.getCat(1);
console.log(`✓ 成功获取猫咪: ${cat.name}`);
console.log(` 品种: ${cat.breed}, 年龄: ${cat.age}, 颜色: ${cat.color}`);
if (!cat.id || !cat.name) {
throw new Error('接收到的猫咪数据无效');
}
} catch (error: any) {
console.error('✗ GetCat 测试失败:', error.message);
throw error;
}
}
private async testListCats(): Promise<void> {
console.log('\n🧪 测试: ListCats');
console.log('-'.repeat(50));
try {
const result = await this.client.listCats(10, 0);
console.log(`✓ 获取到 ${result.catsList.length} 只猫咪 (总数: ${result.total})`);
result.catsList.forEach((cat, index) => {
console.log(` ${index + 1}. ${cat.name} - ${cat.breed}`);
});
if (result.total < 1) {
throw new Error('期望列表中至少有一只猫咪');
}
} catch (error: any) {
console.error('✗ ListCats 测试失败:', error.message);
throw error;
}
}
private async testCreateCat(): Promise<void> {
console.log('\n🧪 测试: CreateCat');
console.log('-'.repeat(50));
try {
const newCat = await this.client.createCat(
'TestCat',
'英国短毛猫',
4,
'灰色'
);
console.log(`✓ 创建新猫咪: ${newCat.name} (ID: ${newCat.id})`);
console.log(` 品种: ${newCat.breed}, 年龄: ${newCat.age}, 颜色: ${newCat.color}`);
if (!newCat.id || !newCat.name) {
throw new Error('创建后的猫咪数据无效');
}
} catch (error: any) {
console.error('✗ CreateCat 测试失败:', error.message);
throw error;
}
}
private async testErrorHandling(): Promise<void> {
console.log('\n🧪 测试: 错误处理');
console.log('-'.repeat(50));
try {
// 尝试获取不存在的猫咪 ID
await this.client.getCat(99999);
throw new Error('期望对无效猫咪 ID 抛出错误');
} catch (error: any) {
if (error.message.includes('未找到') || error.message.includes('not found')) {
console.log('✓ 错误处理对无效 ID 正常工作');
} else {
console.error('✗ 未预期的错误:', error.message);
throw error;
}
}
}
private async testPagination(): Promise<void> {
console.log('\n🧪 测试: 分页');
console.log('-'.repeat(50));
try {
// 第一页
const page1 = await this.client.listCats(2, 0);
console.log(`✓ 第 1 页: 获取到 ${page1.catsList.length} 只猫咪`);
// 第二页
const page2 = await this.client.listCats(2, 2);
console.log(`✓ 第 2 页: 获取到 ${page2.catsList.length} 只猫咪`);
if (page1.catsList.length > 2 || page2.catsList.length > 2) {
throw new Error('分页限制未生效');
}
console.log('✓ 分页功能正常工作');
} catch (error: any) {
console.error('✗ 分页测试失败:', error.message);
throw error;
}
}
}
// 运行测试
async function main() {
const tester = new CatServiceTester('localhost:50051');
await tester.runAllTests();
}
if (require.main === module) {
main().catch((error) => {
console.error('致命错误:', error);
process.exit(1);
});
}
10. 编译 TypeScript 代码
npm run build
编译成功后会在 dist/ 目录生成 JavaScript 文件。
步骤四:运行测试
1. 确保 Go 服务端正在运行
在第一个终端窗口(go-server 目录):
go run main.go
应该看到:
🚀 gRPC 服务器正在 50051 端口运行...
服务器准备接受连接
2. 运行 TypeScript 客户端
在第二个终端窗口(ts-client 目录):
npm start
预期输出:
🐱 === 猫咪服务演示 ===
📋 测试 1: 根据 ID 获取猫咪
✅ 成功找到猫咪
猫咪详情: { id: 1, name: 'Whiskers', breed: '波斯猫', age: 3, color: '白色' }
📋 测试 2: 列出所有猫咪
猫咪总数: 2
- Whiskers (波斯猫), 年龄: 3, 颜色: 白色
- Shadow (暹罗猫), 年龄: 5, 颜色: 黑色
📋 测试 3: 创建新猫咪
✅ 成功创建猫咪
新创建的猫咪: { id: 3, name: 'Fluffy', breed: '缅因猫', age: 2, color: '橙色' }
📋 测试 4: 验证新猫咪在列表中
创建后的猫咪总数: 3
✅ 所有测试完成!
3. 运行完整测试套件
npm run test
预期输出:
🧪 开始猫咪服务测试套件
==================================================
🧪 测试: GetCat
--------------------------------------------------
✅ 成功找到猫咪
✓ 成功获取猫咪: Whiskers
品种: 波斯猫, 年龄: 3, 颜色: 白色
🧪 测试: ListCats
--------------------------------------------------
✓ 获取到 2 只猫咪 (总数: 2)
1. Whiskers - 波斯猫
2. Shadow - 暹罗猫
🧪 测试: CreateCat
--------------------------------------------------
✅ 成功创建猫咪
✓ 创建新猫咪: TestCat (ID: 3)
品种: 英国短毛猫, 年龄: 4, 颜色: 灰色
🧪 测试: 错误处理
--------------------------------------------------
✓ 错误处理对无效 ID 正常工作
🧪 测试: 分页
--------------------------------------------------
✓ 第 1 页: 获取到 2 只猫咪
✓ 第 2 页: 获取到 1 只猫咪
✓ 分页功能正常工作
==================================================
✅ 所有测试通过!
故障排查
问题 1: "protoc: command not found"
解决方案:
# macOS
brew install protobuf
# Ubuntu/Debian
sudo apt-get install -y protobuf-compiler
# 验证安装
protoc --version
问题 2: "protoc-gen-go: program not found or is not executable"
解决方案:
# 安装 Go 插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# 添加到 PATH
export PATH="$PATH:$(go env GOPATH)/bin"
# 永久添加到 shell 配置
echo 'export PATH="$PATH:$(go env GOPATH)/bin"' >> ~/.zshrc
source ~/.zshrc
问题 3: "Cannot find module '../generated/demogrpcpb'"
解决方案:
cd ts-client
npm run proto:generate
npm run build
问题 4: "Connection refused" 或连接失败
解决方案:
- 确保 Go 服务端正在运行
- 检查端口 50051 是否被占用:
# macOS/Linux
lsof -i :50051
# 如果端口被占用,杀死进程
lsof -ti:50051 | xargs kill -9
问题 5: Go 模块相关问题
解决方案:
cd go-server
rm -rf go.mod go.sum
go mod init grpc-demo
go get google.golang.org/grpc
go get google.golang.org/protobuf
go mod tidy
问题 6: npm 依赖安装失败
解决方案:
cd ts-client
rm -rf node_modules package-lock.json
npm cache clean --force
npm install
问题 7: TypeScript 编译错误
解决方案:
在 tsconfig.json 中添加:
{
"compilerOptions": {
"skipLibCheck": true
}
}
然后重新编译:
npm run build
快速命令参考
Go 服务端命令
cd go-server
# 生成 proto 文件
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
../proto/demo.proto
# 下载依赖
go mod download
go mod tidy
# 运行服务端
go run main.go
# 编译二进制文件
go build -o server main.go
./server
TypeScript 客户端命令
cd ts-client
# 安装依赖
npm install
# 生成 proto 文件
npm run proto:generate
# 编译 TypeScript
npm run build
# 运行客户端
npm start
# 运行测试
npm run test
# 开发模式(不需要编译)
npm run dev
npm run dev:test
# 清理生成的文件
rm -rf dist generated
项目扩展建议
1. 添加新的 RPC 方法
- 编辑
proto/demo.proto添加新的 service 方法 - 重新生成代码(Go 和 TypeScript)
- 在
go-server/main.go中实现方法 - 在
ts-client/src/client.ts中添加客户端方法
2. 实现流式 RPC
gRPC 支持四种类型的 RPC:
- 简单 RPC(本教程使用的)
- 服务端流式 RPC
- 客户端流式 RPC
- 双向流式 RPC
3. 添加认证和授权
在生产环境中,建议使用 SSL/TLS 和认证机制。
4. 添加中间件
- 日志记录
- 指标收集
- 错误处理
- 请求追踪
5. 健康检查
实现 gRPC 健康检查协议,方便服务发现和负载均衡。
性能优化建议
- 连接池:复用 gRPC 连接
- 超时设置:为每个 RPC 调用设置合理的超时
- 重试机制:实现智能重试策略
- 负载均衡:使用 gRPC 的负载均衡功能
- 压缩:启用 gRPC 消息压缩
相关资源
总结
恭喜!🎉 您已经成功创建了一个完整的 gRPC 通信系统:
- ✅ 使用 Protocol Buffers 定义服务接口
- ✅ 实现了 Go gRPC 服务端
- ✅ 实现了 TypeScript gRPC 客户端
- ✅ 完成了端到端测试
这个示例展示了 gRPC 的基本用法,您可以在此基础上:
- 添加更多的服务方法
- 实现流式 RPC
- 添加认证和安全机制
- 集成到实际项目中
如果遇到任何问题,请参考故障排查部分,或查阅官方文档。