<!-- title: Claude Code + gRPC微服务全链路实战 -->

# Claude Code + gRPC微服务通信：从Protobuf到流式RPC全链路实战

## 概述

本教程面向中高级后端开发者，教你如何利用 Claude Code 从零构建一套完整的 gRPC 微服务通信系统。你将掌握：用 Claude Code 高效编写 Protobuf 定义、生成跨语言骨架代码、实现四种 RPC 通信模式（Unary/Server Streaming/Client Streaming/Bidi Streaming）、编写拦截器与错误处理、以及负载测试验证。全程使用 Claude Code 作为 AI 结对编程伙伴，每个步骤都附带可直接复制的 Prompt。

**适用人群**：有 Node.js 或 Go 基础、想用 gRPC 替代 REST 提升微服务间通信效率的后端开发者。

**学习收益**：完成本教程后，你将独立交付一个具备健康检查、流式通信、拦截器中间件的 gRPC 生产级微服务。

---

## 前置条件

| 工具 | 版本要求 | 安装命令 |
|------|---------|---------|
| Node.js | ≥ 20.x | `nvm install 20 && nvm use 20` |
| Claude Code | 最新版 | `npm install -g @anthropic-ai/claude-code` |
| Protocol Buffers 编译器 | ≥ 3.21 | `brew install protobuf`（macOS）/ `apt install protobuf-compiler`（Linux） |
| grpcurl | 最新版 | `brew install grpcurl`（调试用） |
| Docker | ≥ 24.x | 可选，用于容器化部署 |

---

## 第一步：理解 gRPC 四种通信模式

gRPC 基于 HTTP/2 和 Protocol Buffers，提供四种通信模式。用 Claude Code 快速生成一张对比表：

```
请用表格对比 gRPC 的四种通信模式：Unary RPC、Server Streaming、
Client Streaming、Bidirectional Streaming。包含：定义、适用场景、
数据流向、类比。
```

**四种模式速查**：

```
┌──────────────────┬────────────────────┬───────────────────┐
│ 模式              │ 数据流向            │ 典型场景           │
├──────────────────┼────────────────────┼───────────────────┤
│ Unary RPC        │ 请求→响应（一对一）  │ 用户登录、查订单   │
│ Server Streaming │ 请求→流式响应       │ 实时日志、股票行情  │
│ Client Streaming │ 流式请求→响应       │ 文件上传、批量写入  │
│ Bidi Streaming   │ 双向流式通信        │ 聊天、实时协作     │
└──────────────────┴────────────────────┴───────────────────┘
```

**为什么选 gRPC 而非 REST？** 根据社区基准测试数据，gRPC 在微服务内部通信场景下，延迟降低 30-50%，吞吐量提升 2-10 倍。核心优势来自 Protobuf 二进制序列化（比 JSON 体积小 3-10 倍）和 HTTP/2 多路复用。

---

## 第二步：项目初始化

在终端中运行 Claude Code 并创建项目结构：

```bash
mkdir grpc-order-service && cd grpc-order-service
claude
```

进入 Claude Code 交互模式后，发送以下 Prompt：

```
请为一个 gRPC 订单微服务创建 Node.js 项目结构，要求：
1. 使用 TypeScript
2. 包含 proto 定义目录 proto/
3. 生成的代码目录 src/generated/
4. 服务端代码 src/server/
5. 客户端代码 src/client/
6. 共享工具 src/shared/
7. 配置好 tsconfig.json 和 package.json
8. 添加 @grpc/grpc-js、@grpc/proto-loader、protobufjs 依赖
```

**Claude Code 将生成的目录结构**：

```
grpc-order-service/
├── proto/
│   └── order.proto          # Protobuf 服务定义
├── src/
│   ├── generated/            # 自动生成的代码（不手动编辑）
│   ├── server/
│   │   ├── index.ts          # 服务入口
│   │   ├── orderService.ts   # 订单服务实现
│   │   └── interceptors.ts   # 拦截器
│   ├── client/
│   │   └── index.ts          # 客户端调用示例
│   └── shared/
│       └── errors.ts         # 统一错误码
├── tests/
│   └── order.test.ts         # 单元测试
├── package.json
├── tsconfig.json
└── CLAUDE.md                 # Claude Code 项目上下文
```

**安装依赖**：

```bash
npm install @grpc/grpc-js @grpc/proto-loader protobufjs
npm install -D typescript @types/node tsx vitest
```

**预期结果**：项目初始化完成，`npm run build` 可正常通过。

**常见问题**：

| 问题 | 原因 | 解决方案 |
|------|------|---------|
| `protoc not found` | 未安装 protobuf 编译器 | `brew install protobuf` 或 `apt install protobuf-compiler` |
| TypeScript 路径报错 | tsconfig paths 未配置 | 在 `tsconfig.json` 中添加 `baseUrl` 和 `paths` |

---

## 第三步：编写 Protobuf 服务定义

这是 gRPC 开发的核心一步——先定义接口契约，再生成代码。在 Claude Code 中：

