Go 在百万亿级搜索引擎中的应用


当前第2页 返回上一页

第一个瓶颈。我们团队的基础组件全是 c++,我们团队核心业务,以及在线引擎、核心引擎都是c++ 来做的。我们用到 gdb 进行调试,进程过多,用 c++ 组件一开始想偷懒,然后编辑进C,再放到 Go 里面去。每一个读取 Docid 中,每一个文件都会去读,我们的运用程序经常就挂,当时也没有原因,最后我们才看到执行 CGO 的时候,我们收到一个信号,就是 signal exit,然后我们进行GDB调试,说是进程太多,因为CGO在执行的时候会新建一个M。

  • 解决方案:用Go重新实现一遍,将组件作为http服务,Go Client调用,做集中式处理。

第二个瓶颈。在系统中,我们大量使用 Goroutine,子写程 panic 在主写程不能被处理掉。

  • 解决方案:我们在通道类型里面为struct,封装正常数据和error,在主协程取取出数据,统一做处理。

经验小结。

  1. 即使精通很多语言,最好不要混用,要非常谨慎引入其他语言的解决方案。

  2. 不要完全相信recover,它不能恢复runtime的一些panic。

Go 在百万亿级搜索引擎中的应用
。如图7所示。要做多天查询有两种方案。第一种方案把多天查询加上,这样使我们核心查询引擎变得非常臃肿,我们还是那句话,加一个中间层。把多天变成单天,然后在Proxy 拿到所有的单天数据,就形成了多天查询。

Go 在百万亿级搜索引擎中的应用

图7

我们还有另外一个项目,请求Poseidon的数据,我们想到两种解决方案,第一种解决方案,你在自己第三方系统里面做缓存,要不我们做缓存,我们是这样取舍。如果第三方系统里面做缓存,所有的查询,缓存只能在第三方系统里面用。如果在我们这里缓存,他们发了请求到我们这来,其他所有第三方里面都有可能能用上。我们是这样做的,首先请求 Searcher 拿到当天的数据,比如查一个月的数据,请求 Searcher 单天的数据,如果每一个Goroutine 去查一天,每一个 Goroutine 拿到 Searcher 单天数据之后,把它解出来,看一下是不是错误数据。如果是错误数据的话,直接给客户端把这条数据返回错误,并不是给客户端整个错误,因为只是这一天某一条数据有错误。而不至于我们在查询 30 天数据的时候,里面只要某一天某一条数据有错误,就直接返回给用户,我这个系统不可用。如果不是错误数据,会根据请求参数,请求参数有很多。除了这些之外,还有查询的时间,根据这个来做一个Cace Key,然后打回给前端。

我们遇到一个问题,每一个用户会把整个索引流程都跑一遍,也就是说用户会给我们实时测试。在同一个时间之内,同一份数据在缓存时间之内不会走完整个 readhdfs 流程。build index 程序化,我们会有监控,如果程序化我们会知道,程序挂了会报警感知,但是数据错误却是未知,我们现在还没有做到这种监控。但是这个数据错误是未知的,我们修复索引就会花费大量时间,去重新写日志,跑 Docid,还要解决漏洞。

我们的解决方案,第一个减少缓存时间,在可容忍错误数据时间之内,用户查询能及时发现问题,恢复一天两天数据还可以,不至于缓存 30 天或者一、两个月,到最后错误数据会越来越多。第二个解决方案,参考 NSQ,利用 for+select 的不确定性来分馏,随机流量到 chanel 和 hdfs 做热测试。缺点,就是开发成本相对第一种方案来说有点高。这块要注意,开发成本并不是非常高,因为 select 而只能从 chanel 拿数据。

第二个经验小结。不要选择非常高大上的一些技术,或者说一些我们所说的黑科技,简单、有效、够用能解决问题完全可以。利用 Goroutine 设计并发程序很方便,但是并发运行模型一定要 hold 住。我们之前Gopher 群里面发过一个博客,里面发了很多动态图,一些 Go 的 Goroutine 和 channel 如何并发,动态图画的非常炫。我们在写自己业务的时候,我们看了 Goroutine 以及 Goroutine 和 channel 怎么联动,我们自己有概念。我要表达观点的时候,我一时也找不到非常恰当的名词来描述,我不知道这个名词之前有没有,或者有没有其他的意义。

Proxy多天异步下载。如图8所示。前端发起请求,要选择下载多少天,下载多少数据,服务端接受到请求之后,马上给客户端返回,我已经收到了,把这个消息写到channel。刚开始我们已经说过在readHDFS是是用JAVA写的,Goroutine太多,底层挂掉。两个Searcher到HDFS的时候,一个分词对应上百个Docid,可能对应着上百个文件,因为每一个Docid不一定在一个文件里面。在Searcher里面的时候,看起来进来一个请求,实际上往后会越来越大,到最后可能就是指数级的增长,像我们滚雪球一样。

图8

首先JAVA做了简单的连接池,然后有熔断机制,如果超出一定的连接数,直接返回error。像我们很早之前的时候,保险丝,家里面的电率大的时候,保险丝是用铅丝做的,铅丝会熔化掉。

再说一下GC的变化。首先我说一下GC在我们整个系统中,从来都不是瓶颈。在这里说的几点,是我们升级之后简单做的测试,在这里和大家交流一下。如果有其他做测试比我们更细的同学,可以交流一下。

