我为何从python转向go

应puppet大拿刘宇的邀请,我去西山居运维团队作了一个简短分享,谈谈为何我要将咱们的项目从python转向go。php

坦白的讲,在一帮python用户面前讲为何放弃python转而用go实际上是一件压力蛮大的事情,语言之争就跟vim和emacs之争同样,是一个永恒的无解话题,稍微不注意就可能致使粉丝强烈地反击。因此我只会从咱们项目实际状况出发,来说讲为何我最终选择了go。java

为何放弃python

首先,我其实得说说为何咱们会选择python。在我加入企业快盘团队以前,整个项目包括更早的金山快盘都是采用python进行开发的。至于为何这么选择,当时的架构师葱头告诉我,主要是由于python上手简单,开发迅速。对于团队里面大部分彻底没服务端开发经验的同窗来讲,python真的是一个很好的选择。python

python的简单高效,我是深有体会的。当时私有云项目也就几个程序员,可是咱们要服务多家大型企业,进行定制化的开发,多亏了python,咱们才能快速出活。后来企业快盘挂掉以后,咱们启动轻办公项目,天然也使用python进行了原始版本的构建。mysql

python虽然很强大,但咱们在使用的时候也碰到了一些问题,主要由以下几个方面:nginx

  • 动态语言c++

    python是一门动态强类型语言。可是,仍然可能出现int + string这样的运行时错误,由于对于一个变量,在写代码的时候,咱们有时候很容易就忘记这个变量究竟是啥类型的了。程序员

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

    上面说到的这些,静态语言在编译的时候就能帮咱们检测出来,而不须要等到运行时出问题才知道。虽然咱们有很完善的测试用例,但总有case遗漏的状况。因此每次出现运行时错误,我内心都想着若是能在编译的时候就发现该多好。sql

  • 性能docker

    其实这个一直是不少人吐槽python的地方,但python有它适合干的事情,硬是要用python进行一些高性能模块的开发,那也有点难为它了。

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

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

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

  • 同步网络模型

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

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

  • 异步网络模型

    tornado的网络模型是异步的,这意味着它不会出现django那样由于外部服务不可用致使这个服务没法响应的问题。话说,比起django,我但是很是喜欢tornado的,小巧简单,之前还写过几篇深刻剖析tornado的文章了。

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

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

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

  • 开发运维部署

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

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

  • 代码失控

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

    虽然这也有咱们自身的缘由,譬如没好的代码review机制,没有好的项目规范,但我的感受,若是一个程序员没通过良好的编码训练,用python很容易就写出烂的代码,由于太自由了。

    固然,我这里并非说用python没法进行大型项目的开发,豆瓣,dropbox都是很好的例子,只是在咱们项目中,咱们的python代码失控了。

上面提到的都是咱们在实际项目中使用python遇到的问题,虽然最终都解决了,可是让我愈发的以为,随着项目复杂度的增大,流量性能压力的增大,python并非一个很好的选择。

为何选择go

说完了python,如今来讲说为何咱们选择go。其实除了python,咱们也有其余的选择,java,php,lua(openresty),但最终咱们选择了go。

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

go并非完美,一堆值得咱们吐槽的地方。

  • error,好吧,若是有语言洁癖的同窗可能真的受不了go的语法,尤为是约定的最后一个返回值是error。项目里面常常会充斥这样的代码:

    if _, err := w.Write(data1); err != nil { returun err } if _, err := w.Write(data2); err != nil { returun err } 

    难怪有个梗是对于一个需求,java的程序员在写配置的时候,go程序员已经写了大部分代码,可是当java的程序员写完的时候,go程序员还在写err != nil

    这方面,errors-are-values却是推荐了一个不错的解决方案。

  • 包管理,go的包管理太弱了,只有一个go get,也就是若是不当心更新了一个外部库,颇有可能就致使现有的代码编译不过了。虽然已经有不少开源方案,譬如godep以及如今才出来的gb等,但毕竟不是官方的。貌似google也是经过vendor机制来管理第三方库的。但愿go 1.5或者以后的版本能好好处理下这个问题。

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

  • 泛型,虽然go有inteface,但泛型的缺失会让咱们在实现一个功能的时候写大量的重复代码,譬如int32和int64类型的sort,咱们得为分别写两套代码,好冗余。go 1.4以后有了go generate的支持,但这种的仍然须要本身根据go的AST库来手动写相关的parser,难度也挺大的。虽然也有不少开源的generate实现,但毕竟不是官方的。

