vault backup: 2026-06-03 10:30:42
This commit is contained in:
+65
-63
@@ -1,22 +1,33 @@
|
||||
# 02 - 协程池 (Worker Pool)
|
||||
---
|
||||
tags: [concurrency, goroutine-pool, backpressure, go, worker-pattern, rate-limiting]
|
||||
create time: 2026-06-03 10:05
|
||||
---
|
||||
|
||||
# 02. 协程池 (Worker Pool)
|
||||
|
||||
## 概述
|
||||
|
||||
有界并发协程池,以 `NumCPU*4` 个 Worker 并行处理任务,配合 per-user 限流和背压保护,支持优雅关闭。
|
||||
|
||||
> **一句话概括**:有界并发协程池,`NumCPU*4` workers,per-user 限流,背压保护,优雅关闭。
|
||||
|
||||
## 工作流
|
||||
## 正文
|
||||
|
||||
### 工作流
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph Submit["Submit() 入口"]
|
||||
subgraph Submit["Submit入口"]
|
||||
CHECK_CLOSED{"池已关闭?"}
|
||||
CHECK_USER{"per-user 限流<br/>active >= max?"}
|
||||
CHECK_FULL{"channel 满?"}
|
||||
CHECK_USER{"per-user限流<br/>active >= max?"}
|
||||
CHECK_FULL{"channel满?"}
|
||||
end
|
||||
|
||||
subgraph Channel["有界任务队列"]
|
||||
TASK_CHAN["chan Task<br/>capacity = queueSize"]
|
||||
end
|
||||
|
||||
subgraph Workers["Worker 协程"]
|
||||
subgraph Workers["Worker协程"]
|
||||
W1["Worker 0"]
|
||||
W2["Worker 1"]
|
||||
W3["Worker ..."]
|
||||
@@ -25,7 +36,7 @@ graph TB
|
||||
|
||||
subgraph Execute["任务执行"]
|
||||
TIMEOUT["context.WithTimeout<br/>10 min"]
|
||||
FN["task.Fn(ctx)"]
|
||||
FN["task.Fnctx"]
|
||||
RELEASE["释放用户槽位"]
|
||||
end
|
||||
|
||||
@@ -33,13 +44,13 @@ graph TB
|
||||
CHECK_CLOSED -->|否| CHECK_USER
|
||||
CHECK_USER -->|"ErrUserLimitReached"| REJECT
|
||||
CHECK_USER -->|通过| CHECK_FULL
|
||||
CHECK_FULL -->|"ErrPoolFull (HTTP 503)"| REJECT
|
||||
CHECK_FULL -->|"ErrPoolFull HTTP 503"| REJECT
|
||||
CHECK_FULL -->|通过| TASK_CHAN
|
||||
TASK_CHAN --> W1 & W2 & W3 & WN
|
||||
W1 & W2 & W3 & WN --> TIMEOUT --> FN --> RELEASE
|
||||
```
|
||||
|
||||
## Pool 结构体
|
||||
### Pool 结构体
|
||||
|
||||
```go
|
||||
type Pool struct {
|
||||
@@ -70,7 +81,7 @@ type Pool struct {
|
||||
| `taskQueue` | `chan Task` | buffered channel | 有界队列,固定容量 |
|
||||
| `userActive` | `map[string]int` | — | 记录每用户活跃任务数 |
|
||||
|
||||
## Functional Options 模式
|
||||
### Functional Options 模式
|
||||
|
||||
协程池采用 Functional Options 模式进行配置,开箱即用、可选覆盖:
|
||||
|
||||
@@ -88,9 +99,11 @@ pool := workerpool.New(
|
||||
| `WithQueueSize(n)` | `100` | `n < 1` 时强制为 1 | 有界缓冲,满时触发背压 |
|
||||
| `WithMaxPerUser(n)` | `2` | `n < 1` 时置 0(不限制) | 防止单用户占满池 |
|
||||
|
||||
> :bulb: **为什么选择 Functional Options?** 可选参数天然为零值时保持默认,新增配置项无需修改 `New()` 签名,调用方按需指定。
|
||||
> [!tip] 为什么选择 Functional Options?
|
||||
>
|
||||
> 可选参数天然为零值时保持默认,新增配置项无需修改 `New()` 签名,调用方按需指定。
|
||||
|
||||
## 有界并发
|
||||
### 有界并发
|
||||
|
||||
协程池的核心是 `make(chan Task, queueSize)` 创建的 **有界缓冲 channel**。
|
||||
|
||||
@@ -112,7 +125,7 @@ default: // 队列满,背压
|
||||
}
|
||||
```
|
||||
|
||||
## Per-user 限流
|
||||
### Per-user 限流
|
||||
|
||||
每个用户同时执行的任务数受到 `maxPerUser` 限制,防止单用户占满整个池。
|
||||
|
||||
@@ -134,55 +147,50 @@ Submit() 调用流程:
|
||||
| Execute | — | Worker 从 channel 取出后开始执行 |
|
||||
| Complete | `defer userActive[userID]--` | 任务完成或失败时释放 |
|
||||
|
||||
> :warning: **槽位预留时机**:在 `Submit()` 而非 `worker()` 中预留,确保 channel 满时不会出现"槽位已分配但任务未入队"的不一致状态。
|
||||
> [!warning] 槽位预留时机
|
||||
>
|
||||
> 在 `Submit()` 而非 `worker()` 中预留,确保 channel 满时不会出现"槽位已分配但任务未入队"的不一致状态。
|
||||
|
||||
## 背压保护
|
||||
### 背压保护
|
||||
|
||||
当任务队列已满时,协程池通过 `select + default` 实现非阻塞拒绝:
|
||||
|
||||
```
|
||||
队列满(channel 已达 capacity)
|
||||
│
|
||||
▼
|
||||
select 进入 default 分支
|
||||
│
|
||||
├── 回滚用户槽位(如果有)
|
||||
├── 原子递增 RejectedTasks
|
||||
└── 返回 ErrPoolFull
|
||||
│
|
||||
▼
|
||||
Handler 层映射为 HTTP 503 Service Unavailable
|
||||
响应体:"系统繁忙,请稍后重试"
|
||||
```mermaid
|
||||
graph TB
|
||||
FULL["队列满channel已达capacity"] --> SELECT["select进入default分支"]
|
||||
SELECT --> ROLLBACK["回滚用户槽位如果有"]
|
||||
SELECT --> INC["原子递增RejectedTasks"]
|
||||
SELECT --> RETURN["返回ErrPoolFull"]
|
||||
RETURN --> HTTP503["Handler层映射为HTTP 503<br/>响应体系统繁忙请稍后重试"]
|
||||
```
|
||||
|
||||
**背压 vs 阻塞**:
|
||||
|
||||
| 策略 | 行为 | 适用场景 |
|
||||
|------|------|----------|
|
||||
| 非阻塞拒绝(Gen2D) | 立即返回错误 | 用户交互型 API,快速失败 |
|
||||
| 非阻塞拒绝Gen2D | 立即返回错误 | 用户交互型 API,快速失败 |
|
||||
| 阻塞等待 | 阻塞直到有空位 | 批处理系统,不能丢任务 |
|
||||
|
||||
Gen2D 选择非阻塞拒绝,因为用户期望快速得到反馈,而非无限等待。
|
||||
|
||||
## 优雅关闭
|
||||
> [!tip] 设计权衡
|
||||
>
|
||||
> 选择「非阻塞拒绝」意味着可能丢失用户的提交意图。但在 AI 生成场景中,用户可以 **重新点击提交按钮**——这种短暂的操作成本远低于服务器因连接堆积导致的雪崩风险。这就是经典的 **「可用性 > 一致性」** 抉择。
|
||||
|
||||
### 优雅关闭
|
||||
|
||||
协程池支持优雅关闭,确保正在执行的任务有时间完成:
|
||||
|
||||
```
|
||||
收到 SIGINT / SIGTERM
|
||||
│
|
||||
▼
|
||||
consumeCancel() ← 停止消费者,不再接收新任务
|
||||
│
|
||||
▼
|
||||
pool.Shutdown(ctx) ← 30 秒超时
|
||||
│
|
||||
├── closed.Swap(true) ← 停止接收新任务
|
||||
├── cancel() ← 通知 Worker 停止取任务
|
||||
├── wg.Wait() ← 等待所有 Worker 退出
|
||||
│
|
||||
├── 成功 → 日志 "workerpool shutdown gracefully"
|
||||
└── 超时 → 日志 "workerpool shutdown timeout"
|
||||
```mermaid
|
||||
graph TB
|
||||
SIG["收到SIGINT / SIGTERM"] --> STOP_CONSUME["consumeCancel<br/>停止消费者不再接收新任务"]
|
||||
STOP_CONSUME --> SHUTDOWN["pool.Shutdownctx<br/>30秒超时"]
|
||||
SHUTDOWN --> SWAP["closed.Swaptrue<br/>停止接收新任务"]
|
||||
SHUTDOWN --> CANCEL["cancel<br/>通知Worker停止取任务"]
|
||||
SHUTDOWN --> WAIT["wg.Wait<br/>等待所有Worker退出"]
|
||||
WAIT --> SUCCESS{"成功?"}
|
||||
SUCCESS -->|是| LOG_OK["日志workerpool shutdown gracefully"]
|
||||
SUCCESS -->|否| LOG_TIMEOUT["日志workerpool shutdown timeout"]
|
||||
```
|
||||
|
||||
**Shutdown 返回值**:
|
||||
@@ -192,7 +200,7 @@ pool.Shutdown(ctx) ← 30 秒超时
|
||||
| `true` | 所有任务正常完成 |
|
||||
| `false` | 超时,部分任务可能丢失 |
|
||||
|
||||
## Metrics 指标
|
||||
### Metrics 指标
|
||||
|
||||
协程池内置原子计数器,支持运行时观测:
|
||||
|
||||
@@ -207,7 +215,7 @@ pool.Shutdown(ctx) ← 30 秒超时
|
||||
|
||||
所有指标通过 `Metrics()` 方法返回只读快照,同时上报 Prometheus。
|
||||
|
||||
## Task 结构体
|
||||
### Task 结构体
|
||||
|
||||
```go
|
||||
type Task struct {
|
||||
@@ -220,19 +228,13 @@ type Task struct {
|
||||
|
||||
每个任务携带超时 context(默认 10 分钟),从池的根 context 派生,确保优雅关闭时能取消正在执行的任务。
|
||||
|
||||
## 与 TaskQueue 的协作
|
||||
### 与 TaskQueue 的协作
|
||||
|
||||
```
|
||||
TaskQueue(全局排队)
|
||||
│
|
||||
▼
|
||||
Consumer(消费消息)
|
||||
│
|
||||
▼
|
||||
WorkerPool.Submit()(单机并发控制)
|
||||
│
|
||||
▼
|
||||
Worker 执行 Pipeline
|
||||
```mermaid
|
||||
graph LR
|
||||
TQ["TaskQueue全局排队"] --> CONSUMER["Consumer消费消息"]
|
||||
CONSUMER --> WP["WorkerPoolSubmit单机并发控制"]
|
||||
WP --> WORKER["Worker执行Pipeline"]
|
||||
```
|
||||
|
||||
| 组件 | 职责 | 范围 |
|
||||
@@ -243,7 +245,7 @@ Worker 执行 Pipeline
|
||||
|
||||
## 关联文档
|
||||
|
||||
- [索引](00-index.md) — 文档导航与架构总览图
|
||||
- [系统总览](01-system-overview.md) — 分层架构与依赖注入
|
||||
- [任务队列](03-task-queue.md) — 可插拔队列接口
|
||||
- [Consumer-Producer 桥接](00-index.md) — TaskQueue 到 WorkerPool 的解耦
|
||||
- [[00-索引]] — 文档导航与架构总览图
|
||||
- [[01-系统总览]] — 分层架构与依赖注入
|
||||
- [[03-任务队列]] — 可插拔队列接口
|
||||
- [[11-Consumer-Producer桥接]] — TaskQueue 到 WorkerPool 的解耦
|
||||
|
||||
Reference in New Issue
Block a user