```
请为电商订单服务编写 proto/order.proto 文件，要求：
1. 包名 order.v1
2. 服务 OrderService 包含以下 RPC：
   - CreateOrder（Unary）：创建订单
   - GetOrder（Unary）：查询订单
   - ListOrders（Server Streaming）：流式返回订单列表
   - UploadOrderItems（Client Streaming）：批量上传订单商品
   - OrderChat（Bidirectional Streaming）：订单实时讨论
3. 定义完整的 Message：Order、OrderItem、CreateOrderRequest/Response 等
4. 包含字段验证注释和字段编号规划
5. 使用 google.protobuf.Timestamp 和 Empty
```

**Claude Code 生成的 `proto/order.proto`**：

```protobuf
syntax = "proto3";

package order.v1;

import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";

option go_package = "github.com/example/order/v1;orderv1";

// 订单状态枚举
enum OrderStatus {
  ORDER_STATUS_UNSPECIFIED = 0;
  ORDER_STATUS_PENDING = 1;
  ORDER_STATUS_CONFIRMED = 2;
  ORDER_STATUS_SHIPPED = 3;
  ORDER_STATUS_DELIVERED = 4;
  ORDER_STATUS_CANCELLED = 5;
}

// 订单商品
message OrderItem {
  string product_id = 1;
  string product_name = 2;
  int32 quantity = 3;
  double unit_price = 4;
}

// 订单主体
message Order {
  string order_id = 1;
  string user_id = 2;
  repeated OrderItem items = 3;
  OrderStatus status = 4;
  double total_amount = 5;
  string shipping_address = 6;
  google.protobuf.Timestamp created_at = 7;
  google.protobuf.Timestamp updated_at = 8;
}

// === Unary RPC: CreateOrder ===
message CreateOrderRequest {
  string user_id = 1;
  repeated OrderItem items = 2;
  string shipping_address = 3;
}

message CreateOrderResponse {
  Order order = 1;
}

// === Unary RPC: GetOrder ===
message GetOrderRequest {
  string order_id = 1;
}

message GetOrderResponse {
  Order order = 1;
}

// === Server Streaming RPC: ListOrders ===
message ListOrdersRequest {
  string user_id = 1;
  OrderStatus filter_status = 2;  // 可选状态过滤
}

// 流式返回，每条消息即一个订单
message ListOrdersResponse {
  Order order = 1;
}

// === Client Streaming RPC: UploadOrderItems ===
message UploadOrderItemRequest {
  string order_id = 1;
  OrderItem item = 2;
}

message UploadOrderItemsResponse {
  string order_id = 1;
  int32 total_items = 2;
  double total_amount = 3;
}

// === Bidi Streaming RPC: OrderChat ===
message OrderChatMessage {
  string order_id = 1;
  string sender = 2;
  string message = 3;
  google.protobuf.Timestamp sent_at = 4;
}

// 订单服务定义
service OrderService {
  // Unary: 创建订单
  rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);
  
  // Unary: 查询订单
  rpc GetOrder(GetOrderRequest) returns (GetOrderResponse);
  
  // Server Streaming: 流式列出用户订单
  rpc ListOrders(ListOrdersRequest) returns (stream ListOrdersResponse);
  
  // Client Streaming: 批量上传订单商品
  rpc UploadOrderItems(stream UploadOrderItemRequest) returns (UploadOrderItemsResponse);
  
  // Bidi Streaming: 订单实时讨论
  rpc OrderChat(stream OrderChatMessage) returns (stream OrderChatMessage);
}
```

**Protobuf 设计最佳实践**（Claude Code 可帮你自动检查）：

1. **字段编号不可复用**——一旦发布，编号即冻结。删除的字段用 `reserved` 保留
2. **用 `optional` 标识可空字段**——proto3 默认所有字段都有零值
3. **包名带版本号**——`order.v1` 方便未来 `order.v2` 演进
4. **Message 命名遵循 Request/Response 模式**——增强可读性

---

## 第四步：生成 TypeScript 类型与 gRPC 骨架

```
请编写一个脚本 scripts/generate.sh，功能：
1. 使用 grpc_tools_node_protoc 生成 TypeScript 类型定义
2. 使用 grpc_tools_node_protoc_ts 生成服务桩
3. 输出到 src/generated/ 目录
4. 同时生成客户端和服务端存根
```

**生成脚本 `scripts/generate.sh`**：

```bash
#!/bin/bash
set -e

PROTO_DIR="proto"
OUT_DIR="src/generated"

mkdir -p "$OUT_DIR"

# 安装生成工具
npm install -D grpc-tools grpc-tools-ts

# 生成 JS 代码和 TypeScript 类型
npx grpc_tools_node_protoc \
  --js_out=import_style=commonjs,binary:"$OUT_DIR" \
  --grpc_out=grpc_js:"$OUT_DIR" \
  --plugin=protoc-gen-grpc=$(npm bin)/grpc_tools_node_protoc_plugin \
  -I "$PROTO_DIR" \
  "$PROTO_DIR"/*.proto

# 生成 TypeScript d.ts 类型声明
npx grpc_tools_node_protoc \
  --plugin=protoc-gen-ts=$(npm bin)/protoc-gen-ts \
  --ts_out=grpc_js:"$OUT_DIR" \
  -I "$PROTO_DIR" \
  "$PROTO_DIR"/*.proto

echo "✅ Proto 代码生成完成: $OUT_DIR/"
```

运行生成：

```bash
chmod +x scripts/generate.sh
./scripts/generate.sh
```

**预期输出**：

