设为首页 收藏本站
查看: 1815|回复: 0

[经验分享] 用elasticsearch和nuxtjs搭建bt搜索引擎

[复制链接]

尚未签到

发表于 2019-1-29 10:10:41 | 显示全部楼层 |阅读模式
世界上已经有了这么多种子搜索引擎,为什么你还要不厌其烦的做一个新的?
  可以这么说,地球上大多数的种子搜索引擎的前后端技术都比较古老,虽然古老的技术既经典又好用,但是作为一个喜欢尝鲜的人,我仍然决定使用目前最为先进的开发技术制作一个功能简明的种子搜索引擎。

采用了什么技术?
  前端:在vue,angular,react三大现×××发框架中选择了vue,做出这个决定的原因也仅仅是一直以来对vue的谜之好感。有时候就是这样,缘分到了不得不从,恰巧nuxtjs在9月更新了2.0,因此毫不犹豫选择了vue。
后端:在koa,gin,springboot中权衡良久,由于很长时间没有写过java,最后选择了springboot + jdk11,用写javascript的感觉来写java,还是很不错的。从追求速度上来讲,可能使用gin或Koa要更快,但是这一点提升对于我这种实验性网站来说,意义并不是很大。
全文检索:尝试了全文检索里面的比较潮的couchbase、redissearch、elasticsearch,最后选定elasticsearch,另外两个的速度虽然远高于elasticsearch,但毕竟是内存式数据库,简单功能尚可,复杂度上去后则吃内存太多。

制作过程呢?
  下面我分享下大概过程,涉及到复杂原理,请自行谷歌,我不认为我可以把复杂原理描述的很简单。
  关于命名:
  从手中的十来个域名选择了
  btzhai.top
  中国国内同名的网站有几个,但是这不是问题。
  关于服务器:
  几经周折,购买了一台美国服务器。配置是:E5-1620|24G|1TB|200M带宽,真正的24小时人工服务。考虑到要用cloudfare,所以不需要硬防。一月1200RMB。
  在此期间尝试了很多家服务器,深感这免备案服务器这一行真的是泥沙俱下。
  关于爬虫:
  大约8月初终于有空来着手bt搜索引擎这件事情。
  首先摆在我面前的问题就是数据来源问题,要知道所谓的dht网络,说白了就是一个节点既是服务器又是客户端,你在利用dht网络下载时会广播到网络中,别的节点就会接收到你所下载文件的唯一标识符infohash(有的地方称之为神秘代码)和metadata,这里面包括了这个文件的名称、大小、创建时间、包含文件等信息,利用这个原理,dht爬虫就可以收集dht网络中的即时热门下载。
  如果仅仅依靠依靠dht爬虫去爬的话,理论上初期速度大约为40w一天,30天可以收集上千万,但是dht网络里面的节点不可能总是下载新的文件,现实情况是:大多数情况下冷门的种子几年无人问津,热门种子天天数十万人下载。可以推想,随着种子基数增加,重复的infohash会越来越多,慢慢地只会增加所谓的种子热度而不会增加基数,但是没有1000w+的种子,从门面上来讲不好看。
  去哪里弄1000w种子成了当时我主要研究的问题。首先我从github上选取了几个我认为比较好用的dht爬虫进行改造,让之可以直接将数据入库到elasticsearch中,并且在infohash重复的时候自动对热度+1。
  elasticsearch的mapping如下,考虑到中文分词,选用了smartcn作为分词器,当然ik也是可以的。种子内的文件列表files,本来设置为nested object,因为nested query性能不高已经取消:

{
"properties": {
"name": {
"type": "text",
"analyzer": "smartcn",
"search_analyzer": "smartcn"
},
"length": {
"type": "long"
},
"popularity": {
"type": "integer"
},
"create_time": {
"type": "date",
"format": "epoch_millis"
},
"files": {
"properties": {
"length": {
"type": "long"
},
"path": {
"type": "text",
"analyzer": "smartcn",
"search_analyzer": "smartcn"
}
}
}
}
}
  服务器上开始24小时挂着dht爬虫。期间我也尝试过多种不同语言的开源爬虫来比较性能,甚至还找人试图购买bt种子。下面这些爬虫我都实际使用过:

https://github.com/shiyanhui/dht
https://github.com/fanpei91/p2pspider
https://github.com/fanpei91/simDHT
https://github.com/keenwon/antcolony
https://github.com/wenguonideshou/zsky
  然而这些dht爬虫经试验,或多或少都有些问题,有的是只能采集infohash而不能采集metadata,有的采集速度不够,有的则随时间增加资源占用越来越大。
  最终确定的是这个最优解:

https://github.com/neoql/btlet
  唯一不妥是运行一段时间(大约10个小时)后就会崩溃退出,可能跟采集速度有关。而在我写这篇文章的前几天,作者称已经将此问题修复,我还没有来得及跟进更新。可以说这是我实验过采集速度最快的dht爬虫。有兴趣的同学可以去尝试、PR。
  爬虫正常化运行以后,我终于发现了基数问题的解决之道,那就是skytorrent关闭后dump出来的数据库和openbay,利用这大约4000w infohash数据和bthub,每天都一定可以保证有数万新的metadata入库。
  关于bthub我要说的是,api请求频率太高会被封ip,发邮件询问的结果如下。经过我的反复测试,api请求间隔设为1s也是没问题的:
