cloud-disk

cloud-disk 架构设计文档

1. 系统架构总览

cloud-disk 采用控制面与数据面分离的微服务架构,通过消息队列解耦两层的一致性需求。

                                    ┌──────────────────────────┐
                                    │      Nacos (8848)        │
                                    │  注册中心 + 配置中心       │
                                    └────────────┬─────────────┘
                                                 │
                    ┌────────────────────────────┴─────────────────────────────┐
                    │                    Gateway (38080)                      │
                    │  ┌──────────┐  ┌──────────┐  ┌──────────────────────┐   │
                    │  │ 路由转发  │  │ JWT 鉴权 │  │ Sentinel 流控(Gateway)│   │
                    │  └──────────┘  └──────────┘  └──────────────────────┘   │
                    └────────┬──────────────────────────────────┬──────────────┘
                             │                                  │
              ┌──────────────▼──────────────┐  ┌────────────────▼────────────────┐
              │       object-storage        │  │        object-metadata          │
              │         (38081)             │  │           (38082)               │
              │       数据面 / IO 密集型      │  │         控制面 / CPU 密集型       │
              │                              │  │                                 │
              │  ┌────────────────────┐      │  │  ┌─────────────────────────┐   │
              │  │  ObjectController  │      │  │  │  BucketController       │   │
              │  │  MultipartControl  │      │  │  │  ShareController        │   │
              │  └────────┬───────────┘      │  │  │  RecycleController      │   │
              │           │                  │  │  └─────────────────────────┘   │
              │           ▼                  │  │              │                 │
              │  ┌────────────────────┐      │  │              ▼                 │
              │  │  ObjectService     │      │  │  ┌─────────────────────────┐   │
              │  │  MultipartUploadSvc│      │  │  │  BucketService          │   │
              │  └──┬──────────┬──────┘      │  │  │  ShareService           │   │
              │     │          │             │  │  │  RecycleService         │   │
              │     ▼          ▼             │  │  └──────┬──────────┬───────┘   │
              │  ┌──────┐ ┌────────┐        │  │         │          │           │
              │  │MinIO │ │ Redis  │        │  │         ▼          ▼           │
              │  │数据面│ │秒传/分片│        │  │  ┌────────┐ ┌──────────┐      │
              │  └──┬───┘ └────────┘        │  │  │ MySQL  │ │  Redis   │      │
              │     │                       │  │  │元数据   │ │ 缓存     │      │
              │     ▼                       │  │  └────────┘ └──────────┘      │
              │  ┌──────────────────┐       │  └────────────────────────────────┘
              │  │ StorageBackend   │       │
              │  │ MinIO/Local/OSS  │       │
              │  └──────────────────┘       │
              │         │                   │
              │         ▼                   │
              │  ┌────────────────┐         │
              │  │   RabbitMQ     │─────────│────► ObjectCreatedConsumer
              │  │   (Outbox)     │         │       (写入 object_info)
              │  └────────────────┘         │
              └─────────────────────────────┘

    Sentinel Dashboard (38858)   MySQL master (33306) + slave (33307)   Redis Cluster (37000-37005)
                                   MinIO (39000/39001)/本地/OSS          RabbitMQ (35672/45672)

分层职责

层次 组件 职责
接入层 Spring Cloud Gateway + Admin UI 统一入口、路由、JWT 鉴权、Sentinel 流控、跨域
数据面 object-storage 对象数据 IO(上传/下载),Redis 缓存操作
控制面 object-metadata 元数据管理(索引/回收站/分享/Bucket)
事件总线 RabbitMQ + Outbox Table 异步解耦数据面与控制面
持久层 StorageBackend (MinIO/本地/OSS) + MySQL + Redis 可插拔对象存储、关系元数据、缓存/状态
基础设施 Nacos / Sentinel / XXL-Job 注册配置、流控熔断、分布式定时任务

2. 核心数据流

2.1 分片上传全流程