```
src/generated/
├── order_pb.js          # Protobuf 消息序列化/反序列化
├── order_pb.d.ts        # TypeScript 类型定义
├── OrderService_grpc_pb.js   # gRPC 服务端/客户端存根
└── OrderService_grpc_pb.d.ts
```

**常见问题**：

| 问题 | 解决方案 |
|------|---------|
| `protoc-gen-ts: not found` | 运行 `npm install -D grpc-tools-ts` |
| 生成代码中 `import` 路径错误 | 检查 `--js_out` 的 `import_style` 参数 |
| 类型推断不完整 | 使用 `@grpc/proto-loader` 动态加载作为补充方案 |

---

## 第五步：实现服务端——Unary RPC

```
请基于生成的 gRPC 存根，实现 src/server/orderService.ts：
1. 实现 CreateOrder 和 GetOrder 两个 Unary RPC
2. 使用内存 Map 模拟数据存储
3. 生成 UUID 作为订单 ID
4. 包含完整的错误处理（NOT_FOUND、INVALID_ARGUMENT）
5. 添加日志记录
```

**服务实现 `src/server/orderService.ts`**：

```typescript
import * as grpc from '@grpc/grpc-js';
import { v4 as uuidv4 } from 'uuid';
import {
  OrderServiceService,
  IOrderServiceServer,
} from '../generated/OrderService_grpc_pb';
import {
  Order,
  OrderItem,
  CreateOrderRequest,
  CreateOrderResponse,
  GetOrderRequest,
  GetOrderResponse,
  ListOrdersRequest,
  ListOrdersResponse,
  UploadOrderItemRequest,
  UploadOrderItemsResponse,
  OrderChatMessage,
} from '../generated/order_pb';

// 内存数据存储
const orders = new Map<string, Order>();

// === Unary RPC: CreateOrder ===
function createOrder(
  call: grpc.ServerUnaryCall<CreateOrderRequest, CreateOrderResponse>,
  callback: grpc.sendUnaryData<CreateOrderResponse>
) {
  const req = call.request;
  const userId = req.getUserId();
  const items = req.getItemsList();
  const address = req.getShippingAddress();

  // 参数校验
  if (!userId) {
    return callback({
      code: grpc.status.INVALID_ARGUMENT,
      message: 'user_id is required',
    } as grpc.ServiceError);
  }

  if (items.length === 0) {
    return callback({
      code: grpc.status.INVALID_ARGUMENT,
      message: 'order must contain at least one item',
    } as grpc.ServiceError);
  }

  // 计算总金额
  const totalAmount = items.reduce(
    (sum, item) => sum + item.getQuantity() * item.getUnitPrice(),
    0
  );

  // 构建订单
  const order = new Order();
  order.setOrderId(uuidv4());
  order.setUserId(userId);
  order.setItemsList(items);
  order.setStatus(1); // PENDING
  order.setTotalAmount(totalAmount);
  order.setShippingAddress(address);
  order.setCreatedAt(new Date().toISOString());
  order.setUpdatedAt(new Date().toISOString());

  // 存储
  orders.set(order.getOrderId(), order);

  console.log(`[CreateOrder] 订单创建成功: ${order.getOrderId()}, 金额: ¥${totalAmount}`);

  const response = new CreateOrderResponse();
  response.setOrder(order);
  callback(null, response);
}

// === Unary RPC: GetOrder ===
function getOrder(
  call: grpc.ServerUnaryCall<GetOrderRequest, GetOrderResponse>,
  callback: grpc.sendUnaryData<GetOrderResponse>
) {
  const orderId = call.request.getOrderId();

  if (!orderId) {
    return callback({
      code: grpc.status.INVALID_ARGUMENT,
      message: 'order_id is required',
    } as grpc.ServiceError);
  }

  const order = orders.get(orderId);
  if (!order) {
    return callback({
      code: grpc.status.NOT_FOUND,
      message: `Order not found: ${orderId}`,
    } as grpc.ServiceError);
  }

  console.log(`[GetOrder] 查询订单: ${orderId}`);

  const response = new GetOrderResponse();
  response.setOrder(order);
  callback(null, response);
}

// 导出服务定义
export const orderServiceHandlers: IOrderServiceServer = {
  createOrder,
  getOrder,
  // 流式 RPC 在下一步实现
} as unknown as IOrderServiceServer;
```

**服务入口 `src/server/index.ts`**：

```typescript
import * as grpc from '@grpc/grpc-js';
import { OrderServiceService } from '../generated/OrderService_grpc_pb';
import { orderServiceHandlers } from './orderService';

const PORT = process.env.GRPC_PORT || '50051';

const server = new grpc.Server();

// 注册服务
server.addService(OrderServiceService, orderServiceHandlers as any);

// 启动
server.bindAsync(
  `0.0.0.0:${PORT}`,
  grpc.ServerCredentials.createInsecure(),
  (err, port) => {
    if (err) {
      console.error('服务启动失败:', err);
      process.exit(1);
    }
    console.log(`✅ gRPC OrderService 运行在端口 ${port}`);
    server.start();
  }
);

// 优雅关闭
process.on('SIGTERM', () => {
  console.log('正在关闭 gRPC 服务...');
  server.tryShutdown(() => {
    console.log('服务已关闭');
    process.exit(0);
  });
});
```

启动服务：

