@@ -45,8 +45,7 @@ AIROPSCAT_RATELIMIT_SNAPSHOT_TTL=10
4545AIROPSCAT_ONLINE_SNAPSHOT_TTL=60
4646AIROPSCAT_RATELIMIT_INTERVAL=3
4747AIROPSCAT_RATELIMIT_ROOT_RATE=10000mbit
48- AIROPSCAT_RATELIMIT_ACTIVE_TTL=15
49- AIROPSCAT_RATELIMIT_MAX_ACTIVE_CLASSES=512
48+ AIROPSCAT_RATELIMIT_MAX_TC_CLASSES=512
5049EOF
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
258257POLL_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+ )
261264SCHEMA_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