feat(channel-concurrency): 新增渠道级并发限制功能#4172
feat(channel-concurrency): 新增渠道级并发限制功能#4172Miraaaacle wants to merge 1 commit intoQuantumNous:mainfrom
Conversation
为每个渠道维护原子计数器实现并发控制,超限时自动降级到其他渠道, 所有渠道满后返回 429。前端支持渠道列表内联编辑和编辑弹窗配置。 后端: - 新增 common/concurrency_limiter.go(sync.Map + atomic.Int64) - Channel 模型新增 max_concurrency 字段 - 重试循环中集成 acquire/release,不消耗 retry 次数 - 支持按 Tag 批量编辑 max_concurrency 前端: - 渠道列表新增最大并发列(内联编辑) - 编辑弹窗新增最大并发数输入框
WalkthroughIntroduces per-channel concurrency limiting using an atomic counter infrastructure. Tracks concurrent requests per channel via a global sync.Map, enforces limits during request relay, and returns HTTP 429 when capacity is exceeded. Extends channel configuration with maxConcurrency settings and provides frontend UI controls for management. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant Relay as Relay Handler
participant Limiter as Concurrency Limiter
participant Model as Channel Model
participant Backend as API Backend
Client->>Relay: Request to channel
Relay->>Model: Get channel config
Model-->>Relay: Channel with maxConcurrency
loop Acquire Semaphore
Relay->>Limiter: ConcurrencyAcquire(channelId, maxConcurrency)
Limiter-->>Relay: permit granted? (bool)
alt Permit Denied
Relay->>Relay: Increment skip counter
alt Skip threshold exceeded (5)
Relay-->>Client: HTTP 429 Too Many Requests
note over Relay: concurrency_full error
else Retry
Relay->>Relay: ResetRetryNextTry()
Relay->>Model: Select different channel
end
else Permit Granted
rect rgba(100, 200, 100, 0.5)
Relay->>Backend: Forward request
Backend-->>Relay: Response
end
Relay->>Limiter: ConcurrencyRelease(channelId)
Limiter-->>Relay: Released
Relay-->>Client: Response
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ast-grep (0.42.1)web/src/components/table/channels/modals/EditChannelModal.jsx[] Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
web/src/components/table/channels/modals/EditChannelModal.jsx (1)
2463-2475: 建议给max_concurrency增加显式默认值,避免新建/重置时出现未定义状态。当前字段已接线正确;再补一个默认值会让提交与重置行为更稳定。
♻️ 建议修改
@@ const originInputs = { @@ priority: 0, weight: 0, + max_concurrency: 0, tag: '', @@ };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/table/channels/modals/EditChannelModal.jsx` around lines 2463 - 2475, The max_concurrency Form.InputNumber can be undefined on new/reset forms; set an explicit default (e.g., 0) when initializing or resetting the form state so the input always receives a value. Update the component that supplies the form values in EditChannelModal.jsx to initialize max_concurrency to 0 (or set a defaultValue on the Form.InputNumber) and ensure handleInputChange('max_concurrency', ...) preserves numeric defaults on resets so submissions never encounter undefined for max_concurrency.web/src/components/table/channels/ChannelsColumnDefs.jsx (1)
685-744: Add validation formax_concurrencyinsubmitTagEdit.The column definition correctly follows the pattern used for
weight, but thesubmitTagEditfunction inuseChannelsData.jsx(lines 627-658) lacks a validation case for'max_concurrency'. Whileweightvalidates that the value is a non-negative integer before the API call,max_concurrencywill fall through without validation.Consider adding validation in
submitTagEdit:case 'max_concurrency': if (data.max_concurrency === undefined || data.max_concurrency < 0 || data.max_concurrency === '') { showInfo('最大并发数必须是非负整数!'); return; } data.max_concurrency = parseInt(data.max_concurrency); break;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/table/channels/ChannelsColumnDefs.jsx` around lines 685 - 744, submitTagEdit is missing validation for the 'max_concurrency' case so non-numeric or negative values can be submitted; update submitTagEdit (in useChannelsData.jsx) to mirror the 'weight' validation: add a case for 'max_concurrency' that checks for undefined/empty string or negative values, shows the same user-facing error (e.g., showInfo('最大并发数必须是非负整数!')) and returns early, then parseInt the value into data.max_concurrency before proceeding with the API call.common/concurrency_limiter.go (1)
8-9: Consider adding a cleanup mechanism for deleted channels.The
sync.Mapentries persist indefinitely. While each entry is small (~8 bytes foratomic.Int64), if channels are frequently created/deleted, this could accumulate. Consider adding a cleanup function similar toCleanupChannelPollingLocksinmodel/channel.gothat removes entries for non-existent channels periodically.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@common/concurrency_limiter.go` around lines 8 - 9, The channelConcurrency sync.Map currently retains entries forever; add a cleanup routine (similar to CleanupChannelPollingLocks in model/channel.go) that periodically iterates channelConcurrency, checks whether each channel ID still exists (using the existing channel lookup function such as ChannelExists or GetChannel in the model package), and calls channelConcurrency.Delete(key) for entries whose channels are gone; run this cleanup on a timer (e.g., background goroutine or scheduled task) and ensure it safely handles the stored *atomic.Int64 values and does not race with normal increment/decrement operations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@common/concurrency_limiter.go`:
- Around line 8-9: The channelConcurrency sync.Map currently retains entries
forever; add a cleanup routine (similar to CleanupChannelPollingLocks in
model/channel.go) that periodically iterates channelConcurrency, checks whether
each channel ID still exists (using the existing channel lookup function such as
ChannelExists or GetChannel in the model package), and calls
channelConcurrency.Delete(key) for entries whose channels are gone; run this
cleanup on a timer (e.g., background goroutine or scheduled task) and ensure it
safely handles the stored *atomic.Int64 values and does not race with normal
increment/decrement operations.
In `@web/src/components/table/channels/ChannelsColumnDefs.jsx`:
- Around line 685-744: submitTagEdit is missing validation for the
'max_concurrency' case so non-numeric or negative values can be submitted;
update submitTagEdit (in useChannelsData.jsx) to mirror the 'weight' validation:
add a case for 'max_concurrency' that checks for undefined/empty string or
negative values, shows the same user-facing error (e.g.,
showInfo('最大并发数必须是非负整数!')) and returns early, then parseInt the value into
data.max_concurrency before proceeding with the API call.
In `@web/src/components/table/channels/modals/EditChannelModal.jsx`:
- Around line 2463-2475: The max_concurrency Form.InputNumber can be undefined
on new/reset forms; set an explicit default (e.g., 0) when initializing or
resetting the form state so the input always receives a value. Update the
component that supplies the form values in EditChannelModal.jsx to initialize
max_concurrency to 0 (or set a defaultValue on the Form.InputNumber) and ensure
handleInputChange('max_concurrency', ...) preserves numeric defaults on resets
so submissions never encounter undefined for max_concurrency.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ad2f7785-36f9-4a45-a9ec-25b20a044c6c
📒 Files selected for processing (10)
common/concurrency_limiter.gocontroller/channel.gocontroller/relay.gomodel/channel.gotypes/error.goweb/src/components/table/channels/ChannelsColumnDefs.jsxweb/src/components/table/channels/modals/EditChannelModal.jsxweb/src/hooks/channels/useChannelsData.jsxweb/src/i18n/locales/en.jsonweb/src/i18n/locales/zh-CN.json
Closes #1730
为每个渠道维护原子计数器实现并发控制,超限时自动降级到其他渠道,
所有渠道满后返回 429。前端支持渠道列表内联编辑和编辑弹窗配置。
后端:
前端:
Important
📝 变更描述 / Description
(简述:做了什么?为什么这样改能生效?请基于你对代码逻辑的理解来写,避免粘贴未经整理的内容)
🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。📸 运行证明 / Proof of Work
(请在此粘贴截图、关键日志或测试报告,以证明变更生效)

Summary by CodeRabbit
Release Notes
New Features
Localization