golang-构建搜索引擎微服务riot

xruidlw 2019-03-09

需求

内容型网站,比如论坛、知识库或者文档查询等,均需要根据用户输入的内容,搜索匹配的文档。

这时我们可以搭建一些高性能的搜索服务,来给主站提供“搜索”功能支持。

使用golang写的开源搜索引擎 riot (riot是在wukong的基础上开发的,wukong现在更新维护基本是停滞状态)吸引了我们的目光,高性能、可扩展、支持中文搜索。

实现

环境准备

首先需要安装golang,版本是1.8以上(ubuntu现在推荐的是1.6版本,因此需要从golang的官网下载,然后配置环境变量即可)

安装riot的依赖包

  • go get “github.com/shirou/gopsutil”
  • go get "github.com/go-ego/gpy"

安装riot

  • go get -u github.com/go-ego/riot
  • go get -u github.com/go-ego/re

roit的工作原理

引擎中处理用户请求、分词、索引和排序分别由不同的协程(goroutines)完成。

1. 主协程,用于收发用户请求
2. 分词器(segmenter)协程,负责分词
3. 索引器(indexer)协程,负责建立和查找索引表
4. 排序器(ranker)协程,负责对文档评分排序

golang-构建搜索引擎微服务riot

索引流程

当一个将文档(document)加入索引的请求进来以后,主协程会通过一个信道(channel)将要分词的文本发送给某个分词协程,该协程将文本分词后通过另一个信道发送给一个索引器协程。索引器协程建立从搜索键(search keyword)到文档的反向索引(inverted index),反向索引表保存在内存中方便快速调用。

搜索流程

主协程接到用户请求,将请求短语在主协程内分词,然后通过信道发送给索引器,索引器查找每个搜索键对应的文档然后进行逻辑操作(归并求交集)得到一个精简的文档列表,此列表通过信道传递给排序器,排序器对文档进行评分(scoring)、筛选和排序,然后将排好序的文档通过指定的信道发送给主协程,主协程将结果返回给用户。

分词、索引和排序都有多个协程完成,中间结果保存在信道缓冲队列以避免阻塞。为了提高搜索的并发度降低延迟,riot 引擎将文档做了裂分(裂分数目可以由用户指定),索引和排序请求会发送到所有裂分上并行处理,结果在主协程进行二次归并排序。

上面就是riot 引擎的大致原理。任何完整的搜索系统都包括四个部分, 文档抓取 、 索引 、 搜索 和 显示 。

简单使用

package main
import (
 "log"
 "github.com/go-ego/riot/engine"
 "github.com/go-ego/riot/types"
)
var (
 // searcher is coroutine safe
 searcher = engine.Engine{}
)
func main() {
 // Init searcher
 searcher.Init(types.EngineInitOptions{
 Using: 4,
 SegmenterDict: "./dict/dictionary.txt"})
 defer searcher.Close()
 // Add the document to the index, docId starts at 1
 searcher.IndexDocument(1, types.DocIndexData{Content: "Google Is Experimenting With Virtual Reality Advertising"}, false)
 searcher.IndexDocument(2, types.DocIndexData{Content: "Google accidentally pushed Bluetooth update for Home speaker early"}, false)
 searcher.IndexDocument(3, types.DocIndexData{Content: "Google is testing another Search results layout with rounded cards, new colors, and the 4 mysterious colored dots again"}, false)
 // Wait for the index to refresh
 searcher.FlushIndex()
 // The search output format is found in the types.SearchResponse structure
 log.Print(searcher.Search(types.SearchRequest{Text: "google testing"}))
}
//输出结果:
//2017/10/24 15:14:29 载入gse词典 ./dict/dictionary.txt
//2017/10/24 15:14:31 gse词典载入完毕
//2017/10/24 15:14:31 Check virtualMemory...Total: 12539645952, Free:7705378816, UsedPercent:11.799818%
//2017/10/24 15:14:31 {[google testing] [{3 Google is testing another Search results layout with rounded cards, new colors, and the 4 mysterious colored dots again <nil> <nil> [4.7] [] []}] false 1}

使用该引擎,需要将数据格式化后存储到riot,然后通过接口查询即可。

性能与扩展

性能数据请参考 https://github.com/go-ego/riot/blob/master/docs/zh/benchmarking.md

riot支持持久化存储,如果需要分布式搜索,需要将索引与数据分块,做二次开发

相关推荐