瞎扯淡 我们为什么会选择 Golang

shawnyu · 2015年08月09日 · 最后由 rasefon 回复于 2015年08月11日 · 9728 次阅读

前情提要: https://ruby-china.org/topics/26785

其实在用 Golang 重写我们的 worker 系统之前是做过很多调研的。 而真正让我们下定决心的是 Parse 的一篇文章。How We Moved Our API From Ruby to Go and Saved Our Sanity。 文中讲了 Facebook 的 Parse 团队为什么选择 Golang 代替 Ruby。 我翻译下关键几点:

Parse 面临的问题

Parse 跟暴漫的技术栈比较相似: 服务器 Unicorn,部署使用 Capistrano。在高流量面前很多问题都被指数级放大,在每次部署的时候 app server 重启都要很长时间。并且 Unicorn 的重启并不是真正的 ‘graceful’(这个我们也有同感,重启之后服务会中断 10 秒左右,另外 Parse 在用 Golang 重构之后还写了一个库 ‘Grace’ 专门解决重启中断服务的问题)。 这样造成的服务中断带来的影响非常不好。

另外像 Unicorn 这样 每个进程同时只能处理一个请求(one process per request),不仅仅是极度的浪费,而且如果某一个 action 突然变慢 将会占满整个 worker poll。 导致整个服务都不可用。

方案选择

Parse 在 EventMachine,JRuby,c++, c#, golang 之前做了对比,并最终选择了 go。

EventMachine

Parse 使用了 EventMachine 实现他们的 push 服务,在使用过程中,由于相关的 gem 成熟度不够,总是碰到一些奇怪的 bug。还是生态的问题,导致实现某些功能的时候无轮子可用。

JRuby

Parse 现在是 Ruby 实现,所以 JRuby 就是正确的选择? JRuby 基于 JVM 可以并发处理大量请求,看起来非常不错。 不是的!JRuby 缺乏各种异步库的支持。Parse 担心为了应对业务的增长,还要第二次重构:从 JRuby 到 JAVA。 并且 Parse 的工程师团队是在不想在 JVM 中部署并调节各种参数。

文中还提到了 Twitter 团队在迁移到 Scala 之前对 JRuby 的调查:Twitter 对 MRuby 做了很多工作,包括自己写了一个 GC 工具。本来在 MRuby 上工作很好,效率很高的库,到了 JRuby 上 就不好使了(说白了就是各种库不成熟,生态系统太重要!)迁移过去之后虽然 JRuby 本身快了 2 倍,但是其他东西都面临各种问题,有点得不偿失

That's not a fault of JRuby; it's just that at the moment the surrounding ecosystem is still kind of immature. CRuby's was too; we put a lot of investment into it, which we can now take advantage of, and we would have to do the same for JRuby.

暴漫团队说实话没人用过 JRuby,而且经过这么长时间的发展,生态应该会好很多,但是尽管这样,迁移之后 仍然有很多工作要做吧?

C++

Parse 团队有很多 c++ 的开发经验, 不过 c++ 代码难以 debug 和维护。 就我个人而言 严重觉得 c++ 肯定不是 web 项目的选择。 另外缺乏 web 相关各种库支持。

C#

c#有非常好的并发模型支持。不过在 Linux 上。。。还是看下一条吧

Golang

Golang 语言效率高,语言层面支持并发,语法非常简单 易于上手,并发模型容易理解。(我们重构之前只给团队讲了一个小时的语法,然后给了一些些好的 worker 作为参考,然后大家都可以顺利的重构 2-3 个 worker,在两周的时间内)。 应该是 worker 系统的最佳选择。

最后回到暴走漫画的问题

大家的疑问更多是 既然都是 io 消耗,为什么 golang 会快这么多。 我试着解释下(水平有限): golang 静态语言 不需要类型推断 抛弃了各种语法糖,在语言效率层面上快上不少,另外在数据库 io 方面 gorm 没有 ActiveRecord 的黑魔法,自然会快很多。

暴漫的 worker 系统瓶颈在高并发峰值,一旦抗不过去后面就会持续累积。 而 golang 在单个任务上虽然只有 5 倍快,但是良好的并发机制,使 job 的执行速度飞快。 而在原系统中 每台机器 150 并发跑慢之后,有些 100ms 的任务都等到 23s 之久。

单个任务执行速度快 5 倍, 并发再快 5 倍,所以从 10 台减到 1 台 而 golang 机器还游刃有余是合理的。