DSC0000.jpg
  关于前端:
  我比较习惯于先画出简单的前端再开始写后端,前端确定清楚功能以后就可以很快写出对应的接口。bt搜索引擎目前具有以下这么几个功能就足够了:


  •   可以搜索关键词

  •   首页可以展现之前搜索过的排行前十的关键词

  •   可以随机推荐一些文件

  • 可以按照相关性、大小、创建时间、热度排序
  首页启动时,为了提高速度,从后台读cache,包括收录了多少infohash、随机推荐的文件名称、搜索关键词top10等等,这些cache使用@Scheduled每天自动更新一次。
  点击搜索后,跳转到结果展现页面,这里只展现elasticsearch处理过highlight之后的结果而不展现所有原始结果,每页展示10个结果。
  原始结果的展现放在最后一个详细画面上。
  前端承载的另一个重要问题就是seo,这也是我使用nuxtjs的原因。前端功能完成以后,我为它添加了meta描述、google analytics、百度。
  sitemap的添加倒是耗废了一些时间,因为是动态网页的缘故,只能用nuxt-sitemap来动态生成。
  另外用媒体查询和vh、vw做了移动适配。不敢说100%,至少可以覆盖90%的设备。
  关于后端:
  spring data在实现核心搜索api时遇到了问题,核心搜索如果写成json,举个例子的话,可能是下面的这个样子:

{
"from": 0,
"size": 10,
"sort": [{
"_score": "desc"
}, {
"length": "desc"
}, {
"popularity": "desc"
}, {
"create_time": "desc"
}],
"query": {
"multi_match": {
"query": "这里是要搜索的关键词",
"fields": ["name", "files.path"]
}
},
"highlight": {
"pre_tags": [""],
"post_tags": [""],
"fields": {
"name": {
"number_of_fragments": 1,
"no_match_size": 150
},
"files.path": {
"number_of_fragments": 3,
"no_match_size": 150
}
}
}
}
  highlight返回的结果将没有办法自动和entity匹配,因为这一部分数据不在source中,spring data无法通过getSourceAsMap来获取。这里需要用到NativeSearchQueryBuilder去手动配置,如果有更好的方式,请务必赐教。java代码如下:

var searchQuery = new NativeSearchQueryBuilder()
.withIndices("torrent_info").withTypes("common")
.withQuery(QueryBuilders.multiMatchQuery(param.getKeyword(), "name", "files.path"))
.withHighlightFields(new HighlightBuilder.Field("name").preTags("").postTags("").noMatchSize(150).numOfFragments(1), new HighlightBuilder.Field("files.path").preTags("").postTags("").noMatchSize(150).numOfFragments(3))
.withPageable(PageRequest.of(param.getPageNum(), param.getPageSize(), sort))
.build();
var torrentInfoPage = elasticsearchTemplate.queryForPage(searchQuery, TorrentInfoDo.class, new SearchResultMapper() {
@SuppressWarnings("unchecked")
@Override
public  AggregatedPage mapResults(SearchResponse searchResponse, Class aClass, Pageable pageable) {
if (searchResponse.getHits().getHits().length Map->FileList->List
var resList = ((ArrayList) searchHit.getSourceAsMap().get("files"));
var fileList = new ArrayList();
for (var map : resList) {
FileList file = new FileList();
file.setPath((String) map.get("path"));
file.setLength(Long.parseLong(map.get("length").toString()));
fileList.add(file);
}
torrentInfo.setFiles(fileList);
// 设置highlight部分
// 种子名称highlight(一般只有一个)
var nameHighlight = searchHit.getHighlightFields().get("name").getFragments()[0].toString();
// path highlight列表
var pathHighlight = getFileListFromHighLightFields(searchHit.getHighlightFields().get("files.path").fragments(), fileList);
torrentInfo.setNameHighLight(nameHighlight);
torrentInfo.setPathHighlight(pathHighlight);
chunk.add(torrentInfo);
}
if (chunk.size() > 0) {
// 不设置total返回不了正确的page结果
return new AggregatedPageImpl((List) chunk, pageable, searchResponse.getHits().getTotalHits());
}
return null;
}
});
  关于elasticsearch:
  种子搜索不需要多高的实时性,一台服务器也不需要副本,因此,index的设置都是这样:

{
"settings": {
"number_of_shards": 2,
"number_of_replicas": 0,
"refresh_interval": "90s"
}
}
  jvm配置了8G内存,G1GC,另外还禁了swapping:

## IMPORTANT: JVM heap size
-Xms8g
-Xmx8g
## GC configuration
-XX:+UseG1GC
-XX:MaxGCPauseMillis=50
运行得怎么样?
  由于搜索比较复杂,平均搜索时间1s左右,搜索命中上百万数据时会大于2s。
  下面是cloudfare的统计:
DSC0001.jpg




运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-669046-1-1.html 上篇帖子: td 下篇帖子: ElasticSearch笔记整理(三):Java API使用与ES中文分词
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表