- 抢购调度重构:参考 dygou_go 将 sniper 拆分为独立子模块(runner/run_control/retry/exchange/classify/config/summary/category/helpers);移除全局自适应降速与轮次上限,采用账号独立循环、统一账号级退避、单请求并发闸门与首发错峰 - 预热与建连优化:默认关闭预热(避免单账号超时连坐整机开抢),开启时转为后台非阻塞协程;WebSocket 握手超时从 10s 降至 1500ms - 双时间日志:logger 增加 SetServerOffset,校准后日志前缀自动渲染「本地时间(H虎牙估算时间)」,实现跨机器绝对毫秒对齐 - 默认配置调优:版本提升至 v0.5.0,默认线程调为 2(缓解多实例并发限流),前端自动平滑迁移 - 验证方式:go test ./... -count=1 全通,npm run build 构建正常,单测覆盖双时间与预热配置
54 lines
1.4 KiB
Go
54 lines
1.4 KiB
Go
package sniper
|
|
|
|
import (
|
|
"sort"
|
|
"time"
|
|
)
|
|
|
|
// emitSummary 在任务结束时回调一次汇总;OnSummary 为 nil 时不做任何事。
|
|
func (r *Runner) emitSummary(state *runState, accounts, targets, attempts int, cost time.Duration) {
|
|
if r.OnSummary == nil {
|
|
return
|
|
}
|
|
stats := state.snapshotStats()
|
|
summary := RunSummary{
|
|
Attempts: attempts,
|
|
Accounts: accounts,
|
|
Targets: targets,
|
|
Duration: cost,
|
|
ListReqs: stats.listReqs,
|
|
ExchangeReqs: stats.exchangeReqs,
|
|
ScoreReqs: stats.scoreReqs,
|
|
RecordReqs: stats.recordReqs,
|
|
SuccessUIDs: len(stats.successUIDs),
|
|
Outcomes: stats.outcomes,
|
|
}
|
|
if state.windowed() {
|
|
summary.OpenAt = state.openAt
|
|
summary.WindowSeconds = int(state.windowLen / time.Second)
|
|
}
|
|
for _, kind := range []requestKind{reqList, reqExchange, reqScore, reqRecord} {
|
|
stat := stats.requests[kind]
|
|
if stat == nil || stat.count == 0 {
|
|
continue
|
|
}
|
|
summary.RequestStats = append(summary.RequestStats, RequestStat{
|
|
Kind: kind.String(),
|
|
Count: stat.count,
|
|
Failed: stat.failed,
|
|
Total: stat.total,
|
|
Slowest: stat.slowest,
|
|
LastErr: stat.lastErr,
|
|
})
|
|
}
|
|
uids := make([]int64, 0, len(stats.accounts))
|
|
for uid := range stats.accounts {
|
|
uids = append(uids, uid)
|
|
}
|
|
sort.Slice(uids, func(i, j int) bool { return uids[i] < uids[j] })
|
|
for _, uid := range uids {
|
|
summary.AccountOutcomes = append(summary.AccountOutcomes, *stats.accounts[uid])
|
|
}
|
|
r.OnSummary(summary)
|
|
}
|