// sese-engine — Go rewrite // Go 版 sese-engine:个人搜索引擎的主入口文件。 // // 所有模块(爬虫、收获服务器、搜索服务器、反向链接计算)均作为 goroutine 在同一进程中启动。 // 主线程阻塞等待系统信号(Ctrl-C / SIGTERM),收到后优雅退出。 // // 运行方式: // // cd golang && go run . [--storage ./savedata] [--entry https://zh.wikipedia.org/] package main import ( "flag" // 命令行参数解析 "fmt" // 格式化(搜索服务端口) "log" // 日志输出 "os" // 操作系统信号 "os/signal" // 信号捕获 "syscall" // 系统调用(SIGTERM) "sese-engine/analyzer" // 文本分析和关键词提取 "sese-engine/backlink" // 反向链接(繁荣值)计算 "sese-engine/config" // 全局配置 "sese-engine/crawler" // BFS 爬虫 "sese-engine/harvester" // 收获服务器(索引写入) "sese-engine/info" // info 服务(繁荣表、调整表、屏蔽词) "sese-engine/search" // 搜索服务器 "sese-engine/storage" // 持久化存储 ) func main() { // ---- 命令行参数 ---- // --storage:存储根目录路径,默认使用 config.StoragePath storageDir := flag.String("storage", config.StoragePath, "path to savedata directory") // --entry:BFS 爬取的起始 URL,默认使用 config.EntryURL(维基百科中文首页) entryURL := flag.String("entry", config.EntryURL, "BFS crawl entry URL") // --stopwords:屏蔽词 JSON 文件路径 stopWords := flag.String("stopwords", "../data/标点符号.json", "path to stop-words JSON") flag.Parse() // 设置日志格式:时间戳 + 短文件名 log.SetFlags(log.LstdFlags | log.Lshortfile) log.Printf("sese-engine starting storage=%s entry=%s", *storageDir, *entryURL) // ---- 1. 存储层:打开 bbolt 数据库 ---- db, err := storage.Open(*storageDir) if err != nil { log.Fatalf("failed to open storage: %v", err) } defer db.Close() // ---- 2. Info 服务:加载繁荣表、调整表和屏蔽词 ---- infoSvc := info.New(*storageDir) // ---- 3. Analyzer:初始化分词器和语言检测器 ---- // modelPath 参数已废弃(lingua-go 使用内置模型,无需外部文件) anal, err := analyzer.New("", *stopWords) if err != nil { log.Fatalf("failed to init analyzer: %v", err) } defer anal.Close() // ---- 4. 收获服务器(:5000):接收爬虫发来的索引数据 ---- harvSrv := harvester.New(db, infoSvc) go func() { if err := harvSrv.ListenAndServe(":5000"); err != nil { log.Fatalf("[harvester] fatal: %v", err) } }() // ---- 5. 搜索服务器(默认 :80):对外提供搜索 API ---- searchSrv := search.New(db, infoSvc, anal) go func() { addr := fmt.Sprintf(":%d", config.SearchServerPort) if err := searchSrv.ListenAndServe(addr); err != nil { log.Fatalf("[search] fatal: %v", err) } }() // ---- 6. 反向链接计算器:每 48 小时运行一次 ---- bl := backlink.New(db, *storageDir) go bl.Run() // ---- 7. 爬虫:从入口 URL 开始 BFS 爬取 ---- // 从 info 服务获取繁荣表快照,用于调度优先级决策 prosperMap := infoSvc.ProsperMap() crawl := crawler.New(db, anal, prosperMap) go crawl.Run(*entryURL, config.MaxEpoch) log.Println("all modules started — press Ctrl-C to stop") // ---- 优雅退出 ---- // 阻塞等待 SIGINT(Ctrl-C)或 SIGTERM 信号 quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt, syscall.SIGTERM) <-quit log.Println("shutdown signal received, exiting...") }