固然还有不少值得吐槽的地方,就不一一列举了,可是go仍旧有它的优点。

  • 静态语言,强类型。静态编译能帮咱们检查出来大量的错误,go的强类型甚至变态到不支持隐式的类型转换。虽然写代码感受很别扭,但减小了犯错的可能。
  • gofmt,应该这是我知道的第一个官方提供统一格式化代码工具的语言了。有了gofmt,你们的代码长一个样了,也就没有花括号到底放到结尾仍是新开一行这种蛋疼的代码风格讨论了。由于你们的代码风格同样,因此看go的代码很容易。
  • 天生的并行支持,由于goroutine以及channel,用go写分布式应用,写并发程序异常的容易。没有了蛋疼的callback致使的代码逻辑割裂,代码逻辑都是顺序的。
  • 性能,go的性能可能赶不上c,c++以及openresty,但真的也挺强悍的。在咱们的项目中,如今单机就部署了一个go的进程,就彻底可以胜任之前200个python进程干的事情,并且CPU和MEM占用更低。
  • 运维部署,直接编译成二进制,扔到服务器上面就成,比python须要安装一堆的环境那是简单的太多了。固然,若是有cgo,咱们也须要将对应的动态库给扔过去。
  • 开发效率,虽然go是静态语言,但我我的感受开发效率真的挺高,直觉上面跟python不相上下。对于我我的来讲,最好的例子就是我用go快速开发了很是多的开源组件,譬如ledisdb,go-mysql等,而这些最开始的版本都是在很短的时间里面完成的。对于咱们项目来讲,咱们也是用go在一个月就重构完成了第一个版本,并发布。

实际项目中一些Go Tips

到如今为止,咱们几乎全部的服务端项目都已经转向go,固然在使用的时候也遇到了一些问题,列出来算是经验分享吧。

  • godep,咱们使用godep进行第三方库管理,可是godep我碰到的最大的坑就是build tag问题,若是一个文件有build tag,godep颇有可能就会忽略这个文件。
  • IO deadline,若是能本身在应用层处理的都本身处理,go的deadline内部是timer来控制,但timer内部采用一个array来实现的heap,全局共用一个锁,若是大并发量,而且timer数量过多,timeout变更太频繁,很容易就引发性能问题。
  • GC,这个前面也说了,多用内存池,对象池,另外,我还发现,若是对象的生命周期跟goroutine一致,对性能的提高也不错,也在go的group问过相关问题,你们猜想多是由于一些对象实际上是在goroutine的8k栈上面分配的,因此一块儿回收没有额外GC了。
  • Go gob,若是要作RPC服务,gob并非一个很好的选择,首先就跟python的pickle不通用,而后为了作不一样系统的数据传入,任何包都必须带上类型的详细信息,size太大。go里面如今还没一套官方的RPC方案,gRPC貌似有上位的可能。

总结

虽然我如今选择了go,可是并不表示我之后不会尝试其余的语言。语言没有好坏,能帮我解决问题的就是好语言。但至少在很长的一段时间,我都会用go来进行开发。Let' go!!!

做者:siddontang 连接:https://www.jianshu.com/p/afa14e631930 來源:简书 简书著做权归做者全部,任何形式的转载都请联系做者得到受权并注明出处。
相关文章
相关标签/搜索