fix: 解析大画廊时连接泄漏 + 解析阶段无进度反馈
用户反馈:点开始下载后 6 个画廊(都是 2000 张级)一直显示「解析中 0/?」, 看起来像卡死。排查发现两个问题: 1. 连接泄漏(真 bug) httpClient() 每次请求都新建 http.Transport,用完丢弃。Transport 被丢弃后 它持有的 keep-alive 连接不会立刻释放(要等 IdleConnTimeout=90s), 于是每发一个请求留下一条僵尸连接。 现场实测:91 条 Established 连接,而解析是串行的,正常应该只有 1 条。 修复:Transport 全局复用,只在代理配置变化时重建, 重建时 CloseIdleConnections 关掉旧连接;新增 Client.Close()。 实测:20 次请求从 20 条连接降到 1 条;100 次并发请求只用 22 条。 2. 解析阶段毫无反馈 2000 张 = 100 屏串行翻页,整个解析阶段界面都显示「解析中 0/?」, 完全没有进度,用户只能干等。 修复:FetchGalleryPage 增加 ParseProgress 回调,每翻完一屏上报一次 (已解析张数/总张数/当前屏),界面显示「已解析 340/2000 张」。 另外:用户设置里请求间隔是 1000ms(1 秒),全局限流让 5 个画廊共享 1 请求/秒 —— 500 次解析请求需要约 8 分钟。这是配置导致的慢,不是 bug, 但可以在界面上看到进度后就不会以为卡死了。 测试从 74 增至 80,新增: - TestTransportIsReused / TestTransportReusedUnderConcurrency(连接复用) - TestSetProxyRebuildsTransport(改代理要重建) - TestParseProgressCallback(进度回调,含最后一屏)
This commit is contained in:
+24
-2
@@ -273,6 +273,22 @@ type existingGallery struct {
|
||||
manifest *Manifest
|
||||
}
|
||||
|
||||
// reportParseProgress 解析阶段进度反馈。
|
||||
//
|
||||
// 解析大画廊要翻上百屏(每屏 20 张),全程可能好几分钟。
|
||||
// 界面必须看得到在动,否则「解析中 0/?」会被当成卡死。
|
||||
func (e *Engine) reportParseProgress(task Task, done, total, page int) {
|
||||
task.Status = "parsing"
|
||||
task.TotalPages = total
|
||||
task.Done = 0
|
||||
// 解析期用「已解析张数」当进度,下载阶段才是真正的完成数
|
||||
task.Pages = map[string]string{}
|
||||
if done > 0 {
|
||||
task.Pages["_parsing"] = strconv.Itoa(done)
|
||||
}
|
||||
e.save(task)
|
||||
}
|
||||
|
||||
// findExistingGallery 在下载根目录里找这个 gid 已存在的画廊。
|
||||
//
|
||||
// 两种匹配方式:
|
||||
@@ -366,7 +382,12 @@ func (e *Engine) runTask(ctx context.Context, task Task) {
|
||||
|
||||
// 没有可用清单(或清单太旧)→ 正常解析画廊页
|
||||
if gallery == nil {
|
||||
g, err := e.client.FetchGalleryPage(ctx, task.GID, task.Token)
|
||||
g, err := e.client.FetchGalleryPage(ctx, task.GID, task.Token,
|
||||
func(done, total, page int) {
|
||||
// 解析阶段也要有反馈,否则大画廊(上千张 = 上百屏)
|
||||
// 会长时间显示「解析中 0/?」,看着像卡死。
|
||||
e.reportParseProgress(task, done, total, page)
|
||||
})
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
e.markCanceled(task)
|
||||
@@ -464,7 +485,8 @@ func (e *Engine) runTask(ctx context.Context, task Task) {
|
||||
e.log("warn", fmt.Sprintf(
|
||||
"画廊 %s:本地清单的页面路径似乎已失效,重新解析画廊页", task.GID))
|
||||
|
||||
if fresh, err := e.client.FetchGalleryPage(ctx, task.GID, task.Token); err == nil {
|
||||
if fresh, err := e.client.FetchGalleryPage(ctx, task.GID, task.Token,
|
||||
func(done, total, page int) { e.reportParseProgress(task, done, total, page) }); err == nil {
|
||||
// 用新解析结果重跑:重置页状态
|
||||
st.reset(fresh)
|
||||
st.scanExisting()
|
||||
|
||||
Reference in New Issue
Block a user