```bash
npx tsx src/server/index.ts
# 输出: ✅ gRPC OrderService 运行在端口 50051
```

用 `grpcurl` 快速验证：

```bash
# 列出服务（需启用 reflection，后文实现）
grpcurl -plaintext localhost:50051 list

# 创建订单
grpcurl -plaintext -d '{
  "user_id": "u-001",
  "items": [{"product_id": "p-001", "product_name": "键盘", "quantity": 2, "unit_price": 299}],
  "shipping_address": "北京市海淀区"
}' localhost:50051 order.v1.OrderService/CreateOrder
```

---

## 第六步：实现流式 RPC

这是 gRPC 的精华所在。让 Claude Code 帮你一次性实现三种流式模式：

```
请在 src/server/orderService.ts 中补充实现以下流式 RPC：
1. ListOrders（Server Streaming）: 按用户ID查询订单，每条订单单独推送
2. UploadOrderItems（Client Streaming）: 接收客户端流式上传的商品，汇总返回
3. OrderChat（Bidirectional Streaming）: 双向实时消息通信

要求：
- Server Streaming 每推送一条调用 call.write()，注意 call.emit('error') 处理
- Client Streaming 在 call.on('data') 中累积，call.on('end') 中汇总返回
- Bidi Streaming 用 call.on('data') + call.write() 实现实时交互
- 所有流式 RPC 都要有超时和错误处理
```

**流式 RPC 实现**：

```typescript
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';

// === Server Streaming RPC: ListOrders ===
function listOrders(call: grpc.ServerWritableStream<ListOrdersRequest, ListOrdersResponse>) {
  const userId = call.request.getUserId();
  const filterStatus = call.request.getFilterStatus();

  console.log(`[ListOrders] 流式查询订单: userId=${userId}, status=${filterStatus}`);

  let count = 0;
  orders.forEach((order) => {
    // 过滤条件
    if (userId && order.getUserId() !== userId) return;
    if (filterStatus && order.getStatus() !== filterStatus) return;

    const response = new ListOrdersResponse();
    response.setOrder(order);
    
    try {
      call.write(response);
      count++;
    } catch (err) {
      console.error('[ListOrders] 写入流失败:', err);
      call.emit('error', {
        code: grpc.status.INTERNAL,
        message: 'Stream write failed',
      });
      return;
    }
  });

  console.log(`[ListOrders] 共推送 ${count} 条订单`);
  call.end();
}

// === Client Streaming RPC: UploadOrderItems ===
function uploadOrderItems(
  call: grpc.ServerReadableStream<UploadOrderItemRequest, UploadOrderItemsResponse>,
  callback: grpc.sendUnaryData<UploadOrderItemsResponse>
) {
  const uploadedItems: OrderItem[] = [];
  let targetOrderId = '';

  call.on('data', (request: UploadOrderItemRequest) => {
    targetOrderId = request.getOrderId() || targetOrderId;
    const item = request.getItem();
    if (item) {
      uploadedItems.push(item);
      console.log(`[UploadOrderItems] 接收商品: ${item.getProductName()}`);
    }
  });

  call.on('error', (err) => {
    console.error('[UploadOrderItems] 流错误:', err);
    callback({
      code: grpc.status.INTERNAL,
      message: err.message,
    } as grpc.ServiceError);
  });

  call.on('end', () => {
    // 更新订单
    const order = orders.get(targetOrderId);
    if (!order) {
      return callback({
        code: grpc.status.NOT_FOUND,
        message: `Order not found: ${targetOrderId}`,
      } as grpc.ServiceError);
    }

    const existingItems = order.getItemsList();
    order.setItemsList([...existingItems, ...uploadedItems]);
    
    const totalAmount = order.getItemsList().reduce(
      (sum, item) => sum + item.getQuantity() * item.getUnitPrice(), 0
    );
    order.setTotalAmount(totalAmount);
    order.setUpdatedAt(new Date().toISOString());
    orders.set(targetOrderId, order);

    const response = new UploadOrderItemsResponse();
    response.setOrderId(targetOrderId);
    response.setTotalItems(order.getItemsList().length);
    response.setTotalAmount(totalAmount);

    console.log(`[UploadOrderItems] 上传完成: ${uploadedItems.length} 件商品`);
    callback(null, response);
  });
}

// === Bidirectional Streaming RPC: OrderChat ===
function orderChat(call: grpc.ServerDuplexStream<OrderChatMessage, OrderChatMessage>) {
  console.log('[OrderChat] 双向流已建立');

  call.on('data', (msg: OrderChatMessage) => {
    const orderId = msg.getOrderId();
    const sender = msg.getSender();
    const text = msg.getMessage();

    console.log(`[OrderChat] ${sender}@${orderId}: ${text}`);

    // 回传确认消息
    const reply = new OrderChatMessage();
    reply.setOrderId(orderId);
    reply.setSender('system');
    reply.setMessage(`[已收到] ${sender}: ${text}`);
    
    const ts = new Timestamp();
    ts.fromDate(new Date());
    reply.setSentAt(ts);

    call.write(reply);
  });

  call.on('error', (err) => {
    console.error('[OrderChat] 流错误:', err);
  });

  call.on('end', () => {
    console.log('[OrderChat] 客户端关闭连接');
    call.end();
  });
}
```

---

## 第七步：拦截器——gRPC 的中间件

