diff --git a/hzh/MS/01-基础概念.md b/hzh/MS/01-基础概念.md
new file mode 100644
index 0000000..f953350
--- /dev/null
+++ b/hzh/MS/01-基础概念.md
@@ -0,0 +1,106 @@
+---
+tags: [microservice, architecture]
+create time: 2026-04-29 12:30
+---
+
+# 微服务基础概念
+
+## 概述
+
+本文阐述微服务架构的核心定义、设计原则与常见误区,帮助建立正确的认知框架。
+
+## 什么是微服务
+
+> [!definition] 核心定义
+> **微服务**(Microservices)是一种将单一应用拆分为一组**小服务**的架构风格。每个服务运行在独立进程中,通过轻量级通信机制协作,且**各自拥有独立的数据库**。
+
+关键特征:
+
+| 特征 | 说明 |
+|------|------|
+| 独立部署 | 每个服务可以独立构建、测试和发布 |
+| 去中心化 | 数据管理、技术选型由各团队自主决策 |
+| 容错性 | 服务故障不影响整个系统 |
+| 组织对齐 | 按业务域划分,与 Conway 定律呼应 |
+
+### 单体 vs 微服务
+
+```mermaid
+graph TB
+ subgraph Monolith["单体架构"]
+ UI["Web UI"]
+ API["API Layer"]
+ Biz["Business Logic"]
+ DB["数据库"]
+ UI --> API --> Biz --> DB
+ end
+
+ subgraph Microservices["微服务架构"]
+ GW["API Gateway"]
+ S1["Service A
+ Database"]
+ S2["Service B
+ Database"]
+ S3["Service C
+ Database"]
+ GW --> S1
+ GW --> S2
+ GW --> S3
+ S2 -.->|RPC/MQ| S1
+ end
+```
+
+> [!question] 思考
+> 既然微服务有这么多好处,为什么不是所有公司都迁移到微服务?什么时候单体反而更合适?
+
+**答案线索**:微服务引入的是**分布式复杂度**。网络延迟、数据一致性、运维成本全部上来了。对于小型团队或小规模应用,单体更简单高效。
+
+## 拆分原则:DDD 与限界上下文
+
+微服务拆分的核心思想来自 **领域驱动设计 (DDD)** —— 按 **限界上下文 (Bounded Context)** 划分。
+
+```mermaid
+graph LR
+ UC["用户中心"]
+ OS["订单服务"]
+ PS["支付服务"]
+ IS["库存服务"]
+ UC -->|查询/注册| OS
+ OS -->|创建支付| PS
+ OS -->|扣减库存| IS
+ PS -->|支付回调| OS
+ IS -->|库存确认| OS
+```
+
+> [!tip] 限界上下文不是代码边界,而是语义边界
+> 同一个词在不同上下文中可能有完全不同的含义。比如 "商品" 在电商场景中是一套完整对象,但在物流场景中可能只是一个包裹编号。明确上下文是 DDD 的第一步。
+
+> [!warning] 反模式
+> - ❌ **按技术层拆分**:Controller 层一个服务、Service 层一个服务 — 这不是微服务,是"分布式单体"
+> - ✅ **按业务域拆分**:每个服务对应一个业务能力边界
+
+## 核心设计原则
+
+> [!summary] SOLID + CAP 的组合拳
+>
+> | 原则 | 含义 | 微服务场景 |
+> |------|------|-----------|
+> | 单一职责 | 一个服务只做一件事 | 订单服务只管订单生命周期 |
+> | 高内聚低耦合 | 内部紧密,外部松散 | 服务间通过接口通信,不共享代码 |
+> | 最终一致性 | 允许短暂不一致 | 分布式环境放弃强一致性,换取可用性 |
+> | 独立失败 | 故障隔离 | 单个服务宕机不影响全局 |
+
+## 何时不应该用微服务
+
+> [!failure] 拒绝伪需求
+>
+> - 团队小于 10 人,维护单个应用就够
+> - 项目处于快速原型阶段,需求频繁变更
+> - 团队成员缺乏分布式系统设计经验
+> - 已有大型单体正在稳定运行
+
+**记住**:架构没有银弹。微服务是为了解决**特定规模下的组织和工程问题**而生的。先问自己:"我的痛点是什么?"再决定是否值得付出代价。
+
+> [!note] 后续延伸
+>
+> - [[02-服务治理]] — 服务间通信、负载均衡、熔断降级等治理机制
+> - [[03-数据一致性]] — 独立数据库架构下的分布式事务方案
+> - [[04-可观测性]] — 日志、指标、链路追踪三支柱
+> - [[05-部署运维]] — 容器化编排、灰度发布、弹性伸缩
diff --git a/hzh/MS/02-服务治理.md b/hzh/MS/02-服务治理.md
new file mode 100644
index 0000000..d7a3c6b
--- /dev/null
+++ b/hzh/MS/02-服务治理.md
@@ -0,0 +1,510 @@
+---
+tags: [microservice, service-governance, api-gateway, circuit-breaker, service-discovery, distributed-tracing, canary-deployment]
+create time: 2026-04-29 12:02
+---
+
+# 服务治理
+
+## 概述
+
+服务治理是微服务架构的"操作系统"——它不直接实现业务逻辑,但决定了系统能否在复杂、高并发、多团队协作的环境中稳定运转。
+
+本文覆盖服务治理的 **七大核心能力**:
+
+```mermaid
+quadrantChart
+ title 服务治理能力矩阵
+ x-axis Low Impact --> High Impact
+ y-axis Foundational --> Advanced
+ "服务发现": [0.25, 0.2]
+ "负载均衡": [0.45, 0.25]
+ "API Gateway": [0.35, 0.4]
+ "熔断降级": [0.55, 0.5]
+ "限流": [0.65, 0.6]
+ "链路追踪": [0.5, 0.75]
+ "配置中心": [0.4, 0.7]
+ "灰度发布": [0.75, 0.9]
+```
+
+> [!question] 开篇思考
+> 假设你有 10 个微服务,每个服务有 3 个实例,总共 30 个服务实例。手动维护它们的地址列表会带来哪些问题?你能想到几种解决方案?
+
+答案藏在下面每一个章节里——从服务发现到流量治理,每一层都在解决特定维度的复杂度。
+
+## 服务发现
+
+服务实例的动态变化(扩容、缩容、故障重启)让硬编码地址成为不可能。服务要回答的核心问题是:**我怎么找到你?**
+
+### 两种模式
+
+```mermaid
+graph TB
+ subgraph Client["客户端发现模式"]
+ A[Service A] -->|1.查询注册中心| R1[(Registry)]
+ R1 -->|2.返回实例列表| A
+ A -->|3.自选实例直连| B[Service B]
+ end
+
+ subgraph Server["服务端发现模式"]
+ C[Service C] -->|请求| P[LoadBalancer Proxy]
+ P -->|查询注册中心| R2[(Registry)]
+ R2 -->|返回实例| P
+ P -->|转发| D[Service D]
+ end
+```
+
+**对比**:
+
+| 维度 | 客户端发现 | 服务端发现 |
+|------|-----------|-----------|
+| 典型实现 | Eureka、Consul、Nacos | Nginx、Envoy、K8s Service |
+| 耦合度 | 客户端嵌入注册逻辑 | 透明代理,客户端无感知 |
+| 性能 | 直连调用,延迟最低 | 多一跳代理开销 |
+| 运维复杂度 | 每门语言需独立 SDK | 统一部署代理,技术无关 |
+
+> [!tip] 推荐路径
+> Kubernetes 环境下直接用 **K8s Service + Ingress**;非容器环境优先考虑 **Nacos**(阿里开源,国内生态好)。
+> 如果团队已有 Consul 基础设施,无需迁移——它在中小规模集群中表现优秀。
+
+### 实战:Nacos 服务发现
+
+```go
+// Nacos 服务注册 & 拉取
+import (
+ "github.com/nacos-group/nacos-sdk-go/v2/clients"
+ "github.com/nacos-group/nacos-sdk-go/v2/common/constant"
+)
+
+// 创建注册客户端
+sc := constant.ServerConfig{
+ IpAddr: "127.0.0.1",
+ Port: 8848,
+}
+
+// 注册服务实例
+client, _ := clients.NewNamingClient(
+ value_map.NewValueMap(map[string]any{"serverConfig": sc}),
+)
+
+client.RegisterInstance(naming_param.RegisterInstanceParam{
+ Ip: "10.0.0.1",
+ Port: 8080,
+ ServiceName: "order-service",
+ ClusterName: "DEFAULT",
+})
+
+// 拉取某服务的可用实例列表
+instances, _ := client.SelectInstances(naming_param.SelectInstanceParam{
+ ServiceName: "order-service",
+})
+```
+
+> [!warning] 注意
+> Nacos 同时支持临时实例(故障自动摘除)和持久实例(基于 etcd)。生产环境中临时实例更常见——节点挂了会自动从列表中剔除。
+
+**关键问题**:服务下线时,如何确保正在处理的请求不会被打断?这引出了健康检查和优雅关闭的概念。
+
+## API Gateway
+
+API Gateway 是所有外部请求的统一入口,承担以下职责:
+
+```mermaid
+graph LR
+ Client["客户端"] --> GW["API Gateway"]
+ GW --> Auth["鉴权 & 限流"]
+ GW --> Route["路由转发"]
+ GW --> Transform["协议转换"]
+ GW --> Log["日志 & 监控"]
+ GW -.-> CB[(配置中心)]
+```
+
+常见实现:**Kong、APISIX、Spring Cloud Gateway、Nginx**。
+
+### 网关放什么?
+
+> [!summary] 网关职责清单
+>
+> - ✅ 鉴权与认证
+> - ✅ 限流熔断
+> - ✅ HTTPS 终结
+> - ✅ 请求/响应转换
+> - ✅ 路由分发
+> - ❌ 业务逻辑 — 网关保持瘦,重逻辑应下沉到业务服务
+
+> [!question] 权衡题
+> 有人说:"鉴权放到网关统一做,避免每个服务重复写。"但如果某个内部服务不需要鉴权呢?或者不同团队要求不同的鉴权方式呢?你怎么设计才能兼顾统一性和灵活性?
+
+## 服务间通信
+
+### RPC vs RESTful API
+
+| 维度 | REST over HTTP/JSON | gRPC (HTTP/2) |
+|------|---------------------|---------------|
+| 性能 | 序列化开销大 | Protocol Buffers,二进制高效 |
+| 人类可读 | URL + JSON 直观 | 需要 Proto 定义辅助理解 |
+| 语言兼容性 | 广泛,任何能发 HTTP 的语言都能用 | 需要代码生成 |
+| 适用场景 | 对外 API、跨团队/跨组织调用 | 内部服务间高频强类型调用 |
+
+```go
+// Go 示例:gRPC Protobuf 定义
+// proto/order/v1/order.proto
+syntax = "proto3";
+package order.v1;
+
+service OrderService {
+ rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);
+ rpc GetOrder(GetOrderRequest) returns (Order);
+}
+
+message CreateOrderRequest {
+ string user_id = 1;
+ repeated Item items = 2;
+ float total = 3;
+}
+
+message Item {
+ string product_id = 1;
+ int32 quantity = 2;
+}
+```
+
+### 同步 vs 异步
+
+> [!question] 关键时刻的判断
+> 用户点击"提交订单"后,系统要做:① 创建订单记录 ② 扣减库存 ③ 扣款 ④ 发送短信通知。哪些步骤必须同步完成?哪些可以异步处理?为什么?
+
+**答案**:①~③需要立即得到结果或保证一致性,走同步流程;④纯粹的通知类场景完全异步,用消息队列解耦。即使扣款失败,短信也没必要发出。
+
+```go
+// 异步消息:Go + RabbitMQ 简易示例
+producer.Publish("orders", []byte(`{
+ "orderId": "ORD-2026-001",
+ "userId": "user-42",
+ "amount": 199.00
+}`))
+
+// 消费者监听
+messages := consumer.Consume("order-notifications")
+for msg := range messages {
+ sendSms(msg.OrderId) // 不阻塞主流程
+}
+```
+
+> [!tip] 选型建议
+> 事件驱动架构(Event-driven)适合「最终一致性」场景;强一致事务需求仍依赖 SAGA/TCC 等分布式事务方案。
+
+## 容错模式
+
+微服务最大的挑战是 **网络不可靠**。分布式系统的每个远程调用都可能超时、被拒绝、或部分成功。必须提前设计降级和恢复策略。
+
+### 重试机制
+
+> [!warning] 关键原则
+> 只对 **幂等操作** 重试!POST 创建操作直接重试会导致重复数据。GET、PUT(同参数)适合重试。
+
+```go
+// exponential backoff + jitter(防雪崩)
+func retryWithBackoff(ctx context.Context, maxRetries int, fn func() error) error {
+ for attempt := uint(0); attempt <= uint(maxRetries); attempt++ {
+ if err := fn(); err != nil {
+ if attempt == uint(maxRetries) {
+ return err
+ }
+ // jitter: 随机偏移,避免所有客户端同时重试造成二次冲击
+ jitter := time.Duration(rand.Int63n(int64(time.Millisecond * 100)))
+ wait := time.Duration(math.Pow(2, float64(attempt)))*time.Second + jitter
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case <-time.After(wait):
+ }
+ } else {
+ return nil
+ }
+ }
+ return nil
+}
+```
+
+### 熔断器 (Circuit Breaker)
+
+当下游服务频繁失败时,快速失败避免线程堆积雪崩:
+
+```mermaid
+stateDiagram-v2
+ [*] --> Closed: "正常状态"
+ Closed --> Open: "失败率超过阈值"
+ Open --> HalfOpen: "等待探测间隔"
+ HalfOpen --> Closed: "探测成功"
+ HalfOpen --> Open: "探测失败"
+```
+
+```go
+// gobreaker 示例
+cb := state.NewCB(state.Settings{
+ Name: "UserService",
+ MaxElems: 10,
+ WaitRetry: 5 * time.Second,
+ ReadyToTrip: func(counts Counters) bool {
+ return counts.Failures > 5
+ },
+})
+
+result := cb.Execute(doRequest)
+if result.Err != nil {
+ // 熔断打开,立即短路返回错误,不调用下游
+}
+```
+
+### 健康检查
+
+```mermaid
+sequenceDiagram
+ participant LB as 负载均衡器
+ participant S1 as 服务实例 A
+ participant S2 as 服务实例 B
+
+ LB->>S1: "GET /health -> 200 OK"
+ LB->>S2: "GET /health -> 503 ERROR"
+ Note over LB: "标记 S2 不健康,剔除出池"
+ LB->>S2: "GET /health -> 200 OK"
+ Note over LB: "逐步恢复,重新加入池"
+```
+
+- **存活探针 (Liveness)**:判断"进程是否还活着",挂了则重启
+- **就绪探针 (Readiness)**:判断"是否能接收流量",未就绪则剔除负载均衡池
+- ** readiness 比 liveness 更重要**——一个进程活着但数据库连接耗尽时,应该停止接收流量而不是重启
+
+> [!summary] 舱壁隔离 (Bulkhead)
+>
+> 为不同下游服务分配独立的线程池/连接池:
+>
+> ```go
+> // poolgroup 示例:为不同服务隔离连接池
+> orderPool := pool.New(10, 100) // 最多10个活跃,上限100
+> userPool := pool.New(5, 50)
+> // 订单服务占满连接不会影响用户服务的调用
+> ```
+>
+> 超时 + 熔断 + 舱壁三者组合,构成了经典的稳定性防护三件套。
+
+## 负载均衡
+
+```mermaid
+flowchart LR
+ subgraph "客户端负载均衡 (Client-Side)"
+ A[Service A] --> RR[轮询]
+ A --> LH[最少连接]
+ A --> CH[一致性哈希]
+ end
+
+ subgraph "服务端负载均衡 (Server-Side)"
+ B[Service B] --> VIP[[Virtual IP]]
+ VIP --> B1[B-1]
+ VIP --> B2[B-2]
+ VIP --> B3[B-3]
+ end
+```
+
+| 策略 | 说明 | 适用场景 |
+|------|------|---------|
+| Round Robin | 轮询分配 | 各实例负载相近,请求耗时均匀 |
+| Least Connections | 最少连接优先 | 长连接场景,请求处理时间差异大 |
+| Consistent Hash | 哈希路由,同一 key 始终到同实例 | 有状态缓存、会话绑定场景 |
+| Random | 随机选择 | 最简单但不一定最优 |
+
+> [!tip] K8s 中的实践
+> K8s Service 默认 Round Robin;如需更精细的控制,可配合 **Istio VirtualService** 实现基于权重/头部的流量分发。
+
+## 配置管理
+
+> [!question] 引出配置中心的必要性
+> 假设你的 50 个服务实例都要连同一个数据库。现在需要把数据库密码从 `password1` 改为 `password2`,你会怎么改?登录每台机器改配置文件?滚动重启所有 Pod?还是……有更好的办法?
+
+当服务实例数以百计时,手动管理配置是不可想象的。配置中心提供 **集中化、动态化、版本化** 的配置管理能力。
+
+### 核心价值
+
+```mermaid
+flowchart LR
+ Dev["开发/运维"] --> CC[(配置中心)]
+ CC -->|热更新推送| S1[Service A]
+ CC -->|热更新推送| S2[Service B]
+ CC -->|热更新推送| S3[Service C]
+
+ CC -.->|版本管理| V1[v1.0 历史配置]
+ CC -.->|版本管理| V2[v1.1 当前配置]
+```
+
+| 能力 | 说明 |
+|------|------|
+| 动态刷新 | 修改配置后立即生效,无需重启 |
+| 环境隔离 | dev / test / prod 配置分离 |
+| 版本管理与回滚 | 每次变更有迹可循,一键回滚 |
+| 权限控制 | 敏感配置(密钥、Token)按角色隔离 |
+
+### Nacos Config 示例
+
+```go
+// 动态监听配置变更
+configClient, _ := clients.NewConfigClient(value_map.NewValueMap(map[string]any{
+ "serverConfig": sc,
+}))
+
+content, _ := configClient.GetConfig(config_param.GetConfigParam{
+ DataId: "order-service.yaml",
+ Group: "DEFAULT_GROUP",
+})
+
+// 监听配置变化
+configClient.ListenChange(config_param.ListenChangeParam{
+ DataId: "order-service.yaml",
+ Group: "DEFAULT_GROUP",
+ Callback: func(content string) {
+ fmt.Println("配置更新了:", content)
+ // 重新加载配置...
+ },
+})
+```
+
+> [!note] 主流对比
+>
+> - **Nacos Config**:阿里开源,国内首选,兼顾客户端发现+配置管理
+> - **Apollo**:携程开源,配置审核流程完善,适合大型团队
+> - **Spring Cloud Config**:与 Spring 生态深度集成,但需额外 Git/DB 存储后端
+> - **K8s ConfigMap**:原生方案,适合纯 K8s 环境,不支持热更新需配合 Sidecar
+
+## 分布式链路追踪
+
+> [!question] 定位问题的困难
+> 用户反映下单慢,你的系统由订单、支付、库存、会员 4 个服务串联而成。没有工具的情况下,你要怎么知道是哪个服务拖慢了整体响应时间?
+
+单个服务的日志只能告诉你局部信息。分布式链路追踪将一次请求跨越多个服务的完整调用链串起来,形成全局视图。
+
+### 核心概念
+
+```mermaid
+flowchart TB
+ Req["请求 trace_id=abc123"] --> Span1["订单服务 - 20ms"]
+ Span1 --> Span2["库存服务 - 80ms"]
+ Span1 --> Span3["会员服务 - 15ms"]
+ Span2 --> Span4["数据库查询 - 60ms"]
+
+ style Span2 fill:#ff9999
+```
+
+- **Trace**:一次完整请求的调用链
+- **Span**:链路中的一个执行片段(如一次 HTTP 调用、一次 SQL 查询)
+- **Context Propagation**:通过 Header 传递 trace_id/span_id,贯穿整条链路
+
+### 主流方案
+
+| 方案 | 协议 | 存储后端 | 特点 |
+|------|------|---------|------|
+| **OpenTelemetry** | OTLP | Prometheus/Jaeger/Zipkin | CNCF 标准,厂商中立,未来趋势 |
+| **Jaeger** | Jaeger native | Cassandra/Elasticsearch | Uber 开源,UI 友好 |
+| **SkyWalking** | SkyWalking | MySQL/Elasticsearch | 国产,零侵入 Agent,中文文档完善 |
+
+### OpenTelemetry Go 示例
+
+```go
+// 初始化 tracer provider
+provider := sdktrace.NewTracerProvider(
+ sdktrace.WithBatcherExporter(exporter), // 异步上报,不阻塞
+)
+
+tracer := provider.Tracer("order-service")
+
+// 在 handler 中创建 span
+func handleOrder(w http.ResponseWriter, r *http.Request) {
+ ctx, span := tracer.Start(r.Context(), "handleOrder")
+ defer span.End()
+
+ // 传播 context 到下游
+ resp, err := callInventoryService(ctx) // SDK 自动注入 trace context
+ if err != nil {
+ span.RecordError(err)
+ span.SetStatus(codes.Error, err.Error())
+ return
+ }
+ w.Write(resp.Body)
+}
+```
+
+> [!tip] 渐进式落地建议
+> 不要试图一开始就采集全部 Span。先从 **核心链路**(下单、支付)入手,采集 rate 设为 100%;稳定后再逐步扩展,日常采样率可降至 5%~10% 以节省存储。
+
+## 灰度发布与流量治理
+
+> [!question] 如何安全地上线?
+> 你修复了一个紧急 Bug,但这次改动涉及核心支付链路。直接全量发布一旦出问题损失巨大。有没有办法先小范围验证,确认没问题再放量?
+
+灰度发布(也叫金丝雀发布)是降低变更风险的核心手段。它让你将新版本逐步推向一小部分用户,观察指标正常后再全量。
+
+### 灰度策略
+
+```mermaid
+flowchart LR
+ subgraph "Canary Release"
+ A["1% 流量"] --> B{"指标正常?"}
+ B -- 是 --> C["10% 流量"]
+ B -- 否 --> D["自动回滚"]
+ C --> E{"指标正常?"}
+ E -- 是 --> F["50% 流量"]
+ E -- 否 --> D
+ F --> G{"指标正常?"}
+ G -- 是 --> H["100% 全量"]
+ G -- 否 --> D
+ end
+```
+
+### 灰度路由规则
+
+```yaml
+# Istio VirtualService 示例
+apiVersion: networking.istio.io/v1beta1
+kind: VirtualService
+metadata:
+ name: order-route
+spec:
+ hosts: ["order-service"]
+ http:
+ # 灰度规则:header 携带 canary=true 的用户走 v2
+ - match:
+ - headers:
+ x-canary:
+ exact: "true"
+ route:
+ - destination:
+ host: order-service
+ subset: v2 # 新版本
+ weight: 100
+ # 默认:全部走 v1(线上稳定版)
+ - route:
+ - destination:
+ host: order-service
+ subset: v1 # 旧版本
+ weight: 100
+```
+
+### 蓝绿 vs 灰度
+
+> [!summary] 两种发布策略对比
+>
+> | 维度 | 蓝绿部署 | 灰度发布 (金丝雀) |
+> |------|---------|-----------------|
+> | 切换方式 | 一次性全切 | 逐步放量 |
+> | 资源消耗 | 双倍(新旧并行) | 初期少量副本 |
+> | 回滚速度 | 秒级(切回即可) | 同样秒级 |
+> | 风险等级 | 中高 | 低 |
+> | 适合场景 | 大型变更、重大版本 | 日常迭代、高风险链路 |
+
+> [!tip] 实战经验
+> 无论哪种发布方式,**必须有可量化的观测指标作为决策依据**——错误率、P99 延迟、CPU/内存使用率。不要凭感觉放量,让数据说话。
+
+## 关联笔记
+
+- [[hzh/MS/01-基础概念]] — 微服务的整体架构认知
+- [[hzh/MS/05-部署运维]] — K8s Service 天然提供负载均衡和服务发现
+- [[hzh/MS/03-数据一致性]] — 微服务下的数据分片与一致性策略
+- [[hzh/MS/04-可观测性]] — 日志、指标、链路追踪的完整体系
diff --git a/hzh/MS/03-数据一致性.md b/hzh/MS/03-数据一致性.md
new file mode 100644
index 0000000..b12426d
--- /dev/null
+++ b/hzh/MS/03-数据一致性.md
@@ -0,0 +1,217 @@
+---
+tags: [microservice, distributed-system, database, data-consistency, saga-pattern, tcc, idempotency, outbox-pattern]
+create time: 2026-04-29 12:03
+---
+
+# 数据一致性
+
+## 概述
+
+微服务的核心设计原则是 **"每个服务拥有独立数据库"**,这带来了分布式事务和数据一致性的经典难题。本文梳理主要解决方案及其取舍。
+
+## 为什么不能共享数据库
+
+```mermaid
+graph LR
+ S1[订单服务 DB]
+ S2[库存服务 DB]
+
+ subgraph BAD["反模式:共享数据库"]
+ S1 --- SHARED[(共享 DB)]
+ S2 --- SHARED
+ end
+```
+
+> [!failure] 反模式警告
+> 如果两个服务连接同一个数据库的同一张表,它们就不再是独立的微服务——你得到的是**分布式单体**。服务可以随意互相查询彼此的数据,失去了边界和自治性。
+
+## 分布式事务方案概览
+
+| 方案 | 一致性级别 | 性能 | 复杂度 | 适用场景 |
+|------|-----------|------|--------|---------|
+| 本地事务 + 事件通知 | 最终一致 | 高 | 低 | 绝大多数场景 |
+| Saga 模式 | 最终一致 | 中 | 中 | 长流程业务 |
+| TCC | 强最终一致 | 中 | 高 | 对一致性要求较高的场景 |
+| AT 模式 (Seata) | 伪强一致 | 中低 | 低 | 不想改业务代码时 |
+
+> [!info] 选择策略
+> 先默认用 **本地事务 + 异步事件**(最简单、最高效),只有在业务明确需要 Saga 或 TCC 时才升级。
+
+## 方案一:本地事务 + 消息队列
+
+这是**最常用**的方案,核心思想是将"数据变更 + 发消息"合并到一个本地事务中。
+
+```mermaid
+sequenceDiagram
+ participant U as 用户
+ participant O as 订单服务
+ participant MQ as 消息队列
+ participant I as 库存服务
+
+ U->>O: 创建订单
+ O->>O: BEGIN 事务
+ O->>O: 插入订单记录
+ O->>MQ: 发送订单创建消息
+ O->>O: COMMIT 事务
+ MQ-->>I: 投递消息
+ I->>I: 扣减库存
+ I-->>O: 返回结果(可选)
+```
+
+### 实战:电商订单创建流程
+
+以「用户下单」为例,这个流程涉及 **订单服务**(创建订单)和 **库存服务**(扣减库存),是典型的跨服务数据一致性问题:
+
+| 步骤 | 动作 | 一致性保障点 |
+|------|------|-------------|
+| 1 | 用户点击"提交订单" | 前端防重复提交(按钮置灰 / Token 机制) |
+| 2 | 订单服务写入订单表 + 发送 `order.created` 消息 | **事务边界**:两件事必须在同一个本地事务中完成 |
+| 3 | 消息队列投递给库存服务 | 保证消息至少一次投递(At-Least-Once) |
+| 4 | 库存服务扣减库存 | **幂等性**:防止消息重投导致库存被扣两次 |
+| 5 | 库存不足时回滚订单 | 通过 Sagas 补偿:已创建的订单标记为"取消" |
+
+> [!question] 思考
+> 如果步骤 2 的数据库写成功了,但 MQ 发消息失败了,会发生什么?用户看到下单成功但实际上库存没被锁定。这说明为什么必须用事务消息或本地消息表,而不是直接调 MQ API。
+
+---
+
+### 如何保证"事务内既写库又发消息"?
+
+**方案 A:事务消息**(推荐)
+- RocketMQ /阿里云 MSE 原生支持事务消息
+- MQ 回查机制确保消息不丢
+
+**方案 B:本地消息表**
+- 在业务库里建一张 `outbox` 表
+- 业务事务同时写入业务数据和消息记录
+- 后台定时任务扫描未发送的消息推送出去
+
+```go
+// 本地消息表方案示意
+tx, _ := db.Begin()
+// 1. 业务操作
+tx.Exec("UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, fromID)
+// 2. 记录待发送消息(带状态字段)
+tx.Exec("INSERT INTO outbox (topic, payload, status, created_at) VALUES (?, ?, 'pending', NOW())", "order.created", jsonPayload)
+tx.Commit()
+// 后台进程轮询 outbox 表中 status='pending' 的记录,发送到 MQ 后更新为 'sent'
+```
+
+#### Outbox 表建表示例
+
+```sql
+CREATE TABLE outbox (
+ id BIGSERIAL PRIMARY KEY,
+ topic VARCHAR(255) NOT NULL, -- 消息主题
+ payload JSONB NOT NULL, -- 消息体
+ status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending / sent / failed
+ error_msg TEXT, -- 失败原因
+ created_at TIMESTAMP NOT NULL DEFAULT NOW(),
+ sent_at TIMESTAMP
+);
+
+-- 加速定时扫描查询
+CREATE INDEX idx_outbox_pending ON outbox (status, created_at) WHERE status = 'pending';
+```
+
+### 消费幂等性
+
+消息可能重复投递(网络超时、MQ 重投),消费者必须做到**幂等**——处理一次和处理多次的结果完全相同。
+
+**三种常见策略:**
+
+| 策略 | 实现方式 | 适用场景 |
+|------|---------|---------|
+| 数据库唯一约束 | 用 `msg_id` 或业务主键做 `UNIQUE` | 最可靠,推荐首选 |
+| Redis 去重键 | SET 一个 TTL Key(如 `dedup:{msg_id}` 过期 24h) | 高吞吐场景,需考虑 Redis 可用性 |
+| 乐观锁版本控制 | UPDATE 时加 `WHERE version = old_version` | 适用于金额调整类操作 |
+
+```go
+// 推荐方案:利用 UNIQUE 约束做幂等保障
+_, err := db.Exec(`
+ INSERT INTO order_events (msg_id, order_id, action, amount)
+ VALUES ($1, $2, $3, $4)
+ ON CONFLICT (msg_id) DO NOTHING -- 重复到达时静默忽略
+`, msgID, orderID, action, amount)
+if err != nil {
+ // 其他错误才需要处理,UNIQUE 冲突直接忽略
+ if !isUniqueViolation(err) {
+ log.Error("process message failed", err)
+ }
+}
+// 执行业务逻辑...
+```
+
+> [!tip] 设计要点
+> `ON CONFLICT DO NOTHING` 比先查后插更可靠——它消除了竞态窗口。即使用两个实例同时收到同一条消息,只会有一个成功写入。
+
+> [!question] 思考
+> 为什么要在生产者侧保证消息可靠投递,而不是靠消费者反复拉取重试?
+
+## 方案二:Saga 模式
+
+Saga 适用于**跨多个服务的长流程操作**,将大事务拆成一系列本地小事务,每个步骤都有对应的补偿操作。
+
+```mermaid
+graph LR
+ S1[① 创建订单
→ 取消订单]
+ S2[② 扣减库存
→ 恢复库存]
+ S3[③ 支付扣款
→ 退款]
+ S4[④ 发货
→ 无补偿]
+
+ S1 --> S2 --> S3 --> S4
+```
+
+**编排式 vs 编舞式**:
+
+```mermaid
+graph TB
+ subgraph ORCHESTRATION["编排式 — 中心协调器"]
+ CO[Coordinator] --> S1[OrderSvc]
+ CO --> S2[InventorySvc]
+ CO --> S3[PaymentSvc]
+ end
+
+ subgraph CHOREOGRAPHY["编舞式 — 事件驱动"]
+ E1[订单创建] --> S11[订单服务]
+ S11 --> E2[库存不足事件]
+ E2 --> S12[库存服务]
+ S12 --> E3[扣减完成事件]
+ E3 --> S13[支付服务]
+ end
+```
+
+| | 编排式 | 编舞式 |
+|---|--------|--------|
+| 控制流 | 中心化 Coordinator | 各服务通过事件自发响应 |
+| 可观测性 | ✅ 集中管理 | ❌ 流程散布在各服务 |
+| 耦合度 | 依赖 Coordinator | 服务间仅感知事件 |
+
+## 方案三:TCC 与 AT 模式
+
+> [!abstract] 进阶了解
+>
+> **TCC** (Try-Confirm-Cancel):每个事务步骤实现三个接口。适合对一致性要求严格且能承受开发成本的业务。
+>
+> **AT 模式** (Seata):框架层自动处理两阶段提交,对业务透明。本质上是基于 undo_log 的增强型 XA,性能低于 Saga。
+
+## 总结选型指南
+
+> [!summary] 决策树
+>
+> ```mermaid
+> graph TD
+> A["跨几个服务"] -->|"1-2"| B["本地事务 + 事件通知"]
+> A -->|"3+"| C["Saga 模式"]
+> B -->|"强"| D["加幂等,必要时上 TCC"]
+> B -->|"弱"| E["本地事务已够"]
+> C -->|"不能"| F["考虑 AT 模式"]
+> C -->|"能"| G["自行实现补偿"]
+> ```
+>
+> **经验法则**:80% 的场景,本地事务 + MQ 就足够了。先别急着上复杂方案。
+
+## 关联笔记
+
+- [[01-基础概念]] — 微服务拆分与独立数据库原则
+- [[02-服务治理]] — 服务调用链路上的超时、重试、熔断
diff --git a/hzh/MS/04-可观测性.md b/hzh/MS/04-可观测性.md
new file mode 100644
index 0000000..be41122
--- /dev/null
+++ b/hzh/MS/04-可观测性.md
@@ -0,0 +1,446 @@
+---
+tags: [microservice, observability, monitoring, tracing]
+create time: 2026-04-29 12:04
+---
+
+# 可观测性
+
+## 概述
+
+微服务架构下,一个请求可能穿越十几个甚至上百个服务。**排查问题就像在大海捞针**。可观测性通过三大支柱(Metrics、Logging、Tracing)让系统行为变得"可见"。
+
+> [!question] 核心思考
+> 单体应用出问题,SSH 到服务器上 `tail -f` 日志就能定位。但当你有 50 个服务、每个 3 个副本,日志散落在不同容器里——你还能用同样的方式排障吗?如果不能,什么体系能替代它?
+
+### 为什么需要独立的"可观测性"体系?
+
+```mermaid
+flowchart LR
+ subgraph MONO["单体系统"]
+ S[Service] --> DB[(DB)]
+ LOG["同一份日志
一目了然"]
+ S --> LOG
+ end
+
+ subgraph MICRO["微服务系统"]
+ REQ("Request") --> GW["Gateway"]
+ GW --> O1["Order Svc"]
+ GW --> U1["User Svc"]
+ O1 --> PAY["Payment Svc"]
+ O1 --> INV["Inventory Svc"]
+ PAY --> DB1[(DB)]
+ INV --> DB2[(DB)]
+
+ style O1 fill:#faa
+ style U1 fill:#faa
+ style PAY fill:#faa
+ style INV fill:#faa
+ end
+
+ NOTE["问题:日志分散、链路断裂
传统监控无法回答'这个请求经历了什么'"]
+ MICRO --> NOTE
+```
+
+可观测性与监控的本质区别:**监控系统告诉你"出事了"**(已知未知),**可观测性系统让你去探究"为什么会出事"**(未知未知)。
+
+### SLO 与 Error Budget
+
+可观测性的终极目标不是收集数据,而是**支撑决策**。SLO(Service Level Objective)将技术指标映射到用户体验。
+
+> [!summary] SLI / SLO / SLA 三层模型
+>
+> | 层级 | 含义 | 示例 |
+> |------|------|------|
+> | **SLI** (Indicator) | 实际测量的指标 | P99 延迟 = 120ms |
+> | **SLO** (Objective) | 内部目标值 | P99 延迟 < 200ms(99.9% 的请求) |
+> | **SLA** (Agreement) | 对外的契约承诺 | 可用性 ≥ 99.95%,否则赔偿 |
+
+> [!tip] Error Budget(错误预算)
+>
+> 如果 SLO 是 99.9%,那么每月允许的错误时间 = 30 × 24 × 60 × 0.1% ≈ **43 分钟**。
+>
+> - **预算充足时**:可以大胆发布新功能、尝试激进方案
+> - **预算耗尽时**:冻结非功能性变更,专注稳定性修复
+>
+> 这构成了"创新"和"稳定"之间的动态平衡机制。
+
+## 三大支柱
+
+| 支柱 | 回答的问题 | 典型工具 |
+|------|-----------|---------|
+| **Metrics** | 系统现在健康吗? — 趋势、告警 | Prometheus + Grafana |
+| **Logging** | 具体发生了什么? — 历史回溯 | ELK / Loki |
+| **Tracing** | 请求在哪一步慢了/失败了? — 链路追踪 | Jaeger / Zipkin |
+
+### Metrics:系统的脉搏
+
+Metrics 回答的问题是:**系统现在健康吗?**——通过聚合后的数值揭示趋势。
+
+> [!summary] RED 和 USE 方法
+>
+> | 方法 | 适用于 | 指标 |
+> |------|--------|------|
+> | **RED** (Rate, Errors, Duration) | 有状态服务(API) | QPS、错误率、响应时间 P99 |
+> | **USE** (Utilization, Saturation, Errors) | 基础设施(CPU/内存/网络) | 使用率、饱和度、错误数 |
+
+核心指标类型:
+
+- **Counter(计数器)**:只增不减,如 `http_requests_total`。适合统计请求总量、错误总数
+- **Gauge(仪表盘)**:可增可减,如 `queue_depth`、在线用户数
+- **Histogram(直方图)**:样本分布,自动分桶统计,如请求延迟的 P50/P90/P99
+- **Summary(摘要)**:类似 Histogram,但客户端计算分位数(Go SDK 默认)
+
+```go
+// Go 示例:定义并注册 Prometheus 指标
+var (
+ // Counter: HTTP 请求总数,按 method 和 status 标签分类
+ httpRequestsTotal = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "http_requests_total",
+ Help: "Total HTTP requests",
+ },
+ []string{"method", "status"},
+ )
+
+ // Histogram: API 响应延迟分布
+ apiLatency = prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Name: "api_latency_seconds",
+ Help: "API latency distribution",
+ Buckets: prometheus.DefBuckets, // [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
+ },
+ []string{"endpoint"},
+ )
+)
+
+func init() {
+ prometheus.MustRegister(httpRequestsTotal, apiLatency)
+}
+```
+
+> [!tip] 解读提示
+> Counter 用于统计"发生了多少",Histogram 用于理解"有多慢"。一个完整的告警面板通常同时展示两者:QPS 突降 + 延迟飙升 = 大概率出事了。
+
+在 Grafana 中的典型视图:
+
+```mermaid
+graph TB
+ subgraph GRAFANA["Grafana Dashboard"]
+ G1["QPS
[Counter]"] -->|关联分析| G2["P99 Latency
[Histogram]"]
+ G3["错误率
[Counter %]"] -->|关联分析| G2
+ G2 --> G4["告警规则
阈值触发"]
+ end
+
+ GRAFANA -.->|从 Prometheus 拉取| PROM[(Prometheus)]
+```
+
+### Logging:集中化日志
+
+Logging 回答的问题是:**具体发生了什么?**——通过原始事件记录提供上下文。
+
+- **所有服务的日志必须集中收集**,不能散落在各台机器上
+- 日志格式推荐 JSON,便于解析
+- 每条日志必须携带 `trace_id`,与 Tracing 串联
+
+```json
+{
+ "timestamp": "2026-04-29T12:00:00Z",
+ "level": "ERROR",
+ "service": "order-service",
+ "trace_id": "abc-123-def",
+ "span_id": "span-456",
+ "msg": "failed to connect payment service",
+ "caller": "payment/client.go:42",
+ "cost_ms": 30000
+}
+```
+
+#### 结构化日志的核心字段
+
+> [!note] 最小集合(必选)
+>
+> - `timestamp` — ISO 8601 格式,统一时区 UTC
+> - `level` — TRACE / DEBUG / INFO / WARN / ERROR / FATAL
+> - `service` — 微服务名称
+> - `trace_id` — 链路追踪 ID,用于跨服务关联
+> - `msg` — 人类可读的消息描述
+
+> [!warning] 日志最佳实践
+>
+> - **不要记录敏感信息**:密码、Token、身份证号等绝不能出现在日志中
+> - **INFO 级记录关键业务事件**:下单成功、支付回调——这些是排查业务问题的主线
+> - **WARN 级记录异常但不致命**:重试了一次、降级走了备用方案
+> - **ERROR 级必须有 trace_id 和错误堆栈**,否则毫无价值
+
+#### 日志采集管线
+
+```mermaid
+flowchart LR
+ APP[应用容器] -->|stdout/stderr| LO[FLUENTD / Filebeat]
+ LO -->|TCP/Lumberjack| LO2[(Logstash / Loki)]
+ LO2 -->|解析 & 过滤| PIPE[Pipeline]
+ PIPE -->|写入| ES[(Elasticsearch)]
+ PIPE -->|跳转| GRAF[Grafana / Kibana]
+
+ style PIPE fill:#ff9
+```
+
+**常见方案对比**:
+
+| 方案 | 存储 | 查询能力 | 适用场景 |
+|------|------|---------|---------|
+| ELK (Elasticsearch) | ES 集群 | 全文检索强大,灵活度高 | 日志量大、需要复杂分析的团队 |
+| Loki + Grafana | 对象存储 (S3) | 对标 LogQL,轻量高效 | 已经在用 Grafana 的团队 |
+| CloudWatch / SLS | 云厂商托管 | 开箱即用,成本较高 | 全云原生环境 |
+
+> [!tip] 日志采样策略
+>
+> 生产环境中并非所有日志都要采集。**正常路径采 1%,异常路径全量**。这既能大幅降低存储成本,又能在出问题时有足够的调试数据可用。采样逻辑建议写在日志库中,而非业务代码里。
+
+### Tracing:分布式链路追踪
+
+Tracing 回答的问题是:**请求在哪一步慢了 / 失败了?**——通过完整的调用链路定位瓶颈和故障。
+
+一次请求在多个服务中的完整调用路径:
+
+```mermaid
+flowchart LR
+ RID["Request
trace_id: abc-123"]
+
+ subgraph SPANS["调用链 (Trace)"]
+ direction TB
+ S1["Span #1
API Gateway
5ms"]
+ S2["Span #2
Order Service
70ms"]
+ S3["Span #3
Payment RPC
160ms"]
+ S4["Span #4
Inventory RPC
70ms"]
+
+ S1 --> S2 --> S3
+ S2 --> S4
+ end
+
+ RID --> S1
+ style S3 fill:#ff9999
+
+ note["Span #3 耗时最长 → Payment 是瓶颈"]
+ S3 -.-> note
+```
+
+> [!tip] 关键实践
+> - 确保 `trace_id` 在整个调用链中传递(HTTP Header 或 MQ Message Header)
+> - 采样策略:**全量采集开发环境**,**生产环境按百分比采样**(避免存储爆炸)
+> - 重点关注的 Span 标签:HTTP method、status code、database query
+> - **Span 不要嵌套过深**,一个 HTTP 调用或 DB 查询对应一个 Span 即可
+
+#### Context Propagation(上下文传播)
+
+trace_id 如何从上游服务传递到下游?
+
+```go
+// 1. 入站:从 HTTP Header 提取 trace context
+func Middleware(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // OpenTelemetry SDK 自动从 Header 恢复 span context
+ ctx := propagator.Extract(r.Context(), headerReader{r.Header})
+
+ // 2. 创建新的 span
+ ctx, span := tracer.Start(ctx, "handleOrder")
+ defer span.End()
+
+ // 3. 将 trace context 注入到新请求的 Header 中
+ r = r.WithContext(ctx)
+ propagator.Inject(ctx, headerWriter{r.Header})
+
+ next.ServeHTTP(w, r)
+ })
+}
+```
+
+```json
+// HTTP Header 中实际传递的字段 (W3C Trace Context)
+{
+ "traceparent": "00-abc123def456...-789ghi012jkl-01",
+ "tracestate": "congo=t61rcWkgMzE"
+}
+```
+
+> [!note] W3C Trace Context 标准
+>
+> `traceparent` 格式:`version-trace_id-span_id-flags`,如 `00-{32位trace_id}-{16位span_id}-{2位flags}`。
+> 这是 W3C 推荐的标准,被 OpenTelemetry、Jaeger、Zipkin 等主流方案统一支持。**无需再自定义 Header**。
+
+## 告警设计原则
+
+### 告警疲劳是常态
+
+> [!warning] 核心问题
+>
+> 如果一个团队每天收到 200 条告警,其中 195 条是误报或无需处理,工程师会对剩下的 5 条真正重要的告警产生"脱敏"。这就是 **告警疲劳**。
+
+- **告警要能 actionable**:收到告警后知道该做什么,否则不要告
+- **区分级别**:P0(电话叫醒)vs P1(工单处理)vs P2(次日处理)
+- **基于 SLO 告警**:不是 CPU > 80% 就告警,而是用户感知到延迟增加时才告
+- **降噪**:同一个根因可能触发连锁告警,需要抑制机制
+
+### 告警层级设计
+
+```mermaid
+flowchart TD
+ subgraph L1["L1: 页面上的用户受损"]
+ A1["HTTP 5xx 错误率 > 1%"]
+ A2["核心接口 P99 > 2s"]
+ end
+
+ subgraph L2["L2: 系统健康度下降"]
+ B1["单个服务错误率 > 5%"]
+ B2["下游依赖超时率升高"]
+ end
+
+ subgraph L3["L3: 资源预警"]
+ C1["磁盘使用 > 70%"]
+ C2["内存使用 > 80%"]
+ end
+
+ A1 -->|P0 - 电话 + IM| ONCALL[On-Call 值班]
+ A2 -->|P0 - 电话 + IM| ONCALL
+ B1 -->|P1 - IM 通知| OPS[运维群]
+ B2 -->|P1 - IM 通知| OPS
+ C1 -->|P2 - 日报汇总| TEAM[团队看板]
+ C2 -->|P2 - 日报汇总| TEAM
+```
+
+> [!tip] 告警规则编写清单
+>
+> 每条告警规则都应该能回答以下问题:
+> 1. **什么问题?** — 清晰描述故障现象
+> 2. **谁负责?** — 指定明确的责任人 / On-Call
+> 3. **怎么修复?** — 链接到 Runbook / 处置文档
+> 4. **多久升级?** — P1 无人响应则自动升级到上级
+
+## OpenTelemetry:统一的可观测性标准
+
+过去,Metrics、Logging、Tracing 各用各的工具链,数据割裂在完全不同的系统中。**OpenTelemetry (OTel)** 试图解决这个问题。
+
+> [!summary] OTel 的核心思想
+>
+> 一套 SDK → 三种信号(Metrics、Logs、Traces)→ 任意后端(Jaeger、Prometheus、ELK...)
+>
+> 这意味着:**你不需要在代码里为不同后端写多套采集逻辑**。换一个后端只需要改配置,不改代码。
+
+### OTel Architecture
+
+```mermaid
+flowchart LR
+ subgraph APP["你的应用"]
+ OTEL_SDK["OpenTelemetry SDK
auto-instrumentation Agent"]
+ end
+
+ OTEL_SDK -->|SDK 自动采集| COLLECTOR[(OpenTelemetry Collector)]
+
+ COLLECTOR -->|OTLP| JAEGER[(Jaeger
Tracing)]
+ COLLECTOR -->|OTLP| PROM[(Prometheus
Metrics)]
+ COLLECTOR -->|OTLP| LOKI[(Loki
Logging)]
+
+ style OTEL_SDK fill:#ff9
+```
+
+> [!note] 为什么需要 Collector?
+>
+> Collector 是一个独立的服务进程,承担三个职责:
+> 1. **接收**:从应用的 SDK 接收数据
+> 2. **处理**:过滤、采样、富化(添加额外标签)
+> 3. **导出**:将数据转发到不同的后端存储
+>
+> 这样你的应用只关心"发送数据",Collector 负责"怎么存、存到哪里"。两者解耦。
+
+### Go 中接入 OpenTelemetry
+
+```go
+// 1. 初始化 TracerProvider(Tracing)
+provider := sdktrace.NewTracerProvider(
+ sdktrace.WithBatcherExporter(exporter), // 异步批量上报,不阻塞业务
+)
+defer provider.Shutdown(context.Background())
+
+tracer := provider.Tracer("order-service")
+
+// 2. 在 handler 中创建 span
+func handleOrder(w http.ResponseWriter, r *http.Request) {
+ ctx, span := tracer.Start(r.Context(), "handleOrder")
+ defer span.End()
+
+ // 设置丰富的 Span 属性(供查询和过滤)
+ span.SetAttributes(
+ attribute.String("http.method", r.Method),
+ attribute.Int("http.status_code", 200),
+ attribute.String("user.id", userID),
+ )
+
+ // 调用下游服务(trace context 自动传播)
+ resp, err := callPayment(ctx)
+ if err != nil {
+ span.RecordError(err)
+ span.SetStatus(codes.Error, err.Error())
+ return
+ }
+}
+```
+
+> [!tip] 渐进式落地路线
+>
+> 1. **第一步**:先接 Tracing(价值最大、投入最小),覆盖核心链路
+> 2. **第二步**:加 Metrics(Prometheus + Grafana),建立基础监控
+> 3. **第三步**:集中 Logging(Loki / ELK),与 trace_id 关联
+> 4. **第四步**:全量上 OTel Collector,统一管理三件套
+>
+> 不要试图一步到位——Tracing 的 ROI 最高,先从这里开始。
+
+## Dashboard 与 On-Call 实践
+
+收集了这么多数据,下一步是怎么用。
+
+### Service Health Dashboard
+
+一个优秀的 Dashboard 应该让任何团队成员在 **30 秒内**了解服务的整体状态:
+
+```mermaid
+flowchart TB
+ subgraph DASH["Service Health Dashboard"]
+ ROW1["🔴 可用性 & 错误率 — 第一眼判断"]
+ ROW2["🟡 性能指标 — P50/P90/P99 趋势"]
+ ROW3["🔵 基础设施 — CPU/内存/连接数"]
+ ROW4["⚫ 业务指标 — 订单量/支付成功率"]
+ end
+
+ ROW1 --> JUDGE{是否异常?}
+ ROW2 --> JUDGE
+ ROW3 --> JUDGE
+ ROW4 --> JUDGE
+
+ JUDGE --"否" --> NORMAL["一切正常 ✓"]
+ JUDGE --"是" --> ALERT["触发告警 → On-Call"]
+```
+
+### Runbook:告警处置手册
+
+告警来了之后该怎么办?答案不应存在某个资深工程师的脑子里。
+
+> [!summary] Runbook 模板
+>
+> | 字段 | 内容 |
+> |------|------|
+> | **标题** | `支付服务 P99 延迟 > 2s 持续 5 分钟` |
+> | **影响范围** | 下单接口超时,用户体验受损 |
+> | **检查步骤** | ① 看 Grafana 延迟面板确认峰值时间点 → ② 查同期部署记录 → ③ 检查下游 DB 慢查询 |
+> | **常见原因** | ① 新上线代码有性能 regression → ② DB 连接池耗尽 → ③ 下游超时风暴 |
+> | **快速恢复** | ① 回滚最近一次发布 → ② 扩容支付服务实例 → ③ 开启熔断降低下游压力 |
+> | **彻底解决** | 排查根本原因,补充回归测试,完善容量规划 |
+
+> [!question] 反模式自检
+>
+> 如果你的 On-Call 流程是这样的:收到告警 → 群里问 "有人遇到过吗?" → 等半天有人回复 → SSH 到服务器上翻日志 → ……——这说明你们的可观测性体系还不够成熟。**好的 On-Call 体验 = 清晰的告警 + 完善的 Runbook + 一键处置工具**。
+
+## 关联笔记
+
+- [[02-服务治理]] — 熔断器可以触发告警,与监控系统联动;分布式链路追踪章节也有详细说明
+- [[05-部署运维]] — K8s Liveness/Readiness Probe 是可观测性的基础
+- [[01-基础概念]] — 微服务的整体架构认知
diff --git a/hzh/MS/05-部署运维.md b/hzh/MS/05-部署运维.md
new file mode 100644
index 0000000..1ffdf4e
--- /dev/null
+++ b/hzh/MS/05-部署运维.md
@@ -0,0 +1,397 @@
+---
+tags: [microservice, kubernetes, cicd, container, sre, observability]
+create time: 2026-04-29 12:05
+---
+
+# 部署运维
+
+## 概述
+
+微服务架构下,服务数量从几个增长到几百个。**人工运维完全不可行**。本文涵盖容器化编排、K8s 资源管理、发布策略、弹性伸缩、CI/CD 流水线、日志告警和 SRE 核心概念。
+
+## 容器化与编排
+
+### Docker 容器 — 标准交付单元
+
+```dockerfile
+# Go 多阶段构建示例
+FROM golang:1.22 AS builder
+WORKDIR /app
+COPY . .
+RUN CGO_ENABLED=0 GOOS=linux go build -o server .
+
+FROM alpine:latest
+COPY --from=builder /app/server /server
+EXPOSE 8080
+CMD ["/server"]
+```
+
+> [!tip] 关键实践
+> - **多阶段构建**:镜像只包含运行时产物,体积极大缩小
+> - **非 root 用户运行**:安全最佳实践
+> - `.dockerignore`:排除不必要的文件(git、vendor、测试文件)
+
+### Kubernetes 核心概念
+
+```mermaid
+graph TB
+ subgraph CLUSTER["K8s Cluster"]
+ master["Master Node
API Server / Scheduler / ETCD"]
+
+ subgraph NODES["Worker Nodes"]
+ N1[Node A]
+ N2[Node B]
+ end
+
+ master --> N1
+ master --> N2
+
+ subgraph PODS1["Deployment: order-service"]
+ P1[Pod 1]
+ P2[Pod 2]
+ P3[Pod 3]
+ end
+
+ subgraph PODS2["Deployment: payment-service"]
+ Q1[Pod 1]
+ Q2[Pod 2]
+ end
+
+ N1 --> P1 & Q1
+ N2 --> P2 & P3 & Q2
+ end
+
+ Svc["Service: order-service
Load Balancer → Pods"]
+ Svc --> P1 & P2 & P3
+```
+
+| K8s 对象 | 作用 |
+|---------|------|
+| Pod | 最小部署单元,一个或多个容器 |
+| Deployment | 管理 Pod 的声明式更新和扩缩容 |
+| Service | 稳定的网络入口,实现负载均衡 |
+| Ingress | HTTP/HTTPS 路由规则(外部访问入口) |
+| ConfigMap / Secret | 配置注入,与代码分离 |
+| HPA (Horizontal Pod Autoscaler) | 根据 CPU/自定义指标自动扩缩 |
+
+### 资源管理与健康检查
+
+K8s 调度 Pod 的核心依据是 **资源请求 (Requests)**,而决定 pod 生死的是 **探针 (Probes)**。这两个概念配合使用才能保障服务稳定运行。
+
+```yaml
+# Deployment 核心片段:资源配置 + 探针
+spec:
+ replicas: 3
+ template:
+ spec:
+ containers:
+ - name: order-service
+ image: registry.example.com/order:v1.2.3
+ resources:
+ requests: # 调度依据:K8s 保证至少有这些资源
+ cpu: "250m" # 0.25 核
+ memory: "256Mi" # 256 MB
+ limits: # 硬上限:超过则 OOMKill / CPU Throttle
+ cpu: "500m"
+ memory: "512Mi"
+ livenessProbe: # 活体检测:死了就重启
+ httpGet:
+ path: /healthz
+ port: 8080
+ initialDelaySeconds: 15
+ periodSeconds: 10
+ readinessProbe: # 就绪检测:未就绪就不进负载均衡
+ httpGet:
+ path: /ready
+ port: 8080
+ initialDelaySeconds: 5
+ periodSeconds: 5
+ startupProbe: # 启动检测:慢启动服务友好
+ httpGet:
+ path: /healthz
+ port: 8080
+ failureThreshold: 30
+ periodSeconds: 10
+```
+
+> [!tip] Probe 选择指南
+>
+> | 探针类型 | 触发条件 | 后果 | 适用场景 |
+> |---------|---------|------|---------|
+> | **Liveness** | `/healthz` 返回非 2xx | K8s **重启容器** | 死锁、无法恢复的崩溃 |
+> | **Readiness** | `/ready` 返回非 2xx | **摘除 Service 流量** | 依赖 DB 连接池未就绪、热加载进行中 |
+> | **Startup** | 首次成功前一直失败 | **不重启,只等待** | 大模型初始化、JVM cold start 等慢启动场景 |
+
+> [!question] 思考
+> 如果一个服务的 `/healthz` 因为数据库连接超时而持续返回 503,K8s 会怎么做?
+
+**答案**:Liveness Probe 会认为容器挂了并反复重启它 — 这就是经典的 **CrashLoopBackOff**。正确做法是让 `/healthz` 做降级判断(DB 不可用时返回 200 + 标注降级),用 `/ready` 来摘除流量。Liveness 应该只对"进程已死"的情况敏感,对"性能下降"保持宽容。
+
+---
+
+## 发布策略
+
+微服务需要独立发布,如何在不停服的情况下完成更新?
+
+### 蓝绿发布
+
+```mermaid
+stateDiagram-v2
+ [*] --> Blue: 初始状态
+ Blue --> DeployGreen: 部署新版本到 Green
+ DeployGreen --> TestGreen: 灰度验证
+ TestGreen --> Switch: 切换流量
+ Switch --> Green: 全部流量到新版
+ Green --> CleanupGreen: 删除旧版 Blue
+```
+
+- 优点:**回滚极速**(切回 Blue 即可),验证充分
+- 缺点:资源翻倍,需要两套环境
+
+### 金丝雀发布 (Canary)
+
+```mermaid
+graph LR
+ A["95% traffic to stable"]
+ B["5% traffic to canary"]
+ C["Metrics normal?"]
+ D["Gradually expand to 20% → 50% → 100%"]
+ E["Metrics abnormal → Auto rollback"]
+
+ A --> B
+ B --> C
+ C -- "yes" --> D
+ C -- "no" --> E
+```
+
+- 优点:资源效率高,风险渐进暴露
+- 缺点:流程复杂,需要完善的监控配合
+
+### 两种策略选择
+
+> [!question] 思考
+> 你的团队应该用蓝绿还是金丝雀?
+
+**答案线索**:看你们的**监控成熟度和发布频率**。低频次(每周/每月)、监控完善时用金丝雀;高频次(每天多次)或想简化流程时用蓝绿。
+
+## 弹性伸缩
+
+```mermaid
+graph TD
+ Metrics["CPU / Memory / Custom QPS"] --> Trigger{Threshold met?}
+ Trigger -- "yes" --> ScaleUp["Scale up new Pod"]
+ Trigger -- "no" --> Keep["Keep current replicas"]
+ ScaleDown{"Load decreased?"} -- "yes" --> ScaleDownPod["Scale down Pod"]
+ ScaleDown -- "no" --> Keep
+```
+
+**注意事项**:
+- **预热时间**:新 Pod 启动后不能立刻算入统计,否则可能反复扩缩
+- **优雅关闭**:HPA 缩容前,Pod 需要先摘除 Service 流量再退出
+- **预留容量**:不要将集群利用率拉到 100%,给突发流量留缓冲
+
+## CI/CD 流水线
+
+```mermaid
+graph LR
+ CODE["代码提交"]
+ TEST["单元测试 + 集成测试"]
+ SCAN["安全扫描 + 代码质量"]
+ BUILD["构建 Docker 镜像"]
+ PUSH["推送镜像仓库"]
+ DEPLOY["CI→Staging → 审批 → Production"]
+
+ CODE --> TEST --> SCAN --> BUILD --> PUSH --> DEPLOY
+```
+
+> [!summary] CI/CD 设计原则
+>
+> - **一次构建,多处部署**:镜像不随环境重新编译,只改 K8s ConfigMap/环境变量
+> - **语义化版本号**:镜像 tag 用 `v1.2.3`,tag 即版本溯源
+> - **自动化测试覆盖率要求**:合并 PR 前必须通过,否则不允许发布
+
+### Pipeline 实战:GitHub Actions
+
+```yaml
+# .github/workflows/deploy.yml
+name: Deploy order-service
+on:
+ push:
+ branches: [main]
+ paths:
+ - "services/order/**" # 仅该目录变更才触发
+
+jobs:
+ build-and-push:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Build & push image
+ run: |
+ docker build -t registry/order:${{ github.sha }} .
+ docker tag registry/order:${{ github.sha }} \
+ registry/order:v$(git describe --tags --abbrev=0)
+ docker push registry/order:${{ github.sha }}
+
+ deploy-staging:
+ needs: build-and-push
+ runs-on: ubuntu-latest
+ environment: staging
+ steps:
+ - name: Apply K8s manifests
+ run: |
+ kubectl set image deployment/order-service \
+ order=registry/order:${{ github.sha }}
+ kubectl rollout status deployment/order-service --timeout=120s
+
+ deploy-production:
+ needs: deploy-staging
+ runs-on: ubuntu-latest
+ environment: production
+ steps:
+ - name: Canary release (10% → 50% → 100%)
+ run: |
+ kubectl patch canary order-service --type merge \
+ -p '{"spec":{"weight":10}}'
+ # ... 等待监控确认,逐步放大流量
+```
+
+> [!tip] Pipeline 设计要点
+>
+> - **路径过滤**:只对相关服务的代码变更触发构建,避免全量重建
+> - **Commit SHA 作为镜像 tag**:保证精确回滚 — `git revert` 之后用同一 SHA 拉取旧镜像
+> - **分阶段部署**:Staging → Production 的审批关卡是最后的防线,不要跳过
+
+## 日志管理与告警
+
+微服务单实例崩溃不可怕 — 可怕的是 **不知道哪一台、为什么挂了**。日志和告警是运维的眼睛。
+
+### 日志架构选型
+
+```mermaid
+graph LR
+ App["业务应用"] -->|stdout/stderr| K8sPod[Pod 容器日志]
+ K8sPod --> Filebeat[采集器: Filebeat / FluentBit]
+ Filebeat --> ES["Elasticsearch / Loki"]
+ ES --> Grafana["Grafana 查询面板"]
+
+ App -->|structured log| sidecar[Sidecar 辅助日志]
+ sidecar --> Filebeat
+```
+
+**业界两种主流方案对比:**
+
+| 维度 | ELK (Elasticsearch) | EFK/Loki |
+|------|---------------------|----------|
+| 存储成本 | 高(全文索引) | 低(索引 label + 对象存储原文) |
+| 查询性能 | 毫秒级全文检索 | 按 label 过滤较快,复杂查询慢 |
+| 运维复杂度 | 高(ES 集群维护) | 低(Loki 无索引) |
+| 适用规模 | 百万条/天以上 | 万 ~ 百万条/天 |
+
+> [!important] 结构化日志
+>
+> 无论选择哪家方案,**日志格式必须是结构化的**(JSON),否则后期处理全是手工活:
+> ```json
+> {
+> "timestamp": "2026-04-29T10:30:00Z",
+> "level": "error",
+> "service": "order-service",
+> "trace_id": "abc-123-def",
+> "msg": "payment gateway timeout",
+> "duration_ms": 5023
+> }
+> ```
+
+### 告警策略设计
+
+> [!question] 思考
+> 如果一个 Pod CPU 使用率从 20% 飙升到 90%,你应该立刻打电话叫值班工程师吗?
+
+不一定。**好的告警应该是" actionable"的** — 能让人立刻采取行动,而不是单纯告诉你"出事了"。
+
+```mermaid
+mindmap
+ root((告警设计原则))
+ 区分等级
+ P0: 立即响应 (页面电话)
+ P1: 当天处理 (IM 消息)
+ P2: 本周修复 (工单)
+ 抑制噪音
+ 相关告警聚合: 一个根因一条告警
+ 静默期: 重启后的自动恢复不打扰
+ 附带上下文
+ 告警信息包含: 什么服务 · 哪个指标 · 当前值 vs 阈值
+```
+
+---
+
+## SRE 核心概念
+
+站点可靠性工程 (SRE) 把运维问题看作**软件工程问题**。它的核心理念是用 SLI/SLO/SLA 来量化服务质量,避免"我觉得系统很慢"这类模糊描述。
+
+### SLI / SLO / SLA
+
+| 术语 | 全称 | 定义 | 谁定义 |
+|------|------|------|--------|
+| **SLI** | Service Level Indicator | **实际度量**:用户请求的成功率是多少? | 观测系统自动产出 |
+| **SLO** | Service Level Objective | **内部目标**:我们承诺达到 99.9% 可用性 | SRE + 研发制定 |
+| **SLA** | Service Level Agreement | **对外合同**:达不到就赔钱 | 法务 + 商务制定 |
+
+> [!tip] 实用比例
+>
+> - **99.9% (三个九)** = 每年约 8.76 小时停机 — 适合大多数后端服务
+> - **99.95%** = 每年约 4.38 小时 — 核心交易链路
+> - **99.99% (四个九)** = 每年约 52 分钟 — 金融级系统
+> - **99%** 意味着每月近 7 小时不可用 — **几乎等于没有可用性目标**
+
+### 错误预算 (Error Budget)
+
+这是 SRE 最核心的机制 — **允许犯错,但设上限**:
+
+```
+错误预算 = 1 - SLO
+
+SLO = 99.9% → 错误预算 = 0.1% → 每月允许 21 分钟停机
+SLO = 99.99% → 错误预算 = 0.01% → 每月仅 4 分钟停机
+```
+
+**基于错误预算的决策逻辑:**
+
+```mermaid
+flowchart TD
+ Budget{"错误预算剩余 > 50%?"}
+
+ Budget -- "是" --> Aggressive["可激进发布:
金丝雀 + 自动扩缩"]
+ Budget -- "< 50%" --> Conservative["保守发布:
蓝绿 + 全量人工审查"]
+ Budget -- "< 10%" --> Freeze["冻结发布:
优先修复稳定性
不允许新功能上线"]
+
+ Aggressive --> Monitor["持续监控指标"]
+ Conservative --> Monitor
+```
+
+> [!summary] SRE 心法
+>
+> 1. **用户视角定义 SLO**:不是"API P99 < 200ms",而是"用户在 3G 网络下打开页面 < 2s"
+> 2. **错误预算用完 = 停止功能开发**:全力修 bug 加稳定性
+> 3. **Blameless Postmortem**:事故复盘不问"谁干的",问"流程哪里可以改进"
+
+---
+
+## 运维 Checklist
+
+每次上线前快速过一遍这个清单,避免低级事故:
+
+| # | 检查项 | 说明 |
+|---|--------|------|
+| 1 | **探针已配置** | liveness/readiness/startup probe 都已设定 |
+| 2 | **资源 limits 已设** | 防止单个 Pod 拖垮整台机器 |
+| 3 | **日志为 JSON 格式** | 可被采集器解析 |
+| 4 | **追踪 ID 透传** | trace_id 在跨服务调用链中不丢失 |
+| 5 | **回滚预案明确** | 知道怎么回到上一版本,且演练过 |
+| 6 | **告警已配置** | 关键指标异常时有人收到通知 |
+| 7 | **数据迁移有回退脚本** | DB schema 变更要兼容新老版本共存 |
+
+## 关联笔记
+
+- [[02-服务治理]] — K8s Service 提供原生的服务发现和负载均衡
+- [[04-可观测性]] — K8s Liveness/Readiness Probe 是可观测性的基础
diff --git a/hzh/MS/README.md b/hzh/MS/README.md
new file mode 100644
index 0000000..156f4b2
--- /dev/null
+++ b/hzh/MS/README.md
@@ -0,0 +1,41 @@
+---
+tags: []
+create time: 2026-04-29 12:00
+---
+
+# 微服务知识索引
+
+## 概述
+
+本目录系统整理微服务架构的核心知识点。围绕 **5 个核心主题**,由浅入深地覆盖从概念理解到工程实践的关键路径。
+
+## 知识体系
+
+```mermaid
+graph LR
+ A["01 基础概念"] --> B["02 服务治理"]
+ B --> C["03 数据一致性"]
+ B --> D["04 可观测性"]
+ C --> E["05 部署运维"]
+ D --> E
+```
+
+| # | 主题 | 核心问题 |
+|---|------|----------|
+| 1 | [[01-基础概念]] | 什么是微服务?单体 vs 微服务的权衡是什么? |
+| 2 | [[02-服务治理]] | 服务如何发现彼此?如何通信?失败怎么处理? |
+| 3 | [[03-数据一致性]] | 每个服务拥有独立数据库,跨服务数据一致性怎么保证? |
+| 4 | [[04-可观测性]] | 请求穿越多个服务后,如何追踪、监控和排查问题? |
+| 5 | [[05-部署运维]] | 上百个服务如何容器化编排、灰度发布、弹性伸缩? |
+
+### 学习建议
+
+> [!tip] 学习路径
+> 按编号顺序阅读。第 1 篇是理论基础,后面 4 篇在实践中高度耦合,可以按需跳读。
+
+> [!question] 带着问题读
+> 每篇文章都设计了启发式问题。先自己想一遍答案,再看文档,效果更好。
+
+### 关联笔记
+
+- [[API 设计原则]] — API Gateway 的设计与 REST/gRPC 选型