From a135effd7a30fbceb1c9905a975e4e69950e5dfa Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sat, 17 Oct 2026 18:36:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(sniper):=20=E5=B0=86=E9=A2=84=E7=83=AD?= =?UTF-8?q?=E6=8F=90=E5=89=8D=E9=87=8F=E8=B0=83=E4=BC=98=E8=87=B3=2010=20?= =?UTF-8?q?=E7=A7=92=E5=B9=B6=E8=AE=BE=E4=B8=BA=E5=BC=82=E6=AD=A5=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E9=BB=98=E8=AE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 预热时序重构:WarmupLead 从 2s 延长至 10s,ResyncLead 从 3s 延长至 15s;开抢前 10s 开始异步建连,开抢前 5s 全部就绪待命 - 兼顾零建连延迟与发令安全:既彻底杜绝了 2s 极限时间被慢网络超时拖垮发车的问题,又保留了首发 0 建连耗时的长连接极速优势 - 默认配置:Prewarm 恢复为默认开启,并在界面增加明确的调度行为提示 - 验证方式:go test ./... -count=1 全通,npm run build 构建通过 --- frontend/src/App.tsx | 13 ++----------- internal/gui/app.go | 2 +- internal/gui/app_test.go | 4 ++-- internal/sniper/timing.go | 11 +++++------ 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c4ecc6c..ed8747b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -562,15 +562,6 @@ function readSavedConfig(): Partial { localStorage.setItem(concurrencyMigrationKey, '1') } - const prewarmMigrationKey = scopedKey('huya_gui_prewarm_default_migration_v050') - if (!localStorage.getItem(prewarmMigrationKey)) { - if (saved.prewarm !== false) { - saved.prewarm = false - dirty = true - } - localStorage.setItem(prewarmMigrationKey, '1') - } - if (dirty) { // 迁移必须立即落盘,否则下一次 readSavedConfig 又会读回旧值。 localStorage.setItem(configKey, JSON.stringify(saved)) @@ -692,7 +683,7 @@ function pickConfig(values: Partial): UiConfig { windowSeconds: Number(values.windowSeconds || 30), stopAfterSuccess: Boolean(values.stopAfterSuccess), stopAccountAfterSuccess: values.stopAccountAfterSuccess !== false, - prewarm: Boolean(values.prewarm), + prewarm: values.prewarm !== false, directExchange: values.directExchange !== false, sharePrizeList: values.sharePrizeList !== false, autoSelect: Boolean(values.autoSelect), @@ -2311,7 +2302,7 @@ export default function Dashboard() { name="prewarm" label="预热" valuePropName="checked" - tooltip="默认关闭。开启时在后台异步建连,绝不阻塞主发令枪;不预热时首发自动建连并配合提前量放行" + tooltip="默认开启。开抢前 10 秒在后台异步建连,开抢前 5 秒就绪待命,享受首发零延迟;独立协程运行,绝不阻塞发令枪" > diff --git a/internal/gui/app.go b/internal/gui/app.go index bc41e26..bc6c375 100644 --- a/internal/gui/app.go +++ b/internal/gui/app.go @@ -308,7 +308,7 @@ func (a *App) DefaultConfig() Config { Concurrency: 2, StopAfterSuccess: false, StopAccountAfterSuccess: true, - Prewarm: false, + Prewarm: true, DirectExchange: true, SharePrizeList: true, AutoSelect: false, diff --git a/internal/gui/app_test.go b/internal/gui/app_test.go index 2f97de2..d55d220 100644 --- a/internal/gui/app_test.go +++ b/internal/gui/app_test.go @@ -76,8 +76,8 @@ func TestDefaultConfigUsesWindowMode(t *testing.T) { if cfg.Concurrency != 2 { t.Fatalf("Concurrency=%d, want 2(降低多开时的请求洪峰)", cfg.Concurrency) } - if cfg.Prewarm { - t.Fatal("Prewarm 应默认关闭,避免开抢前因单账号建连超时阻塞") + if !cfg.Prewarm { + t.Fatal("Prewarm 应默认开启(提前 10 秒异步预热,不阻塞发令枪)") } } diff --git a/internal/sniper/timing.go b/internal/sniper/timing.go index 510abb5..f54bb2d 100644 --- a/internal/sniper/timing.go +++ b/internal/sniper/timing.go @@ -15,14 +15,13 @@ const MinTriggerAdvance = 50 * time.Millisecond const ( // WarmupLead 是预热连接相对开抢时刻的提前量。 // - // 2 秒足够建连 + 握手(实测单账号建连约 300ms),又不会让连接闲置太久: - // 旧实现一启动就预热、然后干等几十分钟,连接是否还活着只能靠运气。 - WarmupLead = 2 * time.Second + // 提前 10 秒开始预热连接:为 30+ 账号留足 3~5 秒平滑建连缓冲,开抢前 5 秒全部就绪; + // 既享受长连接 0 延迟首发,又能在开抢前形成静默隔离带,绝不阻塞整点发车。 + WarmupLead = 10 * time.Second // ResyncLead 是「临近复校」相对开抢时刻的提前量。 // - // 只做一次校准是不够的:pc-1/pc-2/pc-3 三台机器的本机偏移分别是 ≈0 / -370ms / +900ms, - // 而且本机时钟在等待期间还会漂移。触发前 3 秒再校准一次,能把这部分误差压掉。 - ResyncLead = 3 * time.Second + // 提前 15 秒复校时钟:在预热前完成服务端时钟校准,排定精确的预热与触发时刻。 + ResyncLead = 15 * time.Second ) // TriggerPlan 描述一次定时抢购的关键时刻(全部是「本机时钟」下的时刻)。