Client                    Gateway              object-storage              MinIO                  Redis
  │                         │                      │                        │                     │
  │  POST /bucket/key?uploads                      │                        │                     │
  ├────────────────────────►│                      │                        │                     │
  │                         │ 路由至 object-storage │                        │                     │
  │                         ├─────────────────────►│                        │                     │
  │                         │                      │ generateUuidV7()       │                     │
  │                         │                      │ 初始化 Redis           │                     │
  │                         │                      │ parts:{uploadId}       │                     │
  │                         │                      ├──────────────────────────────────────────────►│
  │                         │                      │                        │                     │
  │  ← 200 { uploadId }     │                      │                        │                     │
  │◄────────────────────────┤◄─────────────────────┤                        │                     │
  │                         │                      │                        │                     │
  │  PUT /bucket/key?partNumber=1&uploadId=xxx     │                        │                     │
  ├────────────────────────►│                      │                        │                     │
  │                         ├─────────────────────►│                        │                     │
  │                         │                      │  分片数据上传            │                     │
  │                         │                      ├───────────────────────►│                     │
  │                         │                      │                        │  part-0001 写入       │
  │                         │                      │◄───────────────────────┤                     │
  │                         │                      │ 记录 etag+size          │                     │
  │                         │                      ├──────────────────────────────────────────────►│
  │  ← 200 { etag }        │                      │                        │                     │
  │◄────────────────────────┤◄─────────────────────┤                        │                     │
  │                         │                      │                        │                     │
  │  (重复 1..n 个分片)     │                      │                        │                     │
  │                         │                      │                        │                     │
  │  POST /bucket/key?uploadId=xxx                 │                        │                     │
  │  Body: <CompleteMultipartUpload>               │                        │                     │
  ├────────────────────────►│                      │                        │                     │
  │                         ├─────────────────────►│                        │                     │
  │                         │                      │ 校验 parts 完整性       │                     │
  │                         │                      │ 校验 etag 一致性        │                     │
  │                         │                      │ 校验 partNumber 升序    │                     │
  │                         │                      │                        │                     │
  │                         │                      │  composeObject 合并     │                     │
  │                         │                      ├───────────────────────►│                     │
  │                         │                      │◄───────────────────────┤                     │
  │                         │                      │                        │                     │
  │                         │                      │ 清理临时分片            │                     │
  │                         │                      ├───────────────────────►│  removeObjects       │
  │                         │                      │                        │                     │
  │                         │                      │ 删除 Redis 状态        │                     │
  │                         │                      ├──────────────────────────────────────────────►│
  │                         │                      │                        │                     │
  │                         │                      │ 写入 Outbox 表         │                     │
  │                         │                      │ 发送 RabbitMQ          │                     │
  │  ← 200 { etag }        │                      │                        │                     │
  │◄────────────────────────┤◄─────────────────────┤                        │                     │

2.2 秒传(Instant Upload)流程

秒传利用 upload:hash:{md5}:{size} Redis Key 实现 O(1) 文件去重判定,避免重复传输相同内容。

Client                    Gateway              object-storage                  Redis
  │                         │                      │                            │
  │  PUT /bucket/key        │                      │                            │
  │  x-cloud-disk-md5: X   │                      │                            │
  ├────────────────────────►│                      │                            │
  │                         ├─────────────────────►│                            │
  │                         │                      │                            │
  │                         │                      │ Redis GET                 │
  │                         │                      │ upload:hash:{md5}:{size}   │
  │                         │                      ├───────────────────────────►│
  │                         │                      │◄───────────────────────────┤
  │                         │                      │                            │
  │                         │                ┌─────┴─────┐                      │
  │                         │                │  Key 存在? │                      │
  │                         │                └─────┬─────┘                      │
  │                         │                ┌─────┴─────┐                      │
  │                         │                │  是 (CACHE                 │
  │                         │                │  HIT)    │                      │
  │                         │                └─────┬─────┘                      │
  │                         │                      │                            │
  │                         │                ┌─────▼─────┐                     │
  │                         │                │ MinIO 校验 │                     │
  │                         │                │ statObject │                     │
  │                         │                └─────┬─────┘                     │
  │                         │                ┌─────┴─────┐                     │
  │                         │                │ 文件存在?  │                     │
  │                         │                └─────┬─────┘                     │
  │                         │                ┌─────┴─────┐    ┌─────┐         │
  │                         │                │  是        │    │ 否  │         │
  │                         │                │            │    │     │         │
  │                         │                │ 返回秒传   │    │清除 │         │
  │                         │                │ 响应       │    │缓存 │         │
  │                         │                └─────┬─────┘    └──┬──┘         │
  │                         │                      │              │            │
  │                         │                ┌─────▼─────┐       │            │
  │                         │                │ 返回       │       │            │
  │  ← 200 instant=true    │                │ instant   │       │            │
  │◄────────────────────────┤◄───────────────┤ = true     │       │            │
  │                         │                └───────────┘       │            │
  │                         │                      │              │            │
  │                         │                      │   false      ▼            │
  │                         │                      ├─── 正常上传到 MinIO ──────┤
  │                         │                      │                            │
  │                         │                      │ SET 到 Redis (365天过期)   │
  │                         │                      ├───────────────────────────►│
  │                         │                      │                            │
  │                         │                      │ 写入 Outbox 表             │
  │                         │                      │ 发 RabbitMQ 事件           │
  │                         │                      │                            │
  │  ← 200 instant=false   │                      │                            │
  │◄────────────────────────┤◄─────────────────────┤                            │