拦截器是 gRPC 的"中间件"，用于日志、认证、限流等横切关注点。让 Claude Code 实现：

```
请编写 src/server/interceptors.ts，实现以下 gRPC 拦截器：
1. LoggingInterceptor: 记录每个 RPC 调用的方法名、耗时、状态码
2. AuthInterceptor: 从 metadata 中提取 token 并校验
3. ErrorInterceptor: 统一捕获异常并转换为标准 gRPC 状态码
使用 @grpc/grpc-js 的 ServerInterceptor 接口
```

**拦截器实现 `src/server/interceptors.ts`**：

```typescript
import * as grpc from '@grpc/grpc-js';
import { Status } from '@grpc/grpc-js/build/src/constants';

// 日志拦截器
export const loggingInterceptor: grpc.ServerInterceptor = (methodDescriptor, callStream) => {
  const methodName = `/${methodDescriptor.service.serviceName}/${methodDescriptor.methodName}`;
  const startTime = Date.now();

  const listener = new grpc.ServerListenerBuilder()
    .withOnReceiveMetadata((metadata, next) => {
      console.log(`[gRPC] --> ${methodName}`);
      next(metadata);
    })
    .withOnReceiveMessage((message, next) => {
      next(message);
    })
    .withOnHalfClose((next) => {
      next();
    })
    .withOnCancel((next) => {
      const duration = Date.now() - startTime;
      console.log(`[gRPC] <-- ${methodName} CANCELLED (${duration}ms)`);
      next();
    })
    .build();

  const responder = new grpc.ServerResponseBuilder()
    .withSendMetadata((metadata, next) => {
      next(metadata);
    })
    .withSendMessage((message, next) => {
      const duration = Date.now() - startTime;
      console.log(`[gRPC] <-- ${methodName} OK (${duration}ms)`);
      next(message);
    })
    .withSendStatus((status, next) => {
      const duration = Date.now() - startTime;
      console.log(
        `[gRPC] <-- ${methodName} ${status.code === Status.OK ? 'OK' : `ERROR(${status.code})`} (${duration}ms)`
      );
      next(status);
    })
    .build();

  return { listener, responder };
};

// 认证拦截器
export const authInterceptor: grpc.ServerInterceptor = (methodDescriptor, callStream) => {
  const methodName = methodDescriptor.methodName;
  
  // 健康检查无需认证
  const publicMethods = ['Health/Check', 'Health/Watch'];
  const isPublic = publicMethods.some((m) => methodName.includes(m));

  const listener = new grpc.ServerListenerBuilder()
    .withOnReceiveMetadata((metadata, next) => {
      if (isPublic) return next(metadata);

      const token = metadata.get('authorization')[0] as string;
      if (!token || !token.startsWith('Bearer ')) {
        callStream.sendStatus({
          code: grpc.status.UNAUTHENTICATED,
          details: 'Missing or invalid authorization token',
        });
        return;
      }

      // 简化认证逻辑（生产环境应校验 JWT）
      const authToken = token.replace('Bearer ', '');
      if (authToken !== 'valid-secret-token') {
        callStream.sendStatus({
          code: grpc.status.UNAUTHENTICATED,
          details: 'Invalid token',
        });
        return;
      }

      next(metadata);
    })
    .build();

  return { listener, responder: new grpc.ServerResponseBuilder().build() };
};
```

**注册拦截器到服务**：

```typescript
// 在 src/server/index.ts 中修改
const server = new grpc.Server({
  interceptors: [loggingInterceptor, authInterceptor],
});
```

---

## 第八步：实现客户端调用

```
请编写 src/client/index.ts，实现对 OrderService 的完整客户端调用：
1. Unary 调用：CreateOrder 和 GetOrder
2. Server Streaming 调用：ListOrders
3. Client Streaming 调用：UploadOrderItems
4. Bidi Streaming 调用：OrderChat
每种模式都要有清晰的日志输出
```

**客户端实现 `src/client/index.ts`**：

