說說這篇「我爲何從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:怪我咯?

相關文章
相關標籤/搜索