关键设计点

2.3 Outbox 模式:ObjectCreated 事件流

数据面写入对象后需要同步更新控制面的 object_info 索引表。使用 Outbox 模式保证最终一致性。

                  object-storage                          RabbitMQ               object-metadata

  PUT Object           │                                      │                       │
      │                │                                      │                       │
      ▼                │                                      │                       │
  MinIO 写入           │                                      │                       │
      │                │                                      │                       │
      ▼                │                                      │                       │
  写入 Outbox 表       │                                      │                       │
  (status=PENDING)     │                                      │                       │
      │                │                                      │                       │
      ▼                │                                      │                       │
  RabbitMQ 发送        │                                      │                       │
  object.created       ├─────────────────────────────────────►│                       │
      │                │                                      │                       │
      ▼                │                                      │                       │
  更新 Outbox 表       │                                      │  @RabbitListener       │
  (status=SUCCESS)     │                                      │                       │
                        │                                      │  ┌───────────────────▼──┐
                        │                                      │  │ 查询 Bucket         │
                        │                                      │  │ UPSERT object_info  │
                        │                                      │  │ 更新 Bucket 使用量   │
                        │                                      │  │ 清除 Redis 缓存      │
                        │                                      │  └───────────────────┬──┘
                        │                                      │                       │
                        │                                      │                       ▼
                        │                                      │                  MySQL object_info

故障恢复


3. 数据库设计

所有表使用 utf8mb4 字符集,时间字段统一使用 UTC_TIMESTAMP

3.1 bucket_info

列名 类型 说明
id BIGINT (AUTO) 主键
bucket_name VARCHAR(255) Bucket 名称,全局唯一
region VARCHAR(64) 区域,默认 us-east-1
storage_type ENUM(‘MINIO’,’LOCAL’,’OSS’) 存储后端类型,默认 MINIO
owner_user_id BIGINT 所属用户 ID
status ENUM(‘ACTIVE’,’DELETED’) 软删除标记
quota_bytes BIGINT 配额上限,0 = 不限制
used_bytes BIGINT 已用空间(由 ObjectCreated 事件更新)
created_at DATETIME 创建时间
updated_at DATETIME 更新时间

索引:idx_owner(owner_user_id)

3.2 object_info

列名 类型 说明
id BIGINT (AUTO) 主键
bucket_id BIGINT 所属 Bucket ID
object_key VARCHAR(1024) 对象键
etag VARCHAR(64) MinIO 返回的 ETag
size BIGINT 文件大小(字节)
content_type VARCHAR(255) MIME 类型
md5_hash VARCHAR(64) 文件 MD5,用于秒传判定
upload_user_id BIGINT 上传者 ID
status ENUM(‘NORMAL’,’RECYCLED’,’DELETED’) 对象生命周期状态
user_metadata JSON 用户自定义元数据
created_at DATETIME 创建时间
updated_at DATETIME 更新时间

唯一索引:uk_bucket_object(bucket_id, object_key)
索引:idx_status(status), idx_bucket_id(bucket_id)

3.3 multipart_upload

此表在 sql/init.sql 中定义,供将来扩展使用。当前运行时,分片上传状态完全由 Redis parts:{uploadId} (Hash) 维护,未读写此表。

列名 类型 说明
id BIGINT (AUTO) 主键
upload_id VARCHAR(64) UUID v7,全局唯一
bucket_id BIGINT 所属 Bucket
object_key VARCHAR(1024) 对象键
initiated_by BIGINT 发起人 ID
initiated_at DATETIME 发起时间
expires_at DATETIME 过期时间(24 小时后)
status ENUM(‘IN_PROGRESS’,’COMPLETED’,’ABORTED’) 上传状态