```typescript
import * as grpc from '@grpc/grpc-js';
import { OrderServiceClient } from '../generated/OrderService_grpc_pb';
import {
  CreateOrderRequest,
  OrderItem,
  GetOrderRequest,
  ListOrdersRequest,
  UploadOrderItemRequest,
  OrderChatMessage,
} from '../generated/order_pb';

const client = new OrderServiceClient(
  'localhost:50051',
  grpc.credentials.createInsecure()
);

// 添加认证 metadata
const metadata = new grpc.Metadata();
metadata.set('authorization', 'Bearer valid-secret-token');

// === 1. Unary: 创建订单 ===
function testCreateOrder(): Promise<string> {
  return new Promise((resolve, reject) => {
    const item = new OrderItem();
    item.setProductId('p-001');
    item.setProductName('机械键盘');
    item.setQuantity(2);
    item.setUnitPrice(399);

    const request = new CreateOrderRequest();
    request.setUserId('u-001');
    request.setItemsList([item]);
    request.setShippingAddress('北京市海淀区中关村大街1号');

    client.createOrder(request, metadata, (err, response) => {
      if (err) {
        console.error('创建订单失败:', err.message);
        return reject(err);
      }
      const order = response!.getOrder()!;
      console.log(`✅ 订单创建成功: ${order.getOrderId()}, 金额: ¥${order.getTotalAmount()}`);
      resolve(order.getOrderId());
    });
  });
}

// === 2. Server Streaming: 列出订单 ===
function testListOrders(userId: string) {
  const request = new ListOrdersRequest();
  request.setUserId(userId);

  const stream = client.listOrders(request, metadata);
  
  console.log('📦 开始接收订单流...');
  
  stream.on('data', (response) => {
    const order = response.getOrder()!;
    console.log(`  - 订单 ${order.getOrderId()}: ¥${order.getTotalAmount()}`);
  });

  stream.on('end', () => {
    console.log('📦 订单流结束');
  });

  stream.on('error', (err) => {
    console.error('流错误:', err.message);
  });
}

// === 3. Client Streaming: 批量上传商品 ===
function testUploadItems(orderId: string): Promise<void> {
  return new Promise((resolve) => {
    const stream = client.uploadOrderItems(metadata, (err, response) => {
      if (err) {
        console.error('上传失败:', err.message);
        return resolve();
      }
      console.log(`✅ 上传完成: ${response!.getTotalItems()} 件, 总计 ¥${response!.getTotalAmount()}`);
      resolve();
    });

    // 流式发送 3 件商品
    const products = [
      { id: 'p-010', name: '显示器', qty: 1, price: 2999 },
      { id: 'p-011', name: '鼠标', qty: 3, price: 149 },
      { id: 'p-012', name: '鼠标垫', qty: 2, price: 49 },
    ];

    products.forEach((p, i) => {
      const item = new OrderItem();
      item.setProductId(p.id);
      item.setProductName(p.name);
      item.setQuantity(p.qty);
      item.setUnitPrice(p.price);

      const request = new UploadOrderItemRequest();
      request.setOrderId(orderId);
      request.setItem(item);

      stream.write(request);
      console.log(`  ↑ 发送商品: ${p.name}`);
    });

    stream.end();
  });
}

// === 4. Bidi Streaming: 实时聊天 ===
function testOrderChat(orderId: string) {
  const stream = client.orderChat(metadata);

  stream.on('data', (msg: OrderChatMessage) => {
    console.log(`  ← ${msg.getSender()}: ${msg.getMessage()}`);
  });

  stream.on('end', () => {
    console.log('聊天结束');
  });

  // 发送 3 条消息
  const messages = ['请问什么时候发货？', '能改地址吗？', '谢谢！'];
  messages.forEach((text, i) => {
    setTimeout(() => {
      const msg = new OrderChatMessage();
      msg.setOrderId(orderId);
      msg.setSender('customer');
      msg.setMessage(text);
      stream.write(msg);
      console.log(`  → customer: ${text}`);

      if (i === messages.length - 1) {
        setTimeout(() => stream.end(), 500);
      }
    }, (i + 1) * 1000);
  });
}

// === 运行全部测试 ===
async function main() {
  console.log('🚀 开始 gRPC 客户端测试\n');

  // 1. 创建订单
  const orderId = await testCreateOrder();
  console.log('');

  // 2. 查询订单
  // ... (GetOrder 省略，模式相同)

  // 3. 批量上传商品
  await testUploadItems(orderId);
  console.log('');

  // 4. 流式列出订单
  testListOrders('u-001');

  // 5. 实时聊天
  setTimeout(() => testOrderChat(orderId), 2000);
}

main().catch(console.error);
```

运行客户端：

```bash
npx tsx src/client/index.ts
```

**预期输出**：

```
🚀 开始 gRPC 客户端测试

[gRPC] --> /order.v1.OrderService/CreateOrder
✅ 订单创建成功: 550e8400-e29b-41d4-a716-446655440000, 金额: ¥798
[gRPC] <-- /order.v1.OrderService/CreateOrder OK (45ms)

[gRPC] --> /order.v1.OrderService/UploadOrderItems
  ↑ 发送商品: 显示器
  ↑ 发送商品: 鼠标
  ↑ 发送商品: 鼠标垫
✅ 上传完成: 5 件, 总计 ¥4943
[gRPC] <-- /order.v1.OrderService/UploadOrderItems OK (120ms)

📦 开始接收订单流...
  - 订单 550e8400...: ¥4943
📦 订单流结束

  → customer: 请问什么时候发货？
  ← system: [已收到] customer: 请问什么时候发货？
  → customer: 能改地址吗？
  ← system: [已收到] customer: 能改地址吗？
```

---

## 第九步：健康检查与服务反射

生产环境的 gRPC 服务必须支持健康检查和服务反射。让 Claude Code 添加：

```
请为 gRPC 服务添加：
1. grpc.health.v1 健康检查服务
2. gRPC Server Reflection（服务反射）
使用 @grpc/grpc-js 和 grpc-reflection 包
```

```typescript
import { HealthImplementation } from 'grpc-health-check';
import { grpc } from '@grpc/grpc-js';

// 健康状态
const healthImpl = new HealthImplementation({
  'order.v1.OrderService': 'SERVING',
});
healthImpl.addToServer(server);

// 服务反射（让 grpcurl 能自动发现服务）
import { ReflectionService } from 'grpc-reflection';
const reflection = new ReflectionService({
  protoPath: './proto/order.proto',
  serviceName: 'order.v1.OrderService',
});
reflection.addToServer(server);
```

安装依赖：

```bash
npm install grpc-health-check grpc-reflection
```