Parse 在重构的时候考虑的是能容纳当前业务峰值的 10 倍的方案。我觉得我们在挑选方案的时候 也要有这种意识。虽然有些方案确实也可以解决目前的困境,但是对以后的架构调整是否有益,或者说兼容。

另外像 Unicorn 这样 每个进程同时只能处理一个请求(one process per request),不仅仅是极度的浪费,而且如果某一个 action 突然变慢 将会占满整个 worker poll。 导致整个服务都不可用。

为什么不考虑换线程服务器比如 puma。

em 常年不更新也变成一个坑了。

#1 楼 @rubyu2 暂时我们 api 服务状态非常好 还没有重构的必要。

#2 楼 @shawnyu 能说说为什么选择用 gorm ? 有没有对比其他的 ORM ?

#2 楼 @shawnyu 还有,应该是有很多以前 model 里面的逻辑需要用 golang 再写一遍,那么就是要维护两份不同语言的相同业务逻辑,这个事情有什么可以分享的经验?

C# 在 Linux 上面运行得相当好相当稳定,很多年前就是这样了。 参考 http://www.mono-project.com/

#4 楼 @ch3n 我们现在是根据业务需要重写,没有保持一致。 另外我觉得最佳实践应该是 有统一数据层,然后不同的地方在不同的引用出做修改, 比如说某一个服务引用了一个 model 并且需要新加一个字断之类的 应该在这个服务自己的数据库新建表做记录。

gorm 是因为 star 比较多吧?

@shawnyu 从软件角度来看,换语言,换环境,的确能够提升不少的性能。 如果换种出发点,从硬件角度,既然 IO 消耗如此严重,是否评估过升级硬盘为 SSD 硬盘呢 😄 这样,软硬的提升,会再次带动整个系统的效率提升。

#8 楼 @luolinae86 硬件提升却是是一方面,这次主要是硬件性能不能被充分利用, 另外,ucloud 给我们的机器全部都是 ssd 的。

JRuby 基于 rvm ?

JRuby 基于 JVM

使用 Go 解决了问题,说明暴走漫画做了正确的事情,且能很好的 handle 新语言,应该点赞

关于 EventMachine,如果记得没错的话,虽然是 Reactor Pattern,但是 EventMachine 是基于线程池实现的,且默认 thread pool 大小是 20

https://github.com/eventmachine/eventmachine/blob/master/lib/eventmachine.rb#L1091

其他关于 EventMachine 的文档,以及使用,debug 吐槽也都是对的,我想 Ruby 跟 Go 实现的效率比较,除了语言效率的差别,区别更大的是 EventMachine 跟 Go 的并发处理能力的差别么?

nodejs 呢?

@shawnyu

由于 markdown 标题可以这么写 ## ... ##

所以 C# 中的 # 漏掉了, 像下面这么写就不会漏了:

## C\#

#14 楼 @luikore 囧 改过之后就是 "c \" 了 我用了全角符号改了下

#13 楼 @kikyous 没研究过,对 node 无感啊。 逃)

#15 楼 @shawnyu 啊... 应该是

#### C\# ####

C#

:)

@ch3n 撇开 gorm 功能应该是最多的不说。。。他有个强大的插件系统,你可以基于他写各种插件,例如我们基于它写的类似 rails admin/active admin 类似的后台管理软件 http://getqor.com/ ;)

为什么不是选择 scala?貌似 scala 非常接近 ruby。

#18 楼 @zhangjinzhu 谢谢 jinzhu 老师亲自回复

Parse 的 Video 放出来了 http://blog.parse.com/learn/moving-our-api-from-ruby-to-go-the-video/

P.S. 看这个视频,对英语听力有特殊要求,我还在适应中。

我前面一个项目在遇到性能问题的时候,也是用 golang 来解决的 中间尝试 puma/thin/rainbow 等等,大小坑无数 em 在纯 http 相关的性能不错,一但有数据库连接,就坑无数了 最近的高并发语言里,我就会点 golang...所以就选 golang,效果相当不错

#19 楼 @jimrokliu 如果用了 scala 很可能所有 worker 都要你自己重构了!

我想知道如果用 erlang/otp 会不会效率更高。

gingerhot 如何使用 Rails 集成开发 Go API 提及了此话题。 01月29日 05:14
需要 登录 后方可回复, 如果你还没有账号请 注册新账号