说说这篇「我为何从python转向go

做者 CMGS 2015.05.17 15:47*
写了7891字,被143人关注,得到了97个喜欢

说说这篇「我为何从python转向go」

字数3748 阅读24227 评论21 

恩看了这篇我为何从python转向go,看来做者也是 KSO 轻办公/企业快盘团队的。做为快盘从无到有时期的工程师之一(老是被潇洒哥说他们改我留下的 bug ),又刚好是 Python/Go 双修(大雾其实我是 Rust 党),其实一开始我是拒绝的,duang duang duang,那就随手写一点把。php

一段段来吧,首先做者说 Python 是动态语言java

python是一门动态语言,不是强类型系统。对于一个变量,咱们有时候压根不知道它是什么类型,而后就可能出现int + string这样的运行时错误。node

在python里面,能够容许同名函数的出现,后一个函数会覆盖前一个函数,有一次咱们系统一个很严重的错误就是由于这个致使的。python

事实上,若是是静态检查,pylint 和 pyflakes 是能够作这件事的,虽然不能和 go 那种静态编译型语言比,但也足够了。若是没记错的话,阿通当年是要求全组都在提交前作静态检查的。我认为这种问题更多的应该是人员素质上来避免,毕竟葱头也说过,代码本身写的就要多回头看看,看能不能重构,能不能作更好。不是说偷懒不行,可是从中得出 Python 动态特性太灵活,Python:怪我咯?mysql

另外,函数做为第一对象,在 Python 中是 feature,Go 要写个 mock,简直虐得不要不要的。nginx

其实这个一直是不少人吐槽python的地方,不过想一想,python最开始是为了解决啥问题而被开发出来的?咱们硬是要将他用到高性能服务器开发上面,其实也是有点难为它。c++

若是没记错,不管是轻办公仍是快盘,是重 IO 不重 CPU,最大耗时是数据块加密那块,我在的时候是 Java 写的。另外高性能服务器选 Go 也是虐得不要不要的,各类当心翼翼避免 GC。大多数极端状况下,pypy 的性能足矣胜任了,我认为这不算充分条件。git

python的GIL致使致使没法真正的多线程,你们可能会说我用多进程不就完了。但若是一些计算须要涉及到多进程交互,进程之间的通信开销也是不得不考虑的。程序员

其实,Python 有宏能够绕开这个 GIL,可是呢架构设计得好其实能够避免的,到异步那块我会说。github

无状态的分布式处理使用多进程很方便,譬如处理http请求,咱们就是在nginx后面挂载了200多个django server来处理http的,但这么多个进程天然致使总体机器负载偏高。

但即便咱们使用了多个django进程来处理http请求,对于一些超大量请求,python仍然处理不过来。因此咱们使用openresty,将高频次的http请求使用lua来实现。可这样又致使使用两种开发语言,并且一些逻辑还得写两份不一样的代码。

若是推测没错,大家如今还在用五年前写的 Gateway?那个基于 django route 的流量分发层?四年前我离开的时候已经小范围的使用 Flask+Gevent Demo 测试过了,不管是性能仍是负载都比同步模型的 django 有优点。若是仍是 django 这套的话,我只能说比较遗憾,毕竟当年金山新员工大赛头牌就是我和几个小伙伴写的实时同步在线文档编辑系统,用的就是这套技术。

所以这是个工程问题,并不是语言问题。 Python 提供给了你了这么多工具,硬要选一个传统的,Old fashion 的,Python:怪我咯?

django的网络是同步阻塞的,也就是说,若是咱们须要访问外部的一个服务,在等待结果返回这段时间,django不能处理任何其余的逻辑(固然,多线程的除外)。若是访问外部服务须要很长时间,那就意味着咱们的整个服务几乎在很长一段时间彻底不可用。

为了解决这个问题,咱们只能不断的多开django进程,同时须要保证全部服务都能快速的处理响应,但想一想这实际上是一件很不靠谱的事情。

同步模型并不是不行,由于 overhead 足够低,不少业务场景下用同步模型反而会取得更好的效果,好比豆瓣。同步模型最大的问题是对于 IO 密集型业务等待时间足够长,这时候须要的不是换语言 ,而是提醒你是否是架构要改一下了。