验证：

```bash
grpcurl -plaintext localhost:50051 list
# 输出: order.v1.OrderService
#       grpc.health.v1.Health

grpcurl -plaintext localhost:50051 grpc.health.v1.Health/Check
# 输出: { "status": "SERVING" }
```

---

## 第十步：负载测试与性能验证

最后，用 Claude Code 编写一个基准测试来验证 gRPC 性能：

```
请编写 tests/benchmark.ts，使用以下方案对 gRPC 服务进行负载测试：
1. 并发 100 个 Unary CreateOrder 请求，测量 P50/P95/P99 延迟
2. 测试 Server Streaming 吞吐量（每秒推送多少条订单）
3. 测试 Bidi Streaming 的消息往返延迟
4. 输出结果到控制台，包含柱状图 ASCII 展示
```

**基准测试核心代码**：

```typescript
import * as grpc from '@grpc/grpc-js';
import { OrderServiceClient } from '../generated/OrderService_grpc_pb';
import { CreateOrderRequest, OrderItem } from '../generated/order_pb';

const CONCURRENT = 100;
const client = new OrderServiceClient(
  'localhost:50051',
  grpc.credentials.createInsecure()
);
const metadata = new grpc.Metadata();
metadata.set('authorization', 'Bearer valid-secret-token');

function createOrder(): Promise<number> {
  return new Promise((resolve, reject) => {
    const item = new OrderItem();
    item.setProductId('p-bench');
    item.setProductName('基准测试商品');
    item.setQuantity(1);
    item.setUnitPrice(100);

    const request = new CreateOrderRequest();
    request.setUserId('bench-user');
    request.setItemsList([item]);
    request.setShippingAddress('测试地址');

    const start = performance.now();
    client.createOrder(request, metadata, (err) => {
      if (err) return reject(err);
      resolve(performance.now() - start);
    });
  });
}

async function runBenchmark() {
  console.log(`⚡ 启动负载测试: ${CONCURRENT} 并发请求\n`);

  const latencies: number[] = [];
  const promises = Array.from({ length: CONCURRENT }, () =>
    createOrder().then((lat) => latencies.push(lat))
  );

  await Promise.all(promises);

  latencies.sort((a, b) => a - b);
  const p50 = latencies[Math.floor(CONCURRENT * 0.5)];
  const p95 = latencies[Math.floor(CONCURRENT * 0.95)];
  const p99 = latencies[Math.floor(CONCURRENT * 0.99)];
  const avg = latencies.reduce((s, v) => s + v, 0) / latencies.length;
  const rps = (CONCURRENT / (latencies[latencies.length - 1] / 1000)).toFixed(0);

  console.log('📊 测试结果:');
  console.log(`  平均延迟: ${avg.toFixed(1)}ms`);
  console.log(`  P50: ${p50.toFixed(1)}ms`);
  console.log(`  P95: ${p95.toFixed(1)}ms`);
  console.log(`  P99: ${p99.toFixed(1)}ms`);
  console.log(`  吞吐量: ~${rps} RPS`);
  console.log('');
  console.log('延迟分布:');
  [0, 10, 20, 30, 50, 80, 100, 150, 200].forEach((bucket) => {
    const count = latencies.filter((l) => l >= bucket && l < bucket + 10).length;
    const bar = '█'.repeat(count);
    console.log(`  ${String(bucket).padStart(3)}ms | ${bar} (${count})`);
  });
}

runBenchmark().catch(console.error);
```

**典型测试输出**：

```
⚡ 启动负载测试: 100 并发请求

📊 测试结果:
  平均延迟: 28.5ms
  P50: 22.1ms
  P95: 65.3ms
  P99: 89.7ms
  吞吐量: ~3500 RPS

延迟分布:
    0ms | ████████ (8)
   10ms | ████████████████████████████ (28)
   20ms | ████████████████████████████████ (32)
   30ms | ████████████ (12)
   50ms | ████████████ (12)
   80ms | ████ (4)
  100ms | ████ (4)
```

---

## 实战案例：完整订单微服务架构

将上述所有步骤整合，下图展示了完整的 gRPC 订单微服务架构：

```mermaid
graph TB
    subgraph "客户端层"
        WEB[Web 前端<br/>gRPC-Web]
        MOBILE[移动端<br/>gRPC Native]
        CLI[gRPCurl<br/>调试工具]
    end

    subgraph "API Gateway"
        GW[Envoy/Nginx<br/>gRPC-HTTP 转码]
    end

    subgraph "gRPC 服务集群"
        OS[OrderService<br/>订单服务 :50051]
        PS[ProductService<br/>商品服务 :50052]
        US[UserService<br/>用户服务 :50053]
    end

    subgraph "横切关注点"
        INT[拦截器链<br/>日志→认证→限流→错误]
        HC[健康检查<br/>grpc.health.v1]
        RF[服务反射<br/>Server Reflection]
    end

    subgraph "数据层"
        REDIS[(Redis<br/>订单缓存)]
        PG[(PostgreSQL<br/>持久化)]
    end

    WEB -->|HTTP/2| GW
    MOBILE -->|gRPC| GW
    CLI -->|gRPC| OS
    GW -->|gRPC| OS
    GW -->|gRPC| PS
    GW -->|gRPC| US

    OS --> INT
    PS --> INT
    US --> INT

    OS --> HC
    OS --> RF

    OS --> REDIS
    OS --> PG
    PS --> PG
    US --> PG

    style OS fill:#4A90D9,color:#fff
    style INT fill:#F5A623,color:#fff
    style GW fill:#7B68EE,color:#fff
```