索引:idx_bucket_object(bucket_id, object_key)

3.4 outbox_table

列名 类型 说明
id BIGINT (AUTO) 主键
message_type VARCHAR(64) 消息类型,如 OBJECT_CREATED
payload JSON 消息载荷
status ENUM(‘PENDING’,’SUCCESS’,’FAILED’) 处理状态
retry_count INT 重试次数
created_at DATETIME 创建时间
next_retry_at DATETIME (NULLABLE) 下次重试时间

索引:idx_status_created(status, created_at) —— 用于 XXL-Job 定时扫描待重试消息。

列名 类型 说明
id BIGINT (AUTO) 主键
token VARCHAR(32) 短码,全局唯一
bucket_id BIGINT 所属 Bucket
user_id BIGINT 分享创建者 ID
object_keys JSON 分享的对象键列表
password VARCHAR(255) (NULLABLE) bcrypt 加密密码,空 = 公开
expire_at DATETIME 过期时间
max_downloads INT 下载次数上限,0 = 不限制
current_downloads INT 当前下载次数
status ENUM(‘ACTIVE’,’EXPIRED’,’REVOKED’) 链接状态
created_at DATETIME 创建时间

索引:idx_token(token), idx_status(status), idx_user_id(user_id)


4. Redis 数据结构

4.1 秒传缓存

Key:   upload:hash:{md5}:{size}
Type:  STRING
Value: 文件 MinIO 中的 objectKey
TTL:   365 天

示例:
> SET upload:hash:d41d8cd98f00b204e9800998ecf8427e:1024 "/photos/sunset.jpg" EX 31536000

上传文件时携带 x-cloud-disk-md5 请求头,服务端计算 Key 后执行 GET:

4.2 分片上传状态

Key:   parts:{uploadId}
Type:  HASH
Value: { partNumber: "etag|size" }
TTL:   24 小时(与分片上传过期时间一致)

示例:
> HSET parts:0193f1a2-3b4c-5d6e-7f8a-9b0c1d2e3f4f 1 "abc123|5242880"
> HSET parts:0193f1a2-3b4c-5d6e-7f8a-9b0c1d2e3f4f 2 "def456|5242880"
> EXPIRE parts:0193f1a2-3b4c-5d6e-7f8a-9b0c1d2e3f4f 86400

4.3 Bucket 存储类型缓存

Key:   bucket:storage:{bucketName}
Type:  STRING
Value: 存储类型(MINIO / LOCAL / OSS)
TTL:   1 小时

示例:
> SET bucket:storage:my-bucket "LOCAL" EX 3600

StorageBackendFactory 使用此缓存避免每次请求都查 MySQL。缓存未命中时查 bucket_info.storage_type 字段,写入 Redis 后返回对应后端实现。

此 Key 同样通过 HotKeyUtil 进行 3-shard hash-tag 打散(与秒传缓存相同的热点打散策略),避免高频 Bucket 的存储类型查询成为热点。

4.4 Bucket 列表缓存

Key:   buckets:list:{userId}
Type:  STRING (序列化 JSON)
Value: BucketInfo 列表
TTL:   动态(创建/删除 Bucket 时主动失效)

4.5 对象元数据缓存

Key:   object:info:{bucketName}:{objectKey}
Type:  STRING (序列化 JSON)
Value: ObjectInfo
TTL:   动态(ObjectCreated 事件消费后清除)

当前仅用于缓存失效(delete on write),写入时读取数据库最新数据。可扩展为读缓存以提升 ListObjects 性能。


5. 关键设计决策

5.1 为什么使用标准 S3 协议而非 S3 Express

5.2 为什么使用 UUID v7

// 简化版 UUID v7 实现:timestamp (48 bits) + random (剩余)
private String generateUuidV7() {
    long timestamp = Instant.now().toEpochMilli();
    long random = new SecureRandom().nextLong();
    return String.format("%08x-%s-%04x-%04x-%012x",
            (int)(timestamp >> 16),
            String.format("%04x", (int)(timestamp & 0xFFFF)),
            (int)((random >> 48) & 0xFFFF),
            (int)((random >> 32) & 0xFFFF),
            random & 0xFFFFFFFFFFFFL);
}

5.3 为什么使用软删除

对象状态迁移:NORMAL -> RECYCLED (30天) -> DELETED