虽然tornado是异步的,可是python的mysql库都不支持异步,这也就意味着若是咱们在tornado里面访问数据库,咱们仍然可能面临由于数据库问题形成的整个服务不可用。

tornado 是有这个问题,可是 gevent 已经解决了。我在 node.js 的某问题下曾经回答过,对于 node 而言,能选择的异步模型只有一个,而 Python 就是太多选择了。另外 pypy+tornado+redis 能够随意虐各类长链接的场景,好比我给我厂写过的一个 push service。

其实异步模型最大的问题在于代码逻辑的割裂,由于是事件触发的,因此咱们都是经过callback进行相关处理,因而代码里面就常常出现干一件事情,传一个callback,而后callback里面又传callback的状况,这样的结果就是整个代码逻辑很是混乱。

这个还真不是,若是说没有 ES6 的 JavaScript,可能真有 Callback hell,但这是 Python 啊!Python 早就实现了左值绑定唉,yield 那姿式比某些每天吹的语言不知道高到哪里去了,固然我说的是完整版的 Python3 yield。即使是不完整的 Python 2 yield 用于异步表达式求值也是彻底足够的,tornado 的 gen.coroutine 啊。

同步形态写异步,在 Python 实力强的公司里面早普及了,这是个工程问题,并不是语言问题。固然把这种事怪在 Python 身上,Python:怪我咯?

python没有原生的协程支持,虽然能够经过gevent,greenlet这种的上patch方式来支持协程,但毕竟更改了python源码。另外,python的yield也能够进行简单的协程模拟,但毕竟不能跨堆栈,局限性很大,不知道3.x的版本有没有改进。

不管是 Gevent 仍是 Greenlet 均没修改 Python 源码,事实上这货已经成为了 Py2 coroutine 的标准,加上豆瓣开源出来的greenify,基本上全部的库均可以平滑的异步化,包括 MySQL 等 C 一级的 lib。自从用上这套技术后,豆瓣的 Python dev 各类爽得不要不要的。

当我第一次使用python开发项目,我是没成功安装上项目须要的包的,光安装成功mysql库就弄了好久。后来,是一位同事将他整个python目录打包给我用,我才能正常的将项目跑起来。话说,如今有了docker,是多么让人幸福的一件事情。

而部署python服务的时候,咱们须要在服务器上面安装一堆的包,光是这一点就让人很麻烦,虽然能够经过puppet,salt这些自动化工具解决部署问题,但相比而言,静态编译语言只用扔一个二进制文件,可就方便太多了。

刚好我又是在开发基于 docker 的平台, docker 还真不是用来作部署这事的。首先, Python 是有 virtualenv 这个工具的,事实上对比包管理和包隔离,Python 比 Go 高得不知道哪里去了。Python 跟 Git 谈笑风生的时候, Go 的 dev 们还得考虑我怎样才能使得 import 的包稳定在一个版本上(固然如今有不少第三方方案)。Virtualenv + Pip 彻底能够实现 Python 部署自动化,因此这个问题我认为是,工具链选取问题。毕竟是个十几年的老妖怪了,Python 啥状况没见过啊,各类打包工具任君选择,强行说 Python 部署不方便,Python:怪我咯?

python很是灵活简单,写c几十行代码才能搞定的功能,python一行代码没准就能解决。可是太简单,反而致使不少同窗没法对代码进行深层次的思考,对整个架构进行细致的考量。来了一个需求,啪啪啪,键盘敲完开速实现,结果就是代码愈来愈混乱,最终致使了整个项目代码失控。

曾经知乎有个帖子问 Python 会不会下降程序员编程能力,我只能说这真的很人有关。你不去思考深层次的东西怪语言不行是没道理的,那好,Go 里面 goroutine 是怎么实现的,一个带 socket 的 goroutine 最小能作到多少内存,思考过?任何语言都有本身的优点和劣势,都须要执行者本身去判断,一味的以为简单就不会深刻思考这是有问题的。另外,代码混乱我认为仍是工程上的控制力不够,豆瓣有超过10W行的 Python 实现,虽然不说很完美,大致上作到了不会混乱这么个目标。

还有,C 写几十行搞定的 Python 一行解决这绝对是重大 feature,生产力啊,人员配置啊,招人培养的成本啊,从工程上来讲,Python 在这一块彻底是加分项,不是每一个项目都要求极致的并发,极致的效率,作工程不少时候都是要取舍的。