**gRPC 通信流程**：

```mermaid
sequenceDiagram
    participant C as Client
    participant I as Interceptor
    participant S as OrderService
    participant DB as PostgreSQL
    participant R as Redis

    Note over C,S: Unary RPC: CreateOrder
    C->>I: 1. 发送 CreateOrderRequest
    I->>I: 2. 日志记录 + Token认证
    I->>S: 3. 转发请求
    S->>DB: 4. 插入订单
    DB-->>S: 5. 返回 order_id
    S->>R: 6. 缓存订单
    S-->>I: 7. 返回 CreateOrderResponse
    I-->>C: 8. 响应 + 耗时日志

    Note over C,S: Server Streaming: ListOrders
    C->>I: 9. 发送 ListOrdersRequest
    I->>S: 10. 转发请求
    loop 每条订单
        S-->>I: 11. call.write(order)
        I-->>C: 12. 流式推送
    end
    S-->>C: 13. call.end()

    Note over C,S: Bidi Streaming: OrderChat
    loop 实时交互
        C->>S: 14. call.write(message)
        S-->>C: 15. call.write(reply)
    end
```

---

## CLAUDE.md 配置建议

在项目根目录创建 `CLAUDE.md`，让 Claude Code 深度理解你的 gRPC 项目：

```markdown
# OrderService - gRPC 微服务

## 技术栈
- Node.js 20 + TypeScript
- gRPC (@grpc/grpc-js)
- Protocol Buffers 3
- Vitest 测试框架

## 项目规范
- Proto 文件放在 proto/ 目录，使用 proto3 语法
- 所有 RPC 方法必须有错误处理和参数校验
- 拦截器放在 src/server/interceptors.ts
- 字段编号不可复用，删除用 reserved
- 生成的代码不手动编辑

## 常用命令
- 代码生成: `./scripts/generate.sh`
- 启动服务: `npx tsx src/server/index.ts`
- 运行客户端: `npx tsx src/client/index.ts`
- 负载测试: `npx tsx tests/benchmark.ts`
- 单元测试: `npx vitest run`
```

---

## 进阶：跨语言通信实战

gRPC 的杀手锏是跨语言。同一个 `.proto` 文件可以生成 Go、Python、Java 客户端。用 Claude Code 快速生成 Go 客户端：

```
请基于 proto/order.proto，用 Go 语言实现一个 gRPC 客户端：
1. 使用 google.golang.org/grpc 和 google.golang.org/protobuf
2. 连接到 localhost:50051
3. 调用 CreateOrder 方法
4. 添加 5s 超时和重试逻辑
```

Claude Code 会自动读取你的 `proto/order.proto` 并生成对应的 Go 客户端代码，完全复用相同的接口契约。

---

## 延伸阅读

1. [gRPC 官方文档 - Interceptors](https://grpc.io/docs/guides/interceptors/) — 拦截器设计模式详解
2. [gRPC Health Checking Protocol](https://grpc.io/docs/guides/health-checking/) — 生产环境健康检查规范
3. [Claude Code + gRPC 开发指南](https://claudecode-lab.com/en/blog/claude-code-grpc-development/) — Claude Code Lab 实战教程
4. [Protocol Buffers 语言指南](https://protobuf.dev/programming-guides/proto3/) — proto3 语法完整参考
5. [gRPC 性能基准测试](https://grpc.io/docs/guides/benchmarking/) — 官方性能测试方法论

---

## 参考来源

1. [gRPC Practical Guide with Claude Code | ClaudeCodeLab](https://claudecode-lab.com/en/blog/claude-code-grpc-development/) — Claude Code 下 gRPC 开发实践指南，含代码生成和服务端实现
2. [gRPC 实战：从 ProtoBuf 定义到微服务通信的完整流程 - CSDN](https://blog.csdn.net/weixin_42549451/article/details/159818241) — 中文 gRPC 全链路实战教程，含电商订单案例
3. [Claude CodeでgRPC APIを設計・実装する方法](https://ses-base.com/articles/claude-code-grpc-api-development-guide/) — 日文 Claude Code gRPC 教程，含负载测试和流式 RPC
4. [Microservice Communication with gRPC 2026 Guide](https://ekolsoft.com/en/b/microservice-communication-with-grpc) — 微服务 gRPC 通信模式全景分析
5. [gRPC Interceptors 官方文档](https://grpc.io/docs/guides/interceptors/) — gRPC 拦截器机制权威参考

---

## 训练营适用性评估

| 维度 | 评估 |
|------|------|
| 适合训练营类型 | 后端开发进阶营、微服务架构训练营、云原生开发营 |
| 预计教学时长 | 4-6 小时（含动手实操） |
| 难度等级 | ⭐⭐⭐⭐ (中高级) |
| 前置知识要求 | Node.js/TypeScript 基础、了解 REST API、基本网络协议概念 |
| 实操比重 | 70% 编码实操 + 30% 原理讲解 |
| 硬件要求 | 8GB+ RAM，能运行 Node.js 和 Docker |

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
