Skip to content

Commit a157b7b

Browse files
committed
fix: 避免限速代理频繁重建 HTB
1 parent c17fd0e commit a157b7b

2 files changed

Lines changed: 25 additions & 48 deletions

File tree

docs/ratelimit-loadtest.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ python3 scripts/ratelimit_loadtest.py \
134134
- `connect_failed` 在升压后不持续增长。
135135
- `request_failed` 不持续增长,少量目标站主动断连可接受。
136136
- `journalctl -u airopscat-ratelimit.service` 没有持续刷屏错误。
137-
- `tc class` 数量接近活跃账号数,而不是连接数
137+
- `tc class` 数量接近已配置限速账号数,而不是活跃账号数或连接数
138138
- `nft map` 元素数量接近活跃连接数,随连接关闭逐步下降。
139139

140140
## 需要记录

src/main/resources/config/shell/02-ratelimit-agent.sh

Lines changed: 24 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,7 @@ AIROPSCAT_RATELIMIT_SNAPSHOT_TTL=10
4545
AIROPSCAT_ONLINE_SNAPSHOT_TTL=60
4646
AIROPSCAT_RATELIMIT_INTERVAL=3
4747
AIROPSCAT_RATELIMIT_ROOT_RATE=10000mbit
48-
AIROPSCAT_RATELIMIT_ACTIVE_TTL=15
49-
AIROPSCAT_RATELIMIT_MAX_ACTIVE_CLASSES=512
48+
AIROPSCAT_RATELIMIT_MAX_TC_CLASSES=512
5049
EOF
5150