虽然java和php都是最好的编程语言(你们都这么争的),但我更倾向一门更简单的语言。而openresty,虽然性能强悍,但lua仍然是动态语言,也会碰到前面说的动态语言一些问题。最后,前金山许式伟用的go,前快盘架构师葱头也用的go,因此咱们很天然地选择了go。

Openresty 用 lua 若是按照动态语言的角度去看,还真算不上,顶可能是个简单点的 C。许式伟走的时候大多数都是 CPP,葱头目前我还不知道他创业用的是什么写的,不过他确定没语言倾向。当年不管是 leo 仍是 ufa,一个用 Python 一个用 Java, 他都是从工程实际来选择使用什么样的语言。

error,好吧,若是有语言洁癖的同窗可能真的受不了go的语法,尤为是约定的最后一个返回值是error。

这实际上是 Go style,不管是 go fmt 仍是 error style,Go 实际上是想抹平不一样工程师之间的风格问题。再也不为了一个缩进和大括号位置什么的浪费时间。这种方法并非很差,只是我我的以为没 rust 那种返回值处理友善。

GC,java的GC发展20年了,go才这么点时间,gc铁定不完善。因此咱们仍然不能为所欲为的写代码,否则在大请求量下面gc可能会卡顿整个服务。因此有时候,该用对象池,内存池的必定要用,虽然代码丑了点,但好歹性能上去了。

1.4 开始 go 就是 100% 精确 GC 了,另外说到卡顿啊,彻底和你怎么用对象有关,能内联毫不传引用大部分场景是彻底足够的,这样 gc 的影响程度会最低。实在想用池……只能说为啥不选 Java。

天生的并行支持,由于goroutine以及channel,用go写分布式应用,写并发程序异常的容易。没有了蛋疼的callback致使的代码逻辑割裂,代码逻辑都是顺序的。

这是有代价的,goroutine 的内存消耗计算(固然1.3仍是1.4开始获得了很大的改善,内存最小值限制已经没了),channel 跨线程带来的性能损耗(跨线程锁),还有对 goroutine 的控制力几乎为 0 等。总之这种嘛,算不上是杀手级特性,你们都有,是方便了一点,但也有本身的弊端。好比咱们用 go 吧,常常就比较蛋疼 spawn 出去的 goroutine 怎么优美的 shutdown,反而有时候把事情作复杂化了。

性能,go的性能可能赶不上c,c++以及openresty,但真的也挺强悍的。在咱们的项目中,如今单机就部署了一个go的进程,就彻底可以胜任之前200个python进程干的事情,并且CPU和MEM占用更低。

我不严谨的实测大概 gevent+py2 能达到一样逻辑 go 实现的 30%~40%,pypy+tornado 能达到 80%~90%,混合了一些计算和链接处理什么的。主要仍是看业务场景吧,纯粹的 CPU bound 固然是 go 好,纯粹的 IO bound 你就是用 C 也没用啊。

运维部署,直接编译成二进制,扔到服务器上面就成,比python须要安装一堆的环境那是简单的太多了。固然,若是有cgo,咱们也须要将对应的动态库给扔过去。

咱们如今根据 glibc 所处的 host 版本不一样有2套编译环境,看上去是部署简单了,编译起来坑死你。另外虽说 disk 便宜,这几行代码就几M了,集群同步部署耗时在某些状况下还真会出篓子。

开发效率,虽然go是静态语言,但我我的感受开发效率真的挺高,直觉上面跟python不相上下。对于我我的来讲,最好的例子就是我用go快速开发了很是多的开源组件,譬如ledisdb,go-mysql等,而这些最开始的版本都是在很短的时间里面完成的。对于咱们项目来讲,咱们也是用go在一个月就重构完成了第一个版本,并发布。

go 的开发效率高是对比 C,对比 python,大概后者只须要3天吧……

总之,Go 不是很差,Python 也不是不行,作工程嘛,无外乎就是考虑成本,时间成本,人力成本,维护成本等等。 Go 和 Python 互有千秋,就看取舍了。固然必定要说 Python 不行,Python:怪我咯?

相关文章
相关标签/搜索