Go 1.7。我们之前用的 1.5,升级到 1.7 之后,我们的 GC 下降到了三分之一。

nginx 代理问题,之前我做分享的时候,有同学问我在 Go 前端要不要加nginx代理。我之前做的系统面向海量用户,我们只把 GoServer 打包成二进制的可执行包,请求打到 lvs 的80 端口然后再转发到 GoServer 8080,非常简单。在这个项目我们用了 nginx,我们有用它的理由。

访问控制和负载均衡。负载均衡我们可以用 LVS 做,我们这个项目的场景,使用的人非常少。第一我们是一个内部项目,权限问题,我们所在前端端口只能让开放的一些机器来访问,除了我们自己的前端器会访问以外,其实还有其他的一些团队,会过来直接写脚本请求我们的数据。我们nginx里面直接用了这两个,这样我不需要在Go里面做,前面就可以直接用nginx做了简单的负载均衡。要不要nginx,完全取决于自己业务的场景。因为在这个场景中,加了nginx也只是给运维稍微增加了负担,但是ip限制和负载均衡不需要重新开发了,之前没有用因为它没有在里面起到任何作用,而且之前是对外的服务,不需要有任何的限制,任何人都可以过来请求。

开源的改变

我们考虑开源。在去年11月份的时候,我们开源了系统,系统有66%代码是用Golang写的。我们有两个问题需要解决,第一个问题第三方依赖的问题,我们开源主体方案没有用到我们自己的内部依赖包,这些第三方的组件,我们应该如何维护它,我当时和很多人交流过,这种方式也比较多,但是他们各有各的优点和缺点,几乎没有一个非常完美的方案,能解决到依赖里面再套依赖,以及多层依赖关系,至少我没有找到,既然没有的话,就选择最大众化,最简单的方案,用这个方式来解决。

在我们整个服务里面,我们自己开发了几个服务,一共有五个。我们当时考虑过,如果让用户部署五个服务,即使我们写好了脚本,部署起来在每个用户端操作系统不同,CPU位数不同等等,都会出各种各样的问题。排查起问题来,不知道排查哪一个服务,对于我们这些开发者来说,我们排查问题的时候,也会根据日志一个服务一个服务去找。我们考虑到,我们把所有的服务打成一个ALL in One一个包。在实际交流试用中,我们了解到有很多人没有选择All in One而选择这五个服务独立部署。

我们开源有五个月,有很多人想让我们把模糊查询以及过滤开源出来。模糊查询我们做的非常简单,我们用了一个数据库,有并发能力。我们先把我们需要模糊查询的分词给分出来,放到数据库里面,在数据库里面我就可以操作,我们平常用到的模糊查询关键词,也就是几十亿左右,几十亿的量做一个操作,那简直太简单了,查到之后就知道关键词,拿到关键词之后,接下来的方案就是一个用多个关键词查询多天的场景,用多个关键词和单个关键词是一样的。多个关键词去查询和用多天查询是一样的,每个关键词分一个Goroutine去查询,就可以解决问题了。

总结回顾

首先Go的开发体验比较好,性能比较高,服务很稳定,我们除了线上有一次事故之后,好像就再也没有过。我们线上是用自己写的做监控,如果它挂掉就会自动拉起来,当然这是一种比较low的方式,因为它可能没有挂,但是它的确死掉了。可以满足大部分的需求场景,GO语言程序开发需要在代码可读性和性能之间做平衡取舍,应用程序并发模型需要在控制之内。我们有很多人在群里面问连接池以及对象池,连接池我们不说,因为很多客户端都会实现连接池这个功能,我们考虑对象池。对象池优点的确很大,因为它可以复用对象减轻压力,这是最核心的功能。复用对象解决了gc压力,但还有一个代码可读性的问题,引进对象池,对象池和业务没有关系,你要看对象池怎么做,代码可读性会非常差。还要说的是,对象池这种解决方案,在Go1.2的时候,用起来很爽,但是目前为止1.4到1.7的时候,对象池这种方案已经远远用不到了,因为gc已经不是那么明显。除非在非常极端的情况下,我们可能会用到这种非常极端的方式解决问题,但是我想大部分的公司都不太会遇到这种问题。我们知道Go在开发安卓,我们现在用的最多就是它和c++以及c的配合然后在用CGO引入到GO,谨慎与其他语言合用,即使对语言都非常熟,你也并不知道他们两个结合起来说不定引发一个问题,可能是你永远解决不了的问题。要合理引进第三方解决方案,在运维成本和系统维护成本要做平衡。





本文来自:51CTO博客

感谢作者:mob604756f0bbf4

查看原文:Go 在百万亿级搜索引擎中的应用


返回前面的内容

相关阅读 >>

zookeeper 的 Golang 客户端

Golang 能做前端吗?

Golang的zap怎么使用

2021-03-06:Go中,公共变量是协程安全吗?赋值操作是原子的吗?为什么?

手撸Golang 基本数据结构与算法 堆

使用Go defer时要注意这两处!

Golang如何设置时间

聊聊dubbo-Go-proxy的client

Golang判断js文件是否存在

Golang 中const是什么

更多相关阅读请进入《Go》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...