fix 分页bug,加入手动刷盘
This commit is contained in:
+84
-5
@@ -7,6 +7,8 @@ package harvester
|
||||
|
||||
import (
|
||||
"encoding/json" // JSON 反序列化(解析爬虫请求)
|
||||
"fmt" // 错误格式化
|
||||
"io" // 读取请求体
|
||||
"log" // 日志输出
|
||||
"math/rand" // 随机数(打乱合并顺序、触发概率性操作)
|
||||
"net/http" // HTTP 服务端
|
||||
@@ -42,31 +44,108 @@ func New(db *storage.DB, infoSvc *info.Service) *Server {
|
||||
}
|
||||
}
|
||||
|
||||
// ingestPayload 是爬虫发送的 JSON 请求体结构。
|
||||
// ingestPayload 是爬虫发送的 JSON 请求体结构(Go 爬虫用)。
|
||||
type ingestPayload struct {
|
||||
URL string `json:"url"` // 被索引页面的最终 URL
|
||||
URL string `json:"url"` // 被索引页面的最终 URL
|
||||
Keywords []struct {
|
||||
Word string `json:"word"` // 关键词
|
||||
Weight float32 `json:"weight"` // 该 URL 在该词下的权重
|
||||
} `json:"keywords"`
|
||||
}
|
||||
|
||||
// ingestPayloadLegacy 是 Python 爬虫发送的旧格式:[url, [[word, weight], ...]]
|
||||
// 兼容处理:Python 端发的是数组,Go 期望的是对象。
|
||||
type ingestPayloadLegacy []any
|
||||
|
||||
// Handler 返回 HTTP 路由处理器。
|
||||
func (s *Server) Handler() http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/l", s.handleIngest) // /l 端点:接收爬虫数据
|
||||
mux.HandleFunc("/flush", s.handleFlush) // /flush:强制刷盘(用于手动触发或调试)
|
||||
mux.HandleFunc("/admin/pending", s.handleAdminPending) // /admin/pending:返回未刷盘数据条数
|
||||
return mux
|
||||
}
|
||||
|
||||
// handleAdminPending 返回内存中未刷盘的索引条目数量。
|
||||
func (s *Server) handleAdminPending(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
count := atomic.LoadInt64(&s.rowCount)
|
||||
json.NewEncoder(w).Encode(map[string]int64{"pending": count})
|
||||
}
|
||||
|
||||
// handleFlush 处理 GET /flush 请求,强制将内存索引刷到磁盘。
|
||||
func (s *Server) handleFlush(w http.ResponseWriter, r *http.Request) {
|
||||
s.flush()
|
||||
w.Write([]byte("flushed"))
|
||||
}
|
||||
|
||||
// Flush 公开的刷盘方法,供外部调用(定时刷盘、退出前刷盘)。
|
||||
func (s *Server) Flush() { s.flush() }
|
||||
|
||||
// parsePayload 解析爬虫请求体,兼容新旧两种格式。
|
||||
// 新格式(Go 爬虫):{"url": "...", "keywords": [{"word": "...", "weight": 0.0}]}
|
||||
// 旧格式(Python 爬虫):["url", [["word", weight], ...]]
|
||||
func parsePayload(r *http.Request) (*ingestPayload, error) {
|
||||
// 读取 body 为 bytes(支持重读)
|
||||
body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 先尝试新格式(Go 爬虫)
|
||||
var modern ingestPayload
|
||||
if err := json.Unmarshal(body, &modern); err == nil && modern.URL != "" {
|
||||
return &modern, nil
|
||||
}
|
||||
|
||||
// 尝试旧格式(Python 爬虫):[url, [[word, weight], ...]]
|
||||
var legacy ingestPayloadLegacy
|
||||
if err := json.Unmarshal(body, &legacy); err != nil {
|
||||
return nil, fmt.Errorf("invalid payload: %w", err)
|
||||
}
|
||||
if len(legacy) < 2 {
|
||||
return nil, fmt.Errorf("invalid legacy payload: too few elements")
|
||||
}
|
||||
url, ok := legacy[0].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid url type")
|
||||
}
|
||||
kwsRaw, ok := legacy[1].([]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid keywords type")
|
||||
}
|
||||
payload := &ingestPayload{URL: url}
|
||||
for _, kw := range kwsRaw {
|
||||
kwSlice, ok := kw.([]any)
|
||||
if !ok || len(kwSlice) < 2 {
|
||||
continue
|
||||
}
|
||||
word, _ := kwSlice[0].(string)
|
||||
weight, _ := kwSlice[1].(float64)
|
||||
if word == "" {
|
||||
continue
|
||||
}
|
||||
payload.Keywords = append(payload.Keywords, struct {
|
||||
Word string `json:"word"`
|
||||
Weight float32 `json:"weight"`
|
||||
}{word, float32(weight)})
|
||||
}
|
||||
if payload.URL == "" {
|
||||
return nil, fmt.Errorf("empty url after parsing")
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
// handleIngest 处理爬虫发来的 POST 请求,将关键词数据写入内存索引。
|
||||
func (s *Server) handleIngest(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
var payload ingestPayload
|
||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||
http.Error(w, "bad json: "+err.Error(), http.StatusBadRequest)
|
||||
payload, err := parsePayload(r)
|
||||
if err != nil {
|
||||
http.Error(w, "bad payload: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user