5251
cat > /usr/local/bin/airopscat-connection-snapshot-agent <<'EOF'
@@ -256,8 +255,12 @@ def env_int(name, default, minimum=None, maximum=None):
256255
257256
258257
POLL_INTERVAL = env_int("AIROPSCAT_RATELIMIT_INTERVAL", 3, 1)
259-
ACTIVE_TTL = max(POLL_INTERVAL, env_int("AIROPSCAT_RATELIMIT_ACTIVE_TTL", 15, 1))
260-
MAX_ACTIVE_CLASSES = env_int("AIROPSCAT_RATELIMIT_MAX_ACTIVE_CLASSES", 512, 0, TC_CLASS_MINOR_MAX - 2)
258+
MAX_TC_CLASSES = env_int(
259+
"AIROPSCAT_RATELIMIT_MAX_TC_CLASSES",
260+
env_int("AIROPSCAT_RATELIMIT_MAX_ACTIVE_CLASSES", 512, 0, TC_CLASS_MINOR_MAX - 2),
261+
0,
262+
TC_CLASS_MINOR_MAX - 2,
263+
)
261264
SCHEMA_VERSION = 1
262265
263266
_last_error_log_at = {}
@@ -342,37 +345,20 @@ def calc_burst_k(speed_kbit):
342345
return max(128, max(128 * 1024, bytes_per_sec) // 1024)
343346
344347
345-
def build_active_accounts(flows, accounts, last_seen):
346-
now = time.monotonic()
347-
counts = {}
348-
for flow in flows:
349-
if not isinstance(flow, list) or len(flow) < 4:
350-
continue
351-
account_no = str(flow[3] or "").strip()
352-
if account_no in accounts:
353-
last_seen[account_no] = now
354-
counts[account_no] = counts.get(account_no, 0) + 1
355-
356-
active = []
357-
for account_no, seen_at in list(last_seen.items()):
358-
if account_no not in accounts or now - seen_at > ACTIVE_TTL:
359-
last_seen.pop(account_no, None)
360-
continue
361-
active.append((account_no, seen_at, counts.get(account_no, 0)))
362-
363-
if MAX_ACTIVE_CLASSES and len(active) > MAX_ACTIVE_CLASSES:
364-
active.sort(key=lambda item: (item[2] > 0, item[2], item[1], item[0]), reverse=True)
365-
for account_no, _, count in active[MAX_ACTIVE_CLASSES:]:
366-
if count == 0:
367-
last_seen.pop(account_no, None)
368-
active = active[:MAX_ACTIVE_CLASSES]
348+
def select_limited_accounts(accounts):
349+
if not MAX_TC_CLASSES or len(accounts) <= MAX_TC_CLASSES:
350+
return dict(accounts)
369351
370-
return {account_no: accounts[account_no] for account_no, _, _ in active}
352+
selected = {}
353+
for account_no in sorted(accounts)[:MAX_TC_CLASSES]:
354+
selected[account_no] = accounts[account_no]
355+
log_limited("tc_class_limit", f"HTB class 数量达到配置上限,仅为前 {len(selected)} 个账号创建 class: totalAccounts={len(accounts)}")
356+
return selected
371357
372358
373-
def rebuild_tc(nic, active_accounts):
359+
def rebuild_tc(nic, tc_accounts):
374360
run(["tc", "qdisc", "del", "dev", nic, "root"])
375-
if not active_accounts:
361+
if not tc_accounts:
376362
return {}
377363
378364
run(["tc", "qdisc", "add", "dev", nic, "root", "handle", "1:", "htb", "default", "9999", "r2q", "1"], check=True)
@@ -383,14 +369,14 @@ def rebuild_tc(nic, active_accounts):
383369
384370
marks = {}
385371
class_id = 2
386-
for account_no in sorted(active_accounts):
372+
for account_no in sorted(tc_accounts):
387373
if class_id == 9999:
388374
class_id += 1
389375
if class_id > TC_CLASS_MINOR_MAX:
390376
log_limited("tc_class_limit", f"HTB class 数量达到上限,已跳过剩余账号: built={len(marks)}")
391377
break
392378
393-
speed_kbit = active_accounts[account_no] * 8
379+
speed_kbit = tc_accounts[account_no] * 8
394380
burst_k = f"{calc_burst_k(speed_kbit)}k"
395381
class_result = run(["tc", "class", "add", "dev", nic, "parent", "1:1", "classid", f"1:{class_id}",
396382
"htb", "rate", f"{speed_kbit}kbit", "burst", burst_k, "cburst", burst_k, "quantum", "1514"])
@@ -508,8 +494,7 @@ def main():
508494
nic = detect_nic()
509495
accounts_mtime = None
510496
accounts = {}
511-
active_accounts = {}
512-
last_seen = {}
497+
limited_accounts = {}
513498
marks = {}
514499
prev_maps = empty_maps()
515500
@@ -526,11 +511,10 @@ def main():
526511
if mtime != accounts_mtime:
527512
accounts = load_accounts()
528513
accounts_mtime = mtime
529-
last_seen = {key: value for key, value in last_seen.items() if key in accounts}
530-
active_accounts = {}
531-
marks = rebuild_tc(nic, {})
514+
limited_accounts = select_limited_accounts(accounts)
515+
marks = rebuild_tc(nic, limited_accounts)
532516
sync_nft_maps(prev_maps, empty_maps())
533-
log(f"已加载限速账号: totalAccounts={len(accounts)}")
517+
log(f"已加载限速账号: totalAccounts={len(accounts)}, tcClasses={len(marks)}")
534518
535519
if not accounts:
536520
time.sleep(POLL_INTERVAL)
@@ -543,21 +527,14 @@ def main():
543527
time.sleep(POLL_INTERVAL)
544528
continue
545529
546-
next_active = build_active_accounts(flows, accounts, last_seen)
547-
if next_active != active_accounts:
548-
active_accounts = next_active
549-
marks = rebuild_tc(nic, active_accounts)
550-
sync_nft_maps(prev_maps, empty_maps())
551-
log(f"已刷新限速 class: activeAccounts={len(active_accounts)}, totalAccounts={len(accounts)}")
552-
553530
sync_nft_maps(prev_maps, desired_marks(flows, marks))
554531
except Exception as exc:
555532
log_limited("main", f"限速代理执行失败: {exc}", 10)
556533
try:
557534
nic = detect_nic()
558535
setup_nft()
559536
prev_maps = empty_maps()
560-
marks = rebuild_tc(nic, active_accounts)
537+
marks = rebuild_tc(nic, limited_accounts)
561538
sync_nft_maps(prev_maps, empty_maps())
562539
except Exception as recover_exc:
563540
log_limited("recover", f"限速代理恢复失败: {recover_exc}", 10)

0 commit comments

Comments
 (0)