5.4 为什么使用 Outbox 模式

数据面(object-storage)在写入 MinIO 后需要通知控制面(object-metadata)更新索引。直接双写或 RPC 调用存在以下问题:

方案 问题
直接双写 MinIO 写成功 + MySQL 写失败 → 数据不一致
同步 RPC 增加上传链路延迟,控制面不可用则上传失败
普通 MQ 发送前宕机 → 消息丢失

Outbox 模式方案:

  1. MinIO 写入成功后,在同一本地事务中写入 outbox_table(status=PENDING)。
  2. 异步发送 RabbitMQ,发送成功后更新 status=SUCCESS。
  3. 若 MQ 不可用,OutboxRetryService 通过 XXL-Job 定时扫描 PENDING 记录并重试(outboxRetryHandler,30s 间隔),另有定期清理死信任务(outboxDeadCleanupHandler,1h 间隔)。
  4. 消费者实现幂等,消息重复送达安全。

这确保了 至少一次投递 语义,且不增加上传主路径延迟。

5.5 Sentinel 流控规则

Gateway 层配置了三组流控规则:

规则 ID QPS 上限 场景
object-storage-put 200/s 上传/秒传
object-storage-get 100/s 下载
object-storage-list 50/s 列举对象

下载和列举的流控阈值较低,保护后端 MinIO 和 MySQL 不被打满。

5.6 虚拟线程

spring:
  threads:
    virtual:
      enabled: true

对象存储服务是典型的 IO 密集型应用(MinIO 网络 IO、Redis 网络 IO、MySQL JDBC IO)。Java 21 虚拟线程将 IO 阻塞从操作系统线程模型剥离,在相同内存下支撑更高并发,避免传统线程池的线程耗尽问题。

5.7 Gateway 全链路非阻塞

Gateway 基于 Spring Cloud Gateway (Reactor Netty) 构建,整个请求链路必须保持非阻塞才能发挥 Netty event loop 的并发能力。一个关键的阻塞点是用 Redisson 检查 JWT 黑名单:

// ❌ 阻塞:在 Netty event loop 线程上执行同步 Redis 查询
RBucket<String> bucket = redissonClient.getBucket(jwtBlacklistKey);
if (bucket.isExists()) { ... }  // 阻塞线程!

// ✅ 非阻塞:使用 RedissonReactiveClient 融入响应式链
return redissonClient.getBucket(jwtBlacklistKey)
    .isExists()                     // 返回 Mono<Boolean>
    .flatMap(exists -> exists
        ? unauthorized(exchange)
        : chain.filter(exchange));

问题表现redissonClient.getBucket().isExists() 是一个同步阻塞调用。当 2,000 并发请求同时到达时,16 个 Netty worker 线程全部被 Redis 查询阻塞 → event loop 永久死锁 → Gateway 不再接受任何新连接 → 整个系统不可用。

解决方案:注入 RedissonReactiveClient(由 RedissonClusterConfig 通过 redissonClient.reactive() 创建,复用同一连接池),所有 Redis 操作返回 Mono<T>,融入 Gateway 的响应式管道。

配合优化: | 配置项 | 值 | 说明 | |——–|—–|——| | spring.cloud.gateway.httpclient.pool.type | elastic | 按需扩展连接池 | | spring.cloud.gateway.httpclient.pool.max-connections | 4000 | 容纳 2000 并发 + 余量 | | spring.cloud.gateway.httpclient.pool.max-idle-time | 15s | 尖峰后快速回收空闲连接 | | reactor.netty.ioWorkerCount | 16 | 匹配并发连接数 | | ChannelOption.SO_BACKLOG | 4096 | TCP accept 队列,应对突发连接 | | JVM Heap | -Xms256m -Xmx384m | 容纳 2000 连接对象 + Netty 直接内存 | | Container | 768M | JVM 堆 + Netty 堆外内存 + OS 开销 |

基准测试验证:尖峰测试 10→2000→10 线程,0 错误 100% 成功率,恢复后 P50=3ms。

5.8 Pre-signed URL 双客户端设计

预签名 URL 的核心挑战在于 Docker 环境中 MinIO 内部地址与客户端可访问地址不一致

S3 V4 签名绑定 Host 头,若用内部地址生成 URL 再替换 host,则签名校验失败。

方案:创建两个 MinioClient Bean:

// 日常 CRUD — 内部端点(minio:9000)
@Primary
public MinioClient minioClient() { ... }

// 预签名 URL — 公开端点(host.docker.internal:39000)
@Qualifier("presignMinioClient")
public MinioClient presignMinioClient() { ... }

presignMinioClient 配置为公开端点 minio.public-endpoint,生成的 URL 中 host 与客户端请求 host 一致,签名校验通过。Docker Compose 中通过 extra_hosts 确保 host.docker.internal 在容器内可解析。

MinioStorageBackend 注入两个 client,presignGetObject/presignPutObject 使用 presignMinioClient,其余方法使用 minioClient


6. 部署方案

6.1 Docker Compose 服务

服务 镜像 内存限制 CPU / JVM 参数
mysql mysql:8.0.34 512M -
mysql-slave mysql:8.0.34 512M -
redis-7000~7005 redis:7-alpine (6 节点) 192M ×6 -
redis-cluster-init redis:7-alpine - 一次性初始化容器
rabbitmq rabbitmq:3.13.7-management-alpine 320M -
minio minio/minio:RELEASE.2025-04-22T22-12-26Z 320M -
nacos nacos/nacos-server:v2.4.3 448M JVM: 128m/192m/64m (Xms/Xmx/Xmn)
sentinel bladex/sentinel-dashboard:1.8.7 256M Java: -Xms64m -Xmx128m
xxl-job xuxueli/xxl-job-admin:2.4.2 320M Java: -Xms128m -Xmx192m
gateway 本地构建 (Dockerfile) 768M JVM: -Xms256m -Xmx384m, Netty workers=16, TCP backlog=4096
object-storage 本地构建 (Dockerfile) 512M JVM: -Xms128m -Xmx256m
object-metadata 本地构建 (Dockerfile) 512M JVM: -Xms128m -Xmx256m

6.2 Dockerfile 构建

FROM eclipse-temurin:21-jre-alpine AS builder
RUN addgroup -S clouddisk && adduser -S clouddisk -G clouddisk

FROM builder
ARG MODULE
ARG JVM_OPTS="-Xms128m -Xmx256m"

WORKDIR /app
COPY ${MODULE}/target/*.jar app.jar

ENV JVM_OPTS=${JVM_OPTS}
USER clouddisk

EXPOSE 8080
ENTRYPOINT exec java ${JVM_OPTS} --enable-preview -jar app.jar

6.3 启动顺序

MySQL (master + slave, healthy) ──→ MySQL replication init (一次性)
                                ──→ XXL-Job Admin
Nacos (healthy) ─────────────────→ gateway, object-storage, object-metadata
Redis 6节点 (healthy) ──→ Redis Cluster init (exit 0) ──→ gateway, object-storage, object-metadata

业务服务只有在 Nacos healthyRedis Cluster init 成功退出 后才启动。depends_on 配合 healthcheck 确保服务间依赖链正确。所有业务服务通过 Nacos 服务发现互相定位。

注意docker compose down 会删除 Docker 网络,重新 up 时网络子网可能变化, 导致 Redis Cluster 持久化 volumes 中存储的节点 IP 失效。完整 down/up 前必须删除所有 Redis data volumes。


7. 面试问题映射

面试考点 cloud-disk 对应实现
微服务架构设计 Gateway + 数据面/控制面分离,Nacos 服务发现
响应式编程 / 非阻塞 Gateway AuthFilter 使用 RedissonReactiveClient,全链路非阻塞
分布式事务 Outbox 模式 + 定时重试,最终一致性
大文件上传 分片上传 Init/Part/Complete/Abort
策略模式 / 多后端 StorageBackend 接口,Factory + Redis 缓存路由
缓存设计 Redis 秒传缓存、分片状态、列表缓存、存储类型缓存
Redis Cluster 热点 HotKeyUtil 3-shard hash-tag 打散,偏差 <20%
数据库索引优化 UUID v7 时间有序,复合唯一索引
软删除与回收站 状态机 NORMAL → RECYCLED → DELETED
限流熔断 Sentinel Gateway 流控规则
Java 21 新特性 Virtual Threads, Pattern Matching
消息队列 RabbitMQ Topic Exchange, 消费者幂等
容器化部署 Docker Compose, Healthcheck, 资源限制
性能调优 Netty event loop 治理, JVM 堆外内存, TCP backlog, 连接池弹性伸缩