diff --git a/hzh/MS/02-服务治理/服务间通信/README.md b/hzh/MS/02-服务治理/服务间通信/README.md
index 240f35c..411db8e 100644
--- a/hzh/MS/02-服务治理/服务间通信/README.md
+++ b/hzh/MS/02-服务治理/服务间通信/README.md
@@ -63,6 +63,9 @@ sequenceDiagram
gRPC 基于 Protocol Buffers(Proto3),利用 HTTP/2 的多路复用特性,非常适合高性能的内部服务间调用。
+> [!tip] 深入阅读
+> [[06-gRPC]] — gRPC 深度指南,涵盖 Proto 进阶、流式调用、Interceptor、连接管理、生产最佳实践。
+
```go
// proto/order/v1/order.proto
syntax = "proto3";
diff --git a/hzh/MS/06-gRPC/01-协议与架构.md b/hzh/MS/06-gRPC/01-协议与架构.md
new file mode 100644
index 0000000..32ec369
--- /dev/null
+++ b/hzh/MS/06-gRPC/01-协议与架构.md
@@ -0,0 +1,161 @@
+---
+tags: [grpc, http2, protocol-stack, multiplexing]
+create time: 2026-05-07 16:00
+---
+
+# gRPC 协议与架构
+
+## 概述
+
+本文深入讲解 **gRPC 的内部架构**和 **HTTP/2 协议栈层次**。理解这些底层机制后,才能解释「为什么 gRPC 比 HTTP/1.1 REST 更快」、「连接为什么不会泄漏」——不再停留在"听说性能好"的模糊认知层面。
+
+> [!question] 先想一个问题
+>
+> 微服务之间每秒可能产生数万到数十万次 RPC 调用。如果每次调用都新开一条 TCP 连接,操作系统会耗尽哪些资源?
+
+TCP 握手需要三次交互、端口有数量上限(单进程约 65535)、内核要为每个 socket 维护内存——当并发连接数达到万级时,CPU 花在建立和拆除连接上的时间甚至会超过处理业务的时间。这就是 gRPC **默认复用连接**的设计动机。
+
+## gRPC 整体架构
+
+```mermaid
+graph TB
+ subgraph Client["客户端应用"]
+ CApp["业务代码"]
+ CStub["Generated Stub"]
+ CChan["gRPC Channel"]
+ CCall["Client Call"]
+ CApp --> CStub
+ CStub --> CChan
+ CChan --> CCall
+ end
+
+ subgraph Network["网络层"]
+ H2["HTTP/2 Frame Layer"]
+ HP["HPACK Header Compression"]
+ LM["Load Balancing Picker"]
+ NR["Name Resolver"]
+ CCall --> LM
+ LM --> H2
+ H2 --> HP
+ end
+
+ subgraph Server["服务端应用"]
+ SChan["Server Listener"]
+ SH2["HTTP/2 Frame Layer"]
+ SHP["HPACK Header Compression"]
+ SH2 --> SHP
+ Handler["Registered Handler"]
+ SHandler["业务代码"]
+ SHP --> Handler
+ Handler --> SHandler
+ end
+
+ CChan <-->|Binary Frames| SChan
+```
+
+### 关键组件说明
+
+| 组件 | 职责 |
+|------|------|
+| **Generated Stub** | 从 .proto 文件编译生成的桩代码,封装了序列化 / 反序列化和网络通信细节 |
+| **gRPC Channel** | 逻辑连接抽象,内部管理真实 TCP 连接的创建、复用和健康检查 |
+| **HTTP/2 Frame Layer** | 二进制分帧层,将所有数据拆分为轻量级的 Frame 传输 |
+| **HPACK** | 头部压缩算法,避免重复传输相同的 metadata 字段 |
+
+> [!tip] Go 实现细节
+>
+> Go 中每个 gRPC Channel 底层维护一个 **transport 连接池**,由负载均衡器动态分配 SubConn。你可以显式设置 `WithBlock()` 超时来避免启动时的无限等待:
+>
+> ```go
+> conn, err := grpc.DialContext(ctx, target,
+> grpc.WithBlock(),
+> grpc.WithTimeout(5*time.Second),
+> )
+> ```
+
+## HTTP/2 的关键特性
+
+gRPC 不是一种新协议,而是 **Protocol Buffers + HTTP/2 的绑定规范**。HTTP/2 为 gRPC 提供了三个核心能力:
+
+| 特性 | HTTP/1.1 | HTTP/2 | gRPC 收益 |
+|------|----------|--------|-----------|
+| **多路复用** | 一个连接只能处理一个请求(除非 SPDY) | 一个 TCP 连接上并行的多个 Stream | 无需连接池,连接复用率极高 |
+| **头部压缩** | 明文 Head,重复字段多 | HPACK 算法压缩 | 减小传输体积,降低延迟 |
+| **二进制分帧** | 文本协议,解析慢 | 二进制 Frame,解析快 | 双方不需要手写解析逻辑 |
+
+### 多路复用演示
+
+```mermaid
+sequenceDiagram
+ participant C as Client
+ participant H2 as HTTP/2 Connection
+ participant S as Server
+
+ Note over C,S: 一个 TCP 连接,四个并发 Stream
+ C->>H2: Stream 1: GetOrder(id=1)
+ S->>H2: Stream 1: Order{...}
+
+ C->>H2: Stream 2: GetUser(id=5)
+ S->>H2: Stream 2: User{...}
+
+ C->>H2: Stream 3: CreateItem(...)
+ S->>H2: Stream 3: Item{id: "new"}
+```
+
+> [!keypoint] 关键洞察
+>
+> HTTP/1.1 开 10 个并行请求需要 10 条 TCP 连接 → 握手开销大、端口耗尽。**一条 HTTP/2 连接就能承载几百个并发 RPC**,这就是 gRPC 在高频内部调用的性能优势来源。
+
+## 协议栈层次
+
+```mermaid
+graph LR
+ App["Application
Business Logic"] --> Stub["Generated Stub"]
+ Stub --> GRPC["gRPC Framework"]
+ GRPC --> H2["HTTP/2 Protocol"]
+ H2 --> TCP["TCP/IP"]
+
+ style App fill:#e3f2fd
+ style Stub fill:#fff3e0
+ style GRPC fill:#c8e6c9
+ style H2 fill:#fce4ec
+ style TCP fill:#f3e5f5
+```
+
+每一层解决不同的问题:
+
+| 层级 | 解决的问题 | 类比 |
+|------|-----------|------|
+| Application | 你写什么业务逻辑 | 写信的内容 |
+| Generated Stub | 把业务对象映射为二进制编码 | 翻译官(Proto 定义 = 字典) |
+| gRPC Framework | 负责重试、拦截器、流控 | 邮局分拣系统 |
+| HTTP/2 | 多路复用 + 头部压缩 + 二进制帧 | 快递包裹的分装规范 |
+| TCP/IP | 可靠传输 + 路由寻址 | 公路运输网络 |
+
+## 为什么不只是"更快的 HTTP"
+
+许多开发者误以为 gRPC 的优势只是"用了更快的序列化"。实际上真正的分水岭在于:
+
+> [!summary] gRPC vs HTTP API 的本质差异
+>
+> | 维度 | HTTP API(REST) | gRPC |
+> |------|-----------------|------|
+> | **契约先行** | 接口文档滞后于代码 | `.proto` 是单一事实来源 |
+> | **类型安全** | JSON 无类型,运行时才暴露 bug | 编译期捕获字段缺失、类型错误 |
+> | **代码即 SDK** | 客户端需要手动拼装 HTTP 请求 | 自动生成全语言客户端 Stub |
+> | **连接复用** | 需要自行管理连接池 | 框架内置连接复用和负载均衡 |
+> | **流式能力** | WebSocket 需额外建通道 | 原生支持双向流,强类型契约 |
+
+> [!question] 带着问题继续读
+>
+> 既然 gRPC 这么多优势,是不是所有场景都应该用 gRPC?什么情况下 HTTP/JSON 仍然更合适?
+
+答案见 [[02-服务治理/服务间通信]]。对外暴露 API 时,HTTP/JSON 仍然不可替代——因为浏览器的 Native Fetch 无法直接调用 gRPC,第三方接入者也不想安装 Proto 编译器。
+
+## 关联笔记
+
+- [[02-服务治理/服务间通信]] — gRPC 与 REST 的基础对比及选型建议
+- [[02-服务治理/容错模式/README]] — 基于此架构的重试、熔断等治理机制
+- [[02-Proto设计]] — Proto 文件设计的进阶实践
+- [[03-RPC模式]] — 四种 RPC 模式的深度用法
+- [[04-拦截器]] — 切面编程和上下文传播
diff --git a/hzh/MS/06-gRPC/02-Proto设计.md b/hzh/MS/06-gRPC/02-Proto设计.md
new file mode 100644
index 0000000..0f109df
--- /dev/null
+++ b/hzh/MS/06-gRPC/02-Proto设计.md
@@ -0,0 +1,195 @@
+---
+tags: [grpc, protobuf, schema-versioning, proto3]
+create time: 2026-05-07 16:00
+---
+
+# Proto 设计规范
+
+## 概述
+
+本文覆盖 **Proto3 进阶特性**和**版本管理策略**。如果说架构篇是"看懂 gRPC 长什么样",这篇就是教你"如何写出经得起演进的 Proto 文件"——这是工程中最容易踩坑、却最容易被忽视的部分。
+
+> [!question] 思考
+>
+> 你的 Service A 调用 Service B 的 `GetUser`,Proto 里定义了 20 个字段。半年后你想加第 21 个字段,但旧版客户端没有编译更新。会发生什么?
+
+好消息是:Protocol Buffers 的二进制编码规则天然保证了向前兼容。坏消息是:**只有遵守规则的改动才是兼容的**,违反规则的静默破坏会让你排查整整一天。
+
+## Oneof —— 互斥字段
+
+当某个消息体可能有多种不同类型的值,但同一时刻只出现一种时,使用 `oneof`:
+
+```go
+// proto/order/v1/order.proto
+message Payment {
+ string order_id = 1;
+
+ oneof method {
+ AlipayPayment alipay = 2;
+ WechatPayment wechat = 3;
+ CreditCard card = 4;
+ }
+}
+
+message AlipayPayment {
+ string token = 1;
+}
+
+message WechatPayment {
+ string openid = 1;
+}
+
+message CreditCard {
+ string last_four = 1;
+ string exp_date = 2;
+}
+```
+
+> [!note] Proto3 oneof 的限制
+>
+> - 不能加 `repeated` 修饰符
+> - 空 oneof 值为 `NONE(0)`,反序列化时各字段都为默认值
+> - **向后兼容策略**:新增 oneof 变体是安全的;删除变体会破坏旧客户端
+
+## Well-Known Types —— 内置类型库
+
+Proto3 内置了一些常用类型,直接引用即可,不用自己定义:
+
+```proto
+import "google/protobuf/timestamp.proto";
+import "google/protobuf/empty.proto";
+import "google/protobuf/duration.proto";
+import "google/protobuf/wrappers.proto";
+
+message Event {
+ string name = 1;
+ google.protobuf.Timestamp created_at = 2; // 替代手动时间字符串
+ google.protobuf.Empty body = 3; // 空消息占位
+ google.protobuf.StringValue desc = 4; // 可选字符串(区分 unset 和空串)
+}
+```
+
+| 类型 | 用途 |
+|------|------|
+| `Timestamp` | RFC 3339 纳秒级时间戳 |
+| `Duration` | 带单位的时长 |
+| `Empty` | 无参数 / 无返回值的 RPC 占位 |
+| `StringValue / Int32Value / BoolValue` | 包装基本类型,表示"可选"语义 |
+| `Struct / Value / ListValue` | 类 JSON 的动态结构 |
+| `Any` | 泛型消息容器(需搭配 type URL) |
+
+> [!tip] StringValue 的妙用
+>
+> Proto3 默认把未设置的字段归入"默认值",导致无法区分"用户传了空字符串"和"用户没传这个字段"。用 `StringValue` 包装后,Proto3 可以精确表达 Optional 语义。
+
+## Map 字段
+
+```proto
+message Config {
+ map labels = 1;
+ map price_cache = 2;
+}
+```
+
+- **有序遍历**:Proto3 的 map 按 key 排序遍历
+- **兼容性**:删除整个 map 字段可接受,删除其中某个 key-value 对**不安全**(会被当作未知字段忽略)
+
+## 版本管理与向前兼容规则
+
+这是工程中最容易踩坑的部分:
+
+```mermaid
+graph LR
+ Rule["向前兼容三大铁律"] --> R1["新增字段 → 旧客户端忽略"]
+ Rule --> R2["删除字段 → 新客户端忽略"]
+ Rule --> R3["字段编号永不重用"]
+
+ R1 -.-> F1["新字段标记 optional"]
+ R2 -.-> D1["标记 deprecated 而非删除"]
+ R3 -.-> N1["预留号段:1~19 用于 Google, 20~10000 自定"]
+```
+
+### 兼容性决策表
+
+| 操作 | 是否安全 | 说明 |
+|------|---------|------|
+| 新增字段(用更大编号) | ✅ 安全 | 旧版客户端忽略未知编号字段 |
+| 删除字段 | ⚠️ 部分安全 | 建议标记 `deprecated`,保留编号 |
+| 修改字段类型 | ❌ 危险 | 可能导致二进制解析失败 |
+| 重编字段编号 | ❌ 致命 | 读写两边理解错位 |
+| 修改字段名 | ✅ 安全 | 名称不影响二进制编码 |
+| 修改 enum 值 | ⚠️ 部分安全 | 新增安全,删除需用 reserved |
+
+### 常见错误示范
+
+```proto
+// ❌ 错误做法:删掉 field 3 后把 field 4 改成 = 3
+message BadExample {
+ string field1 = 1;
+ string field2 = 2;
+ // string removed_field = 3; ← 直接注释掉了
+ string new_field = 3; // ← 重编了!危险!
+}
+
+// ✅ 正确做法:保留编号并标记 reserved
+message GoodExample {
+ string field1 = 1;
+ string field2 = 2;
+ reserved 3; // 锁定已删除字段的编号
+ string new_field = 4; // 用新编号
+}
+```
+
+### reserved 的完整写法
+
+```proto
+message OldMessage {
+ reserved 2, 15; // 单个编号
+ reserved 9, 10, 14; // 连续范围可用语法 reserved 9 to 14;
+ reserved "name", "email"; // 已废弃的字段名
+}
+```
+
+> [!warning] reserved 不是可选项
+>
+> 当你要删除一个字段或其编号时,必须加上 `reserved` 声明。否则未来有人新增字段时重新使用了这个编号,就会造成静默的数据损坏——旧客户端读到新字段数据当成旧字段解析。
+
+## Proto 组织规范
+
+推荐的目录结构:
+
+```
+proto/
+├── buf.gen.yaml # Buf 代码生成配置
+├── api/
+│ └── v1/
+│ ├── order/
+│ │ ├── order.proto # Service + Message 定义
+│ │ └── error.proto # 统一错误码定义
+│ ├── user/
+│ │ └── user.proto
+│ └── common/
+│ └── page.proto # 分页等通用定义
+└── gen/go/github.com/example/api/ # 自动生成代码
+```
+
+### 命名约定
+
+| 元素 | 命名风格 | 示例 |
+|------|---------|------|
+| package | 小写,点分隔,含版本前缀 | `order.v1` |
+| service | PascalCase + Service 后缀 | `OrderService` |
+| rpc method | PascalCase | `CreateOrder`, `GetUserInfo` |
+| message | PascalCase | `CreateOrderRequest` |
+| enum | PascalCase + Status/Type 后缀 | `OrderStatus`, `RoleType` |
+| field | snake_case | `user_id`, `created_at` |
+
+> [!tip] 推荐的工具链
+>
+> 建议使用 **[Buf](https://buf.build)** 替代 protoc 直调。Buf 提供统一的依赖管理、linting 和 CI 集成,且屏蔽了 protoc 在不同语言间的命令差异。
+
+## 关联笔记
+
+- [[01-协议与架构]] — Proto 文件最终服务于协议栈中的 Generated Stub 层
+- [[07-最佳实践]] — Buf 代码生成流程和生产配置
+- [[02-服务治理/服务间通信]] — 不同序列化方案的性能对比
diff --git a/hzh/MS/06-gRPC/03-RPC模式.md b/hzh/MS/06-gRPC/03-RPC模式.md
new file mode 100644
index 0000000..e8c4ba5
--- /dev/null
+++ b/hzh/MS/06-gRPC/03-RPC模式.md
@@ -0,0 +1,197 @@
+---
+tags: [grpc, rpc-patterns, streaming, bidirectional]
+create time: 2026-05-07 16:00
+---
+
+# RPC 模式详解
+
+## 概述
+
+gRPC 支持 **四种 RPC 调用模式**,每种对应不同的客户端/服务端消息交互时序。理解它们不仅仅是记住名词,更要掌握「什么时候该用哪种」以及「各种模式的坑在哪里」。
+
+> [!question] 先看一个实际场景
+>
+> 你需要实现一个订单列表功能:用户在前端点击按钮 → 后端查询数据库 → 一次返回 100 条订单记录。
+>
+> 你会选择哪种 RPC 模式?如果用 Unary 一行行发回来会怎样?
+
+Unary 模式下这 100 条记录会变成 100 次独立的 HTTP/2 Stream,虽然 HTTP/2 支持多路复用,但这依然浪费了宝贵的 Stream 标识符空间。更好的方式是用 **Server Streaming** 在一个 Stream 内批量发送。
+
+## Unary —— 普通请求/响应
+
+最常见的调用方式,一问一答:
+
+```go
+// client side
+resp, err := client.GetOrder(ctx, &pb.GetOrderRequest{Id: "ORD-001"})
+if err != nil {
+ // handle error
+}
+
+// server side
+func (s *orderServer) GetOrder(ctx context.Context, req *pb.GetOrderRequest) (*pb.Order, error) {
+ return s.store.Get(req.GetId())
+}
+```
+
+适用场景:CRUD 常规操作、单次计算任务、短平快的查询。这是占 gRPC 流量 **90%+** 的模式。
+
+## Server Streaming —— 服务端流式
+
+服务端一次性返回多个结果,适合批量查询或推送:
+
+```mermaid
+sequenceDiagram
+ participant C as Client
+ participant S as Server
+
+ C->>S: ListOrders(request)
+ S-->>C: Order[1]
+ S-->>C: Order[2]
+ S-->>C: Order[3]
+ S-->>C: EOF
+
+ Note right of C: 客户端收到完整列表
+```
+
+```go
+func (s *orderServer) ListOrders(req *pb.ListOrdersRequest, stream pb.OrderService_ListOrdersServer) error {
+ orders := s.store.ListAll()
+ for _, o := range orders {
+ if err := stream.Send(o); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+```
+
+> [!warning] 服务端必须主动关闭流
+>
+> 如果服务端 `Send` 循环中没有遇到错误或主动返回 `nil`,Stream 永远不会结束,客户端将永远阻塞在 `Recv()`。务必确保所有路径都会退出循环或返回错误。
+
+## Client Streaming —— 客户端流式
+
+客户端连续发送多条消息,服务端最后返回聚合结果。适合大数据上传:
+
+```mermaid
+sequenceDiagram
+ participant C as Client
+ participant S as Server
+
+ C->>S: BatchRecord[1]
+ C->>S: BatchRecord[2]
+ C->>S: BatchRecord[3]
+ C->>S: CloseSend()
+
+ Note right of S: 收集所有数据
+ S-->>C: BatchResult{count: 3}
+```
+
+```go
+func (s *orderServer) UploadLogs(stream pb.OrderService_UploadLogsServer) error {
+ var count int32
+ for {
+ log, err := stream.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return err
+ }
+ s.store.Log(log.Message)
+ count++
+ }
+ return stream.SendAndClose(&pb.UploadResult{Count: count})
+}
+```
+
+适用场景:批量写入日志、文件分块上传、批量创建记录。注意要在客户端控制流速,避免一次性塞爆缓冲区。
+
+## Bidi Streaming —— 双向流式
+
+双方各自独立发送流,完全异步。**最适合实时场景**(聊天、行情推送、协同编辑):
+
+```mermaid
+sequenceDiagram
+ participant CS as Client Stream
+ participant SS as Server Stream
+
+ CS->>SS: Msg[1]: "Hello"
+ SS->>CS: Reply[1]: "Hi!"
+ CS->>SS: Msg[2]: "What's up?"
+ SS->>CS: Reply[2]: "Not much"
+ CS->>SS: Msg[3]...
+ SS->>CS: Reply[3]...
+ CS--)SS: Cancel
+ SS--)CS: Cancel
+```
+
+```go
+// 实战示例:WebSocket-like 的实时通知推送
+func (s *server) SubscribeNotifications(
+ req *pb.SubscribeRequest,
+ stream pb.NotificationService_SubscribeServer,
+) error {
+ // 注册订阅
+ notifier.Register(stream.Context().Done(), func() {
+ stream.Send(&pb.Notification{})
+ })
+ <-stream.Context().Done()
+ return stream.Context().Err()
+}
+```
+
+> [!question] 选型思考
+>
+> 假设你要实现一个实时订单状态推送功能(前端监听订单从"待支付"→"已发货"→"已完成"的变化),你会选哪种流式模式?为什么不用 WebSocket?
+
+gRPC 双向流是**强类型 + 全双工**的替代方案,不需要额外建 WebSocket 通道,Proto 定义即契约,代码自动生成即客户端 SDK。对于纯内部服务链路,gRPC 双向流通常优于 WebSocket。
+
+### Bidi Streaming 的生命周期管理
+
+```mermaid
+flowchart TD
+ Start["建立双向连接"] --> Idle["空闲等待"]
+ Idle --> SendMsg["客户端 send"]
+ Idle --> SendReply["服务端 send"]
+ SendMsg --> Idle
+ SendReply --> Idle
+ SendMsg --> ClientCancel{"client cancel?"}
+ SendReply --> ServerCancel{"server cancel?"}
+ ClientCancel -->|yes| Cleanup["释放资源"]
+ ServerCancel -->|yes| Cleanup
+ Cleanup --> End["断开连接"]
+
+ style Start fill:#e3f2fd
+ style End fill:#ffcdd2
+ style Cleanup fill:#fff3e0
+```
+
+> [!keypoint] 黄金法则
+>
+> 双向流的**任何一方都可以随时单方面关闭发送端**(`CloseSend` 或 `context.Done`)。另一方应检测到 EOF 并及时清理资源,而不是无限等待。
+
+## 四种模式速查
+
+| 模式 | 客户端消息数 | 服务端消息数 | 典型场景 |
+|------|------------|------------|---------|
+| Unary | 1 | 1 | CRUD 常规操作 |
+| Server Stream | 1 | N | 列表查询、日志流拉取 |
+| Client Stream | N | 1 | 批量写入、大文件分块上传 |
+| Bidi Stream | N | M | 聊天室、实时协作、行情推送 |
+
+> [!note] 组合使用
+>
+> 同一个 Service 中可以混合使用四种模式。比如 `OrderService` 里:
+> - `CreateOrder` 用 Unary(创建后立即返回结果)
+> - `ListOrders` 用 Server Stream(大量数据分批返回)
+> - `ImportRecords` 用 Client Stream(批量导入)
+> - `NotifyOrderStatus` 用 Bidi Stream(实时状态变更推送)
+
+## 关联笔记
+
+- [[01-协议与架构]] — 流式建立在 HTTP/2 Stream 的多路复用之上
+- [[04-拦截器]] — 流式调用同样可以通过 StreamInterceptor 进行切面处理
+- [[05-错误处理]] — 流式调用的错误处理与普通模式有差异
+- [[06-连接管理]] — 长时间存活的流式连接需要 Keepalive 保活
diff --git a/hzh/MS/06-gRPC/04-拦截器.md b/hzh/MS/06-gRPC/04-拦截器.md
new file mode 100644
index 0000000..5f4c12d
--- /dev/null
+++ b/hzh/MS/06-gRPC/04-拦截器.md
@@ -0,0 +1,176 @@
+---
+tags: [grpc, interceptor, middleware, context-propagation, metadata]
+create time: 2026-05-07 16:00
+---
+
+# Interceptor 与上下文传播
+
+## 概述
+
+Interceptor 是 gRPC 的切面编程能力,相当于 Web 框架中的 Middleware。每个 RPC 调用都会经过 **Unary Interceptor** 或 **Stream Interceptor** 链——无论它是普通的一问一答还是长时间的双向流。
+
+本文涵盖拦截器的编写模式、链式串联技巧,以及最重要的**上下文传播**机制。
+
+## Interceptor 链架构
+
+```mermaid
+graph LR
+ subgraph ClientChain["客户端拦截器链"]
+ CAuth["认证拦截"] --> CMetric["指标采集"] --> CRetry["重试策略"] --> CHand["RPC 调用"]
+ end
+
+ subgraph ServerChain["服务端拦截器链"]
+ SHand["RPC Handler"] --> SMetric["指标采集"] --> SLog["日志记录"] --> SAuth["鉴权拦截"]
+ end
+
+ CHand ==>|HTTP/2| SHand
+
+ style CAuth fill:#fce4ec
+ style SAuth fill:#e3f2fd
+```
+
+### 典型执行顺序
+
+| 位置 | 拦截器 | 职责 |
+|------|--------|------|
+| 客户端 | Metrics | 记录请求耗时、成功/失败计数 |
+| 客户端 | Retry | 根据错误码决定是否自动重试 |
+| 客户端 | Auth | 注入 Token / mTLS 证书 |
+| 服务端 | Auth | 验证 Token 有效性 |
+| 服务端 | Logging | 记录完整的请求/响应元信息 |
+| 服务端 | Metrics | 统计服务端处理时间 |
+| 服务端 | Handler | 实际业务逻辑 |
+
+> [!warning] Go 中拦截器注册的陷阱
+>
+> Go 的 `grpc.WithUnaryInterceptor` 会**覆盖**而非追加。如果注册多次,只有最后一次生效。所以要用一个统一的包装函数串联所有逻辑:
+
+## 链式包装函数
+
+```go
+func chainUnaryInterceptors(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor {
+ n := len(interceptors)
+ return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
+ ch := handler
+ for i := n - 1; i >= 0; i-- {
+ ih := ch
+ ch = func(c context.Context, r any) (any, error) {
+ return interceptors[i](c, r, info, ih)
+ }
+ }
+ return ch(ctx, req)
+ }
+}
+```
+
+调用方只需传入所有拦截器即可:
+
+```go
+allInterceptors := []grpc.UnaryServerInterceptor{
+ TraceInterceptor(),
+ AuthInterceptor(),
+ LoggingInterceptor(),
+ MetricsInterceptor(),
+}
+
+server := grpc.NewServer(
+ grpc.UnaryInterceptor(chainUnaryInterceptors(allInterceptors...)),
+ grpc.StreamInterceptor(chainStreamInterceptors(...)),
+)
+```
+
+> [!tip] 其他语言的差异
+>
+> - **Go**:需要通过上述手动链式包装,因为 `NewServer` 只接受一个拦截器
+> - **Java**:`Server.intercept()` 支持注册多个拦截器,按注册顺序依次执行
+> - **Node.js**:通过插件体系 `Server.addServiceDefinition` 间接实现
+
+## 实战:统一鉴权拦截器
+
+```go
+// auth interceptor
+func AuthInterceptor() grpc.UnaryServerInterceptor {
+ return func(ctx context.Context, req any,
+ info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
+
+ // 白名单路径跳过鉴权
+ skipPaths := []string{"/health.Health/Check", "/grpc.reflection.v1.ServerReflection/ServerReflectionInfo"}
+ for _, p := range skipPaths {
+ if info.FullMethod == p {
+ return handler(ctx, req)
+ }
+ }
+
+ // 从 metadata 中提取 Token
+ md, ok := metadata.FromIncomingContext(ctx)
+ if !ok {
+ return nil, status.Error(codes.Unauthenticated, "missing metadata")
+ }
+
+ tokens := md.Get("authorization")
+ if len(tokens) == 0 || !validateToken(tokens[0]) {
+ return nil, status.Error(codes.Unauthenticated, "invalid token")
+ }
+
+ // 将用户信息注入 ctx(传递给下游 handler)
+ userCtx := context.WithValue(ctx, "userID", extractUserID(tokens[0]))
+ return handler(userCtx, req)
+ }
+}
+```
+
+## 上下文传播 (Context Propagation)
+
+gRPC 天然支持通过 `metadata` 传递自定义元数据——这是实现分布式追踪、链路溯源的核心机制。
+
+### 手动传播
+
+```go
+// 服务端注入 trace ID
+ctx = metadata.AppendToOutgoingContext(ctx,
+ "x-trace-id", traceID,
+ "x-user-id", userID,
+)
+
+// 客户端接收
+md, ok := metadata.FromOutgoingContext(ctx)
+traceID := md.Get("x-trace-id")
+```
+
+### OpenTelemetry 自动传播
+
+手动维护 metadata 繁琐且易遗漏。使用 `otelgrpc` 中间件可以自动注入 W3C Trace Context:
+
+```go
+import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
+
+server := grpc.NewServer(
+ grpc.StatsHandler(otelgrpc.NewServerStatsHandler()),
+ // otelgrpc 自动在 metadata 中注入 traceparent / tracestate
+)
+
+conn, _ := grpc.Dial(target,
+ grpc.WithStatsHandler(otelgrpc.NewClientStatsHandler()),
+)
+```
+
+> [!keypoint] 为什么需要上下文传播
+>
+> 当一个请求穿越 5 个微服务时,如果没有上下文传播,你在日志系统中看到的是 5 条孤立的记录。有了 W3C Trace Context,每层服务自动将 `traceparent` 透传给下一跳——最终汇聚成一条完整的调用链路,这就是 [[02-服务治理/分布式追踪]] 的核心能力。
+
+## 拦截器最佳实践
+
+> [!summary] 五条黄金法则
+>
+> 1. **不要吞掉错误**——拦截器应该记录错误再转发给下一个,而不是独自决定忽略
+> 2. **不要在拦截器里做重型计算**——它处于热路径,每条 RPC 都要经过
+> 3. **设置合理的超时**——拦截器里的 `context.WithTimeout` 不应短于原始请求的 deadline
+> 4. **白名单排除健康检查**——`/health.Health/Check` 和 reflection 服务不需要鉴权和日志
+> 5. **用 `info.FullMethod` 做条件判断**——格式为 `/package.Service/Method`,如 `/order.v1.OrderService/CreateOrder`
+
+## 关联笔记
+
+- [[01-协议与架构]] — 拦截器位于 gRPC Framework 层,在 HTTP/2 之前处理请求
+- [[05-错误处理]] — 拦截器经常需要根据错误码决定重试或降级策略
+- [[02-服务治理/分布式追踪]] — Context 传播是实现分布式追踪的前提
+- [[02-服务治理/安全机制]] — 鉴权拦截器是安全机制在代码层的落地形式
diff --git a/hzh/MS/06-gRPC/05-错误处理.md b/hzh/MS/06-gRPC/05-错误处理.md
new file mode 100644
index 0000000..25df44e
--- /dev/null
+++ b/hzh/MS/06-gRPC/05-错误处理.md
@@ -0,0 +1,182 @@
+---
+tags: [grpc, error-handling, status-codes, graceful-degradation]
+create time: 2026-05-07 16:00
+---
+
+# 错误处理与状态码规范
+
+## 概述
+
+良好的错误处理决定了微服务系统的**可观测性**和**恢复速度**。gRPC 设计了 16 个标准状态码,每个都有明确的语义边界。正确使用它们,可以让调用方精准判断该重试、该降级、还是直接报错。
+
+> [!question] 先看一个反面教材
+>
+> 以下两个服务的错误处理方式,哪个更好?
+>
+> **服务 A**:所有异常一律返回 `InternalError: something went wrong`
+>
+> **服务 B**:参数校验失败返回 `InvalidArgument`,资源不存在返回 `NotFound`,数据库超时返回 `Unavailable`
+
+答案是显而易见的。但现实工程中,服务 A 的比例远高于服务 B——原因是很多开发者不了解状态码的准确含义,或者嫌麻烦直接 return nil, fmt.Errorf(...)。
+
+## gRPC 状态码一览
+
+gRPC 定义了 **16 个标准状态码**,与 HTTP 状态码不完全映射,有自己的语义体系:
+
+```mermaid
+graph TB
+ OK["OK - 成功"]
+
+ subgraph "客户端错误 4xx 对应"
+ CANCELLED["CANCELLED - 客户端取消"]
+ UNKNOWN["UNKNOWN - 未知错误"]
+ INVALID_ARG["INVALID_ARGUMENT - 参数无效"]
+ DEADLINE_EX["DEADLINE_EXCEEDED - 超时"]
+ NOT_FOUND["NOT_FOUND - 资源不存在"]
+ ALREADY_EXIST["ALREADY_EXISTS - 重复创建"]
+ PERMISSION_DENIED["PERMISSION_DENIED - 权限不足"]
+ UNAUTH["UNAUTHENTICATED - 未认证"]
+ end
+
+ subgraph "服务端错误 5xx 对应"
+ RESOURCE_EXP["RESOURCE_EXHAUSTED - 资源耗尽"]
+ UNAVAIL["UNAVAILABLE - 服务不可用"]
+ DATA_LOSS["DATA_LOSS - 数据损坏"]
+ end
+
+ subgraph "未实现"
+ UNIMP["UNIMPLEMENTED - 方法未实现"]
+ INTERNAL_ERR["INTERNAL - 内部错误"]
+ SYSTEM_LOST["SYSTEM_ERROR - 系统级错误"]
+ end
+
+ OK
+ CANCELLED ~~~ UNAUTH
+ INVALID_ARG ~~~ PERMISSION_DENIED
+ DEADLINE_EX ~~~ UNAVAIL
+ INTERNAL_ERR ~~ RESOURCE_EXP
+```
+
+### 状态码速查表
+
+| 业务含义 | 推荐状态码 | HTTP 等价 |
+|---------|-----------|----------|
+| 参数校验失败 | `InvalidArgument` | 400 |
+| 资源不存在 | `NotFound` | 404 |
+| 重复创建 | `AlreadyExists` | 409 |
+| 权限不足 | `PermissionDenied` | 403 |
+| 未认证 | `Unauthenticated` | 401 |
+| 超时 | `DeadlineExceeded` | 504 |
+| 服务挂了/连接断开 | `Unavailable` | 503 |
+| 限流 | `ResourceExhausted` | 429 |
+| 方法未定义 | `Unimplemented` | 501 |
+| 代码 bug | `Internal` | 500 |
+
+## 正确的错误处理方式
+
+```go
+// ❌ 错误示范:用普通 error 包装,丢失 gRPC 语义
+return nil, fmt.Errorf("failed to get order: %w", db.ErrNotFound)
+
+// ✅ 正确示范:使用 status.Error 保持 gRPC 协议一致性
+if errors.Is(err, db.ErrNotFound) {
+ return nil, status.Errorf(codes.NotFound, "order %s not found", id)
+}
+
+// ✅ 更精细:带上详情(status details)——客户端可以程序化解析
+detail := &errdetails.BadRequest{
+ FieldViolations: []*errdetails.BadRequest_FieldViolation{{
+ Field: "user_id",
+ Description: "must be a valid UUID",
+ }},
+}
+return nil, status.New(codes.InvalidArgument, "validation failed").WithDetails(detail).Err()
+```
+
+### 为什么要用 status.Error
+
+| 维度 | fmt.Errorf | status.Error |
+|------|-----------|-------------|
+| 客户端获取状态码 | 需要 parse 字符串 | `status.Code(err)` 直接获取 |
+| 跨语言一致 | 各语言行为不一致 | gRPC 协议标准化 |
+| 携带结构化错误 | 不支持 | 通过 `WithDetails` 携带 proto detail |
+| 配合拦截器重试 | 无法识别 | 拦截器根据 codes.* 决定重试 |
+
+## 状态码详情 (Status Details)
+
+gRPC 允许在错误响应中附加结构化详情,这对自动化运维尤其重要:
+
+```go
+// 超时场景中附带重试信息
+retryInfo := &errdetails.RetryInfo{
+ RetryDelay: durationpb.New(2 * time.Second),
+}
+
+// 权限场景中附带帮助链接
+help := &errdetails.Help{
+ Links: []*errdetails.Help_Link{{
+ Url: "https://internal.wiki/perm-error",
+ Description: "如何申请访问权限",
+ }},
+}
+
+status.New(codes.PermissionDenied, "no access").
+ WithDetails(retryInfo, help).
+ Err()
+```
+
+客户端可以程序化提取这些信息:
+
+```go
+st := status.Convert(err)
+for _, d := range st.Details() {
+ switch detail := d.(type) {
+ case *errdetails.RetryInfo:
+ time.Sleep(detail.RetryDelay.AsDuration())
+ // 执行重试
+ }
+}
+```
+
+## 客户端优雅降级
+
+```go
+resp, err := client.CreateOrder(ctx, req)
+switch status.Code(err) {
+case codes.NotFound:
+ // 降级:尝试加载缓存数据
+ return fallbackFromCache(ctx, id)
+case codes.DeadlineExceeded, codes.Unavailable:
+ // 降级:返回友好提示或部分数据
+ return partialOrder(id), nil
+default:
+ return nil, err
+}
+```
+
+> [!keypoint] 黄金法则
+>
+> - 永远用 `codes.*` 而不用 `fmt.Errorf` 做 gRPC 返回值
+> - 不要吞掉所有错误一律返回 `Internal` —— 这会让排查问题无从下手
+> - 客户端根据状态码决定重试还是降级,而不是所有错都 retry
+
+## 何时返回 Internal
+
+`Internal` 是最不应该被使用的状态码。只在以下情况使用:
+
+| 场景 | 示例 |
+|------|------|
+| 代码逻辑 bug | panic recover、nil pointer dereference |
+| 不可恢复的内部状态 | 数据库连接池耗尽、配置文件解析失败 |
+| 子服务报错但不确定原因 | 下游返回的 code 不在预期范围内 |
+
+> [!warning] Internal 不等于"随便的错"
+>
+> 如果你的日志里有大量的 `Internal` 错误,这通常意味着上游的错误分类不够细,掩盖了真正的根因。每次遇到不知道该怎么映射的错误时,优先考虑是否能归入现有的某个 code。
+
+## 关联笔记
+
+- [[04-拦截器]] — 拦截器根据错误码决定是否触发自动重试
+- [[07-最佳实践]] — 重试策略中与错误状态的配合配置
+- [[02-服务治理/容错模式]] — 熔断器在收到特定错误码后触发熔断
+- [[02-服务治理/分布式追踪]] — 错误链路在追踪系统中的标注方式
diff --git a/hzh/MS/06-gRPC/06-连接管理.md b/hzh/MS/06-gRPC/06-连接管理.md
new file mode 100644
index 0000000..ca94a1f
--- /dev/null
+++ b/hzh/MS/06-gRPC/06-连接管理.md
@@ -0,0 +1,155 @@
+---
+tags: [grpc, connection-management, keepalive, load-balancing, dns, service-discovery]
+create time: 2026-05-07 16:00
+---
+
+# 连接管理与负载均衡
+
+## 概述
+
+在生产环境中,gRPC 连接的管理质量直接影响**稳定性**和**可用性**。本文将 Cover 连接保活策略、负载均衡 Picker、服务发现 Name Resolver 三大主题——这些都是日常开发容易忽略但出问题时就是一大片故障的领域。
+
+> [!question] 先看一个问题
+>
+> 两个微服务之间建立了 gRPC 连接,之后 30 分钟没有任何请求。此时第一个新的 RPC 请求发起时会发生什么?
+
+如果中间经过了 Nginx、云厂商 LB 或 AWS ALB,很可能连接已经被idle timeout 切断。但两端都还认为连接是活的,于是第一次 `Send()` 报 `use of closed network connection`。这就是**没有配置 Keepalive 的典型故障**。
+
+## Keepalive 策略
+
+gRPC 连接默认不发送 keepalive ping,长时间空闲的连接会被中间代理(如 Nginx、云厂商 LB)切断:
+
+```go
+server := grpc.NewServer(
+ grpc.KeepaliveParams(keepalive.ServerParameters{
+ Time: 10 * time.Second, // ping 间隔
+ Timeout: 5 * time.Second, // 超时检测
+ MaxConnectionAge: 5 * time.Minute, // 最大生命周期(平滑退役)
+ }),
+ grpc.KeepaliveEnvelope(keepalive.EnforcementPolicy{
+ MinTime: 5 * time.Second, // 客户端最小 ping 间隔
+ PermitWithoutStream: true, // 允许空闲连接保活
+ }),
+)
+```
+
+### Keepalive 参数解读
+
+| 参数 | 方向 | 含义 |
+|------|------|------|
+| `Time` | 服务端 | 多久没收到 ping 就发一个 ping |
+| `Timeout` | 服务端 | 发了 ping 后等多久没回复就算对方挂了 |
+| `MaxConnectionAge` | 服务端 | 连接最多存活多久,强制关闭让客户端重建 |
+| `MinTime` | 服务端 | 限制客户端 ping 频率,防 DoS |
+| `PermitWithoutStream` | 服务端 | 即使没有活跃 Stream 也允许发送 ping |
+
+### 连接生命周期
+
+```mermaid
+timeline
+ title 连接生命周期管理
+ 0 min : 建立连接
+ 5 min : 达到 MaxConnectionAge
服务端主动关闭
(旧连接不再收新请求)
+ 5min+ : 客户端创建新连接
完成平滑迁移
+```
+
+> [!keypoint] MaxConnectionAge 的作用
+>
+> 它不是为了保活,而是为了**平滑退役**。当后端实例缩容或升级时,通过 MaxConnectionAge 让旧连接自然到期失效,客户端自动切换到新连接,避免突然断连导致的请求失败。
+
+## 负载均衡 Picker
+
+gRPC 内建了几种负载均衡策略,客户端侧自动分发请求到不同后端实例:
+
+```mermaid
+graph LR
+ LR_WRR["Weighted Round Robin
权重轮询"] --> Picking["Picker.pick()
→ 选择一个 SubConn"]
+ LR_HRR["Hash-based
粘性会话"] --> Picking
+ LR_RRS["Random Selection
随机挑选"] --> Picking
+ LR_PR["Pick First
单一连接"] --> Picking
+
+ Picking --> SubConn["SubConn
单个后端实例"]
+```
+
+### 策略对比
+
+| 策略 | 行为 | 适用场景 |
+|------|------|---------|
+| **pick_first**(默认) | 每次只用一个 SubConn,直到 health check 失败才切换 | 只有一个后端、简单场景 |
+| **round_robin** | 轮询分发到新连接 | 无状态服务、均匀负载 |
+| **weighted_round_robin** | 按权重轮询(Go 1.25+) | 后端规格不一致时使用 |
+| **hash-based** | 按 key hash 选定后端 | 需要会话粘性的场景 |
+
+```go
+// 客户端指定负载均衡策略
+conn, err := grpc.Dial(
+ "dns:///orderservice:9000",
+ grpc.WithDefaultServiceConfig(`{"loadBalancingConfig":[{"round_robin":{}}]}`),
+)
+```
+
+> [!warning] pick_first 的隐患
+>
+> 默认的 pick_first 策略只使用一个连接。如果这个连接对应的后端实例挂了,gRPC 要等到 health check 失败才会切换。在高可用要求高的场景下,务必切换到 round_robin。
+
+## Name Resolver —— 服务发现桥梁
+
+Name Resolver 是 gRPC 将逻辑服务名解析为物理 IP:Port 的桥梁:
+
+```mermaid
+graph LR
+ Target["目标地址:
dns:///svc:9000"] --> NR["Name Resolver"]
+ NR --> SD["服务发现后端
consul / kubernetes / etcd"]
+ SD --> AddrList["[]Resolver.Addresses"]
+ AddrList --> CC["ClientConn
新建/复用 SubConn"]
+
+ style NR fill:#fff3e0
+ style SD fill:#e3f2fd
+```
+
+### Name Resolver 方案
+
+| 方案 | URI 前缀 | 适用场景 |
+|------|---------|---------|
+| DNS | `dns:///host:port` | 最简单,依赖 DNS 记录 |
+| Kubernetes | `k8s://` | K8s Service Discovery |
+| Eureka | `eureka:///service-name` | Spring Cloud 生态 |
+| File | `file:///path/to/config` | 静态配置文件开发调试 |
+
+### Kubernetes 环境下的零配置方案
+
+```go
+// 结合 CoreDNS SRV 记录,无需硬编码任何地址
+conn, err := grpc.Dial(
+ "dns:///my-service.default.svc.cluster.local:9000",
+ grpc.WithDefaultServiceConfig(`{"loadBalancingConfig":[{"round_robin":{}}]}`),
+)
+```
+
+Kubernetes 的 DNS 控制器会自动将 Service 的 Endpoints 更新到 DNS 记录,gRPC 客户端只需要监听 DNS 变化即可。
+
+> [!tip] 云原生首选
+>
+> 在 Kubernetes 环境中,结合 ExternalName 或 CoreDNS SRV 记录可以实现零配置的服务发现。配合 `round_robin` 负载均衡策略,后端扩容缩容时客户端自动感知,无需手动干预。
+
+## 连接管理与可观测性联动
+
+| 维度 | 与可观测性的配合 |
+|------|----------------|
+| **连接断开** | 通过 Metrics 监控连接创建/销毁频率,异常突增说明后端频繁重启 |
+| **负载均衡不均** | 通过 Histogram 看每个后端实例的 QPS 分布,失衡则调整 picker |
+| **Keepalive 超时** | 通过日志告警 Detect connection reset,定位中间代理 idle timeout 配置 |
+
+> [!keypoint] 监控建议
+>
+> 生产环境的 gRPC 客户端和服务端都应该暴露以下指标:
+> - `grpc_connection_state_changes_total`:连接状态变更次数
+> - `grpc_call_duration_seconds`:单次 RPC 耗时
+> - `grpc_server_handled_total`:按 status code 分组的服务端调用计数
+
+## 关联笔记
+
+- [[01-协议与架构]] — Name Resolver 是架构图中连接管理层的第一环
+- [[07-最佳实践]] — Keepalive、负载均衡在生产环境的配合配置
+- [[02-服务治理/服务发现/README]] — 更深度的服务发现机制对比(Nacos / Consul / K8s)
+- [[02-服务治理/容错模式/README]] — 负载均衡 + 熔断的组合效果
diff --git a/hzh/MS/06-gRPC/07-最佳实践.md b/hzh/MS/06-gRPC/07-最佳实践.md
new file mode 100644
index 0000000..f4726f1
--- /dev/null
+++ b/hzh/MS/06-gRPC/07-最佳实践.md
@@ -0,0 +1,220 @@
+---
+tags: [grpc, production-readiness, retries, tls, performance-tuning]
+create time: 2026-05-07 16:00
+---
+
+# 生产环境最佳实践
+
+## 概述
+
+本章汇总 gRPC 在生产部署时需要关注的各项配置与策略:重试机制、TLS/mTLS、代码生成工具链、性能调优。这些知识点往往是"知道能解决问题,不知道就是故障"的存在。
+
+> [!question] 最后的思考题
+>
+> 如果你的 gRPC 服务 QPS 达到 10 万级别,但仍然发现延迟偏高,你觉得最可能的瓶颈在哪里?是序列化、网络、还是连接管理?带着这个问题去实际压测一遍,答案会比看十篇文章深刻。
+
+## 重试策略
+
+生产环境不建议无条件重试,但要针对可恢复错误配置自动重试:
+
+```go
+// 客户端配置自动重试
+retryPolicy := `{
+ "retryPolicy": {
+ "maxAttempts": 3,
+ "initialBackoff": "0.1s",
+ "maxBackoff": "1s",
+ "backoffMultiplier": 2,
+ "retryableStatusCodes": ["UNAVAILABLE", "DEADLINE_EXCEEDED"]
+ }
+}`
+
+conn, err := grpc.Dial(target,
+ grpc.WithDefaultServiceConfig(retryPolicy),
+)
+```
+
+```mermaid
+flowchart LR
+ attempt1["第1次调用
UNAVAILABLE"] -->|指数退避 100ms| attempt2["第2次调用
UNAVAILABLE"] -->|指数退避 200ms| attempt3["第3次调用
SUCCESS"]
+
+ attempt1 -.->|INTERNAL → 不重试| final["终止"]
+
+ style attempt1 fill:#fff3e0
+ style attempt2 fill:#ffe0b2
+ style attempt3 fill:#c8e6c9
+ style final fill:#ffcdd2
+```
+
+### 重试的安全边界
+
+> [!warning] 重试的三条红线
+>
+> 1. **仅幂等操作**(GET、DELETE)可以安全重试
+> 2. **POST/create 操作**重试可能产生重复数据,必须在业务层加幂等键(如 `idempotency-key` header)
+> 3. 重试会增加**读放大**,特别是涉及 DB 的场景
+
+| 操作类型 | 可重试? | 注意事项 |
+|---------|---------|---------|
+| Query / Get | ✅ 安全 | 本身幂等,可放心重试 |
+| Update / Patch | ⚠️ 有条件 | 需要在 DB 层加乐观锁或唯一索引 |
+| Create / Insert | ❌ 谨慎 | 必须有幂等键机制,否则可能重复插入 |
+| Delete | ✅ 安全 | 幂等操作 |
+
+## TLS / mTLS 配置
+
+```go
+tlsConfig := &tls.Config{
+ Certificates: []tls.Certificate{cert},
+ ClientAuth: tls.RequireAndVerifyClientCert,
+ ClientCertPool: certPool,
+ MinVersion: tls.VersionTLS12,
+}
+```
+
+对于大规模微服务,推荐使用 **mTLS**(双向证书认证)作为服务间信任的基础:
+
+```go
+// 服务端:需要客户端证书
+conn, _ := grpc.Dial(target,
+ grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
+)
+
+// 客户端也需要携带自己的证书
+creds := credentials.NewTLS(tlsConfig)
+```
+
+> [!tip] Istio 集成
+>
+> 在服务网格中,mTLS 由 Sidecar Proxy(Envoy)自动处理,业务代码无需关心 TLS 细节。gRPC 连接经过 Sidecar 时透明加密,业务层仍然使用明文连接(localhost)。
+
+## 代码生成工具链
+
+```mermaid
+graph LR
+ Proto["*.proto files"] --> Buf["buf generate"]
+ Buf --> Go["go_proto_plugin
生成 Go 代码"]
+ Buf --> JS["js_proto_plugin
生成 TS/JS 代码"]
+ Buf --> Validate["validate.proto
生成校验代码"]
+ Buf --> GRPC["go_grpc_plugin
生成 gRPC 桩"]
+
+ style Proto fill:#e3f2fd
+ style Buf fill:#fff3e0
+ style Go fill:#c8e6c9
+ style JS fill:#fce4ec
+ style GRPC fill:#e8f5e9
+```
+
+### buf.gen.yaml 示例
+
+```yaml
+version: v2
+plugins:
+ - remote: buf.build/protocolbuffers/go
+ out: gen/go
+ opt: paths=source_relative
+ - remote: buf.build/grpc/go
+ out: gen/go
+ opt: paths=source_relative
+ - remote: buf.build/bufbuild/validate-go
+ out: gen/go
+ opt: paths=source_relative
+```
+
+> [!tip] buf.lock —— 锁定依赖版本
+>
+> 像 go.mod 锁定 Go 模块版本一样,`buf.lock` 锁定 proto 依赖的确切 commit,避免上游变更导致构建不一致。CI 中应加入 `buf dep update --lock` 的检查步骤。
+
+### CI/CD 集成
+
+```yaml
+# GitHub Actions 示例
+- name: Generate gRPC code
+ uses: bufbuild/buf-action@v1
+ with:
+ command: generate
+ input: proto/
+
+- name: Check generated code is up to date
+ run: buf mod update && git diff --exit-code
+```
+
+## 配置管理
+
+完整的 Dial 配置参考:
+
+```go
+conn, err := grpc.DialContext(ctx, target,
+ // 基础选项
+ grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
+ grpc.WithInitialWindowSize(1<<20), // 窗口大小 1MB
+ grpc.WithInitialConnWindowSize(1<<20), // 连接窗口 1MB
+ grpc.MaxCallRecvMsgSize(10 << 20), // 最大收包 10MB
+ grpc.MaxCallSendMsgSize(10 << 20), // 最大发包 10MB
+ grpc.WithConnectParams(grpc.ConnectParams{ // 连接参数
+ MinConnectTimeout: 5 * time.Second,
+ BackoffConfig: backoff.Config{
+ BaseDelay: 100 * time.Millisecond,
+ Multiplier: 1.6,
+ MaxDelay: 3 * time.Second,
+ },
+ }),
+)
+```
+
+### Dial 选项速查
+
+| 配置项 | 默认值 | 建议值 | 作用域 |
+|--------|--------|--------|--------|
+| MaxCallRecvMsgSize | 4MB | 10MB | 仅客户端 |
+| MaxCallSendMsgSize | 4MB | 10MB | 仅服务端 |
+| InitialWindowSize | 64KB | 1MB(高吞吐) | 连接级 |
+| BackoffBaseDelay | 100ms | 100ms | 连接断开重连 |
+| BackoffMaxDelay | 10s | 3s | 连接断开重连 |
+
+## 性能调优要点
+
+| 优化项 | 建议值 | 影响 |
+|--------|--------|------|
+| 单个消息大小上限 | 4MB~10MB | 防止 OOM,超出则分块传 |
+| Initial Window Size | 1MB | 吞吐瓶颈时常需调大 |
+| Keepalive Time | 10~30s | 太短增加开销,太长被代理杀 |
+| Compressor | gzip(按需启用) | CPU vs 带宽权衡 |
+| Connection Pooling | 让 gRPC 自动管理 | 不要手动开连接 |
+
+### gzip 压缩的取舍
+
+```go
+// 客户端对特定大 Payload 调用启用压缩
+resp, err := client.GetBigData(
+ ctx,
+ req,
+ grpc.UseCompressor("gzip"),
+)
+```
+
+> [!summary] 何时启用 gzip
+>
+> | 场景 | 是否建议 gzip | 理由 |
+> |------|-------------|------|
+> | 小消息(< 1KB) | 否 | 压缩开销大于节省的带宽 |
+> | 大消息(> 10KB)且 CPU 充裕 | 是 | 带宽通常是更大瓶颈 |
+> | 高 QPS 短命请求 | 否 | CPU 反而成为瓶颈 |
+> | 跨数据中心调用 | 是 | 网络 RTT 高,减少数据传输量有意义 |
+
+## 连接管理与 Keepalive 回顾
+
+连接配置的最佳实践详见 [[06-连接管理]]。总结来说,生产环境至少要做到三件事:
+
+1. **开启 Keepalive**,ping 间隔 10~30s,防止被代理切断
+2. **配置 MaxConnectionAge**,让旧连接平滑退役,新版本自动接盘
+3. **设置合理 backoff**,连接断开时指数退避重连,不要打满服务器
+
+## 关联笔记
+
+- [[01-协议与架构]] — 理解协议栈有助于调优每一个参数的意义
+- [[04-拦截器]] — 重试策略可以通过 interceptor 实现更复杂的逻辑
+- [[05-错误处理]] — 重试策略根据错误状态码来决定是否重试
+- [[06-连接管理]] — Keepalive、负载均衡、Name Resolver 的详细配置
+- [[02-服务治理/安全机制]] — mTLS 与服务间身份认证的更深内容
+- [[02-服务治理/容错模式]] — 熔断器、限流器与重试策略的组合配置
diff --git a/hzh/MS/06-gRPC/README.md b/hzh/MS/06-gRPC/README.md
new file mode 100644
index 0000000..4df98a6
--- /dev/null
+++ b/hzh/MS/06-gRPC/README.md
@@ -0,0 +1,133 @@
+---
+tags: [grpc]
+create time: 2026-05-07 16:30
+---
+
+# gRPC 知识索引
+
+## 概述
+
+本目录系统整理 **gRPC** 的核心知识点,从底层协议到生产实践,由浅入深覆盖 gRPC 的每一个关键领域。与 [[02-服务治理/服务间通信]] 中的入门对比不同,这里聚焦于「用了 gRPC 之后」—— 如何设计 Proto、如何编写拦截器、如何处理流式调用、连接怎么管、出错怎么查。
+
+```mermaid
+graph LR
+ A["01 协议与架构"] --> B["02 Proto设计"]
+ B --> C["03 RPC模式"]
+ C --> D["04 拦截器"]
+ D --> E["05 错误处理"]
+ E --> F["06 连接管理"]
+ F --> G["07 最佳实践"]
+
+ style A fill:#e3f2fd
+ style B fill:#fff3e0
+ style C fill:#c8e6c9
+ style D fill:#fce4ec
+ style E fill:#e8f5e9
+ style F fill:#f3e5f5
+ style G fill:#ffe0b2
+```
+
+## 知识体系
+
+### 1. [[01-协议与架构]] — gRPC 的内部架构
+
+从协议栈层次到 HTTP/2 多路复用原理,回答「为什么 gRPC 更快」。
+
+| 核心内容 | 说明 |
+|---------|------|
+| 协议栈分层 | Application → Generated Stub → gRPC Framework → HTTP/2 → TCP/IP |
+| HTTP/2 三特性 | 多路复用、HPACK 头部压缩、二进制分帧 |
+| 组件职责 | Channel、Stub、Transport、Picker 的作用域划分 |
+
+> [!tip] 理论基础篇
+> 建议先读此篇,建立正确认知后再深入配置细节。
+
+### 2. [[02-Proto设计]] — Proto 文件设计规范
+
+如何写出经得起演进的 `.proto` 文件——这是最容易踩坑也最容易被忽视的部分。
+
+| 核心内容 | 说明 |
+|---------|------|
+| Oneof / Map / Well-Known Types | Proto3 进阶类型用法 |
+| 版本管理 | 向前兼容三大铁律与决策速查表 |
+| reserved 机制 | 锁定已删除字段编号的安全声明 |
+| 命名约定 | package / service / message / field 统一规范 |
+
+### 3. [[03-RPC模式]] — 四种 RPC 调用模式详解
+
+| 模式 | 客户端消息数 | 服务端消息数 | 典型场景 |
+|------|------------|------------|---------|
+| Unary(普通) | 1 | 1 | CRUD 常规操作 |
+| Server Streaming | 1 | N | 列表查询、日志流拉取 |
+| Client Streaming | N | 1 | 批量写入、大文件分块上传 |
+| Bidi Streaming | N | M | 聊天室、实时协作、行情推送 |
+
+> [!question] 选型思考
+>
+> 实时订单状态推送:用双向流还是 WebSocket?gRPC 强类型契约 vs 浏览器原生支持的权衡在哪里?
+
+### 4. [[04-拦截器]] — Interceptor 与上下文传播
+
+gRPC 的切面编程能力:鉴权、日志、指标采集、重试决策都通过 interceptor 实现。
+
+| 核心内容 | 说明 |
+|---------|------|
+| 拦截器链架构 | 客户端链 vs 服务端链的执行顺序 |
+| Go 链式封装 | `chainUnaryInterceptors` 解决多拦截器叠加问题 |
+| Context Propagation | metadata 传递 trace ID、user ID 等上下文信息 |
+| OpenTelemetry 集成 | W3C Trace Context 自动注入 |
+
+### 5. [[05-错误处理]] — 状态码规范与客户端降级
+
+| 核心内容 | 说明 |
+|---------|------|
+| 16 个标准状态码 | 按 4xx / 5xx / 未实现分类的决策图 |
+| status.Error vs fmt.Errorf | 为什么必须用 `codes.*` 做返回值 |
+| Status Details | 带结构化详情的错误响应(BadRequest / RetryInfo) |
+| 优雅降级 | 根据状态码选择重试、降级或直接报错 |
+
+### 6. [[06-连接管理]] — Keepalive、负载均衡与服务发现
+
+| 核心内容 | 说明 |
+|---------|------|
+| Keepalive 策略 | ping 间隔、超时检测、MaxConnectionAge 平滑退役 |
+| 负载均衡 Picker | pick_first / round_robin / weighted_round_robin 选型 |
+| Name Resolver | DNS / K8s / Eureka 等服务发现后端接入 |
+
+### 7. [[07-最佳实践]] — 生产部署 checklist
+
+| 核心内容 | 说明 |
+|---------|------|
+| 重试策略 | 指数退避 + 幂等性约束 + retryableStatusCodes 配置 |
+| TLS / mTLS | 服务间信任基础,Istio Sidecar 透明加密 |
+| Buf 工具链 | buf generate / buf.lock CI 集成 |
+| 性能调优 | Window Size、gzip 取舍、QPS 万级压测要点 |
+
+## 阅读路径
+
+```mermaid
+graph LR
+ Index["本文档
(索引)"] --> Arch["01 协议与架构"]
+ Arch --> Proto["02 Proto设计"]
+ Proto --> RPCC["03 RPC模式"]
+ RPCC --> Intc["04 拦截器"]
+ Intc --> Err["05 错误处理"]
+ Err --> Conn["06 连接管理"]
+ Conn --> Prod["07 最佳实践"]
+
+ style Index fill:#fff9c4
+ style Arch fill:#e3f2fd
+ style Prod fill:#ffe0b2
+```
+
+- **推荐路径**:按编号顺序逐篇阅读,每篇独立成篇也可跳读
+- **快速上手**:直接读 [[03-RPC模式]] 和 [[07-最佳实践]],掌握核心用法后按需补其他篇
+- **遇到问题时**:优先定位到对应子篇,不必通读全文
+
+## 关联笔记
+
+- [[02-服务治理/服务间通信]] — gRPC 与 REST 的基础对比及混合通信模式
+- [[02-服务治理/服务发现/README]] — Nacos / Consul / K8s Service 深度对比
+- [[02-服务治理/容错模式/README]] — 重试、熔断、限流、降级的完整治理
+- [[02-服务治理/分布式追踪/README]] — OpenTelemetry 链路追踪
+- [[02-服务治理/安全机制/README]] — mTLS、JWT、RBAC