Go 語言剛剛度過了它的十週年記念日。而要說我與 Go 的緣分,也一樣始於十年前(2007 年)。這十年,是 Go 成長的十年,一樣也是我成長的十年。程序員
2007 年 2 月,我創建了金山實驗室,主攻分佈式存儲方向。這是我職業生涯相當重要的一個轉折點。此前我已經作了 7 年的 WPS Office 辦公套件的研發。這對我有兩個重要的挑戰:一個是角色轉換上的,從作軟件研發的架構師到作業務的產品經理;一個是技術領域變化上的,從單機的桌面軟件開發到分佈式的後端服務器開發。golang
我第一個面臨的問題是技術選型。幾乎沒有做任何糾結,咱們選擇了 Java 做爲主體的開發語言。做爲初入後端開發領域的團隊來講,這只是一個從務實角度出發的選擇。但 TrustNo1 在程序員雜誌發表的《一場茶杯裏的風暴》一文讓我認識了 Erlang 語言,並深深被 Erlang 的編程思想所打動。我意識到這種編程思想將會極大釋放服務端編程的生產力。爲了推進服務端最佳實踐的探索,也爲了進一步推進更多人瞭解和研究 Erlang,我發起了 ECUG 社區。ECUG 到如今已經十年。這十年咱們風雨無阻,每一年年末都會舉行一場 ECUG Con 大會,和來自各行各業的技術大拿們齊聚一堂,你們一塊兒分享這一年來服務端開發的實踐心得。編程
儘管咱們選擇了 Java,可是隨後咱們也作了小範圍使用 Erlang 進行開發的嘗試。在 2008 年咱們啓動了一個用 Erlang 來編寫存儲系統(極簡版)的業餘項目。此次的嘗試讓我對 Erlang 有了進一步的判斷:儘管 Erlang 的編程思想很好,可是 Erlang 語言自己並不適合大型的工程項目。後端
到了 2009 年 3 月,也就是我加入盛大創新院重啓分佈式存儲項目時,我面臨了第二次技術選型。這一次我選了最難的一條路:用 C++ 來開發。這是一條艱苦的路。促使我作出這一選擇的最大緣由是,我很但願可以製造一個新輪子,它應該既有 Erlang 編程思想的優點,又能夠克服 Erlang 語言的劣勢。若是這事可以幹成,它將是一件偉大的創舉,全部的服務端開發人員都將受益於此。因而,在心懷滿腔熱血之下,一個名爲 CERL 的項目誕生了。在咱們存儲的第一個版本期間,實際上咱們花費在 CERL 庫的時間遠超過了作存儲自己,不管從代碼量仍是花費的精力來講都是如此。服務器
CERL 項目經歷了 2 個大的版本。CERL 1.0 徹底遵循 Erlang 的編程思想,主要編程範式以下:網絡
能夠啓動任意多的進程(這裏進程是抽象的概念,實現上是纖程 / 協程),進程數上限只受限於內存大小。架構
每一個進程有進程郵箱,相互發消息經過進程郵箱。併發
消息分同步消息和異步消息,同步消息會阻塞等待對方返回消息。異步
網絡服務器是單進程模型(就是你們理解的單線程模型),一次只處理一個消息。分佈式
沒有鎖。
…
基於 CERL 1.0 的編程模型,咱們實踐中發現了這樣一些問題:
在網絡服務器 A、B 相互給對方發送同步消息時會發生「死鎖」。由於雙方可能都正在處理消息中而沒法及時響應,這種「死鎖」最終表現爲超時(可是實際上超時更可怕,是服務器性能的殺手)。
解決「死鎖」的方法是把同步消息改成異步,可是這對編程複雜性帶來很大的影響,程序語義變得晦澀。本質上,這是回到了傳統的異步編程模型,放棄了 Erlang 編程模型最大的優點。
「沒有鎖」的假設代價太高:在網絡服務器的單個處理響應時間較長(存儲服務必然如此)時,必然但願啓動獨立的進程來響應單個請求。可是各個處理請求的進程之間須要共享網絡服務器的狀態,這就意味着須要有鎖(除非網絡服務器是無狀態的,可是很不幸存儲服務器必然是帶狀態的)。要麼放棄性能串行化地處理請求,要麼有鎖 —「沒有鎖」,想說愛你不容易。
…
在發現「死鎖」問題後,咱們馬上對咱們整個編程模型進行了反思,決定重構整個編程模型,修改後的 CERL2.0 要點以下:
能夠啓動任意多的進程(這裏進程是抽象的概念,實現上是纖程 / 協程),進程數上限只受限於內存大小。
沒有進程郵箱。
進程之間只有同步消息(要發送異步消息,用啓動一個新的進程併發送同步消息來達到一樣的效果)。
網絡服務器是多進程模型,每一個請求都由一個獨立的進程來處理。
有鎖。
兩個版本的編程模型的關鍵差別在於拒絕鎖仍是拒絕異步消息。CERL 1.0(也就是 Erlang 編程模型)中拒絕鎖,而 CERL 2.0 拒絕異步消息。基於 CERL 2.0 咱們實現了分佈式存儲的第二版、第三版。事實證實,徹底杜絕了異步消息這個概念後,這個版本的服務器編程模型心智負擔小了不少。
而後,如你們所知,後來 Go 語言就發佈了。咱們團隊一看,在服務器編程模型這一點上,CERL 2.0 和 Go 語言竟然如出一轍(包括全部細節上的決策)。有一次團隊聚餐談起這事時,道哥(李道兵)說了一句:咱們趕忙把 CERL 開源吧,否則等 Go 流行起來它就沒機會了。我還真和創新院院長陳大年談了 CERL 開源的事情,大年說:沒問題,你發個郵件申請吧,留個憑證。然而我最終沒有發出這封郵件。在嘗試用 Go 寫了一週的代碼後,我內心已經有告終論:我並不打算讓 CERL 面世。由於有人已經把它的目標完成了,並且遠超預期。
因而,2011 年 6 月,咱們離開盛大創新院創辦七牛雲的時候,我面臨了第三次技術選型。這一次,我很堅定地選擇了 Go 語言。爲此我還專門給團隊發了一封郵件,郵件中有一段是這麼說的:
在創業過程當中咱們會面臨不少選擇,也會有不少選擇後來會被證實是錯的,可是今天我能夠肯定的是,選擇 Go 將會成爲咱們最正確的選擇。
在選擇了 Go 語言後,考慮到 Go 仍然是一門十分小衆的語言,咱們開始有意識地培養 Go 中國社區。爲了讓更多人可以知道 Go,加入 Go 的行列,咱們作了不少工做。咱們啓動了《Go 語言編程》一書的編寫工做,並最終和 Go 1.0 版本同步發佈。2012 年 2 月,我首次在公開場合說:Go 會超過 C、Java,成爲最流行的語言。這一年我處處宣講,作了不下十場的 Go 語言講座,平均每月有一場。講得最多的一個 PPT 是《Go,Next C》這篇,它基本上算我對 Go 的革命性到底在哪裏的一個總結。對於一個初創公司來講,爲一個並不屬於本身業務的技術這麼花時間去宣傳,有些人可能會以爲比較不可理解。可是實際上有三個理由支撐咱們這麼作:
Go 真的是一門革命性的語言,它的流行將對產業發展具重大意義。
Go 仍然是一門小衆語言,而咱們不止要招 Go 程序員,更重要的是要說服他們相信 Go 語言是有遠大前景的專業技能方向。
七牛的用戶是程序員,咱們須要創建在用戶心目中的專家形象。
七牛雲的起步第一個業務是雲存儲,咱們選擇了徹底用 Go 來實現咱們的存儲系統。這是全球第一個用 Go 寫的雲存儲,也是第一個用 Go 寫的雲服務。而到了 2014 年,在咱們決定進入大數據領域時,咱們再一次面臨技術選型問題。坦白說,咱們仍是糾結了一段時間的。從生態來講,選擇 Java,或者某種 JVM 平臺的語言(好比 Scala)有很是顯著的優點。尤爲對於咱們大數據業務的負責人陳超這個 Scala 的狂熱粉絲(八卦一下:陳超的網絡 id 是 CrazyJVM),選擇 JVM 平臺彷佛更加是毋庸置疑的選擇。那麼咱們糾結什麼呢?咱們認爲將來 Go 會佔領整個基礎設施領域,而大數據無疑是其中極具關鍵意義的一個領域。因此面向如今作選型,仍是面向將來作選型,這是一個問題。
說到這裏我提一個有意思的細節。在陳超剛加入七牛的時候,我對他說:「無論將來你會用什麼語言,可是進入七牛必需要會寫 Go。」因而他被我逼着寫了一個月的 Go 代碼。一個月後我問他感受如何,他說:「可以理解爲何你推薦 Go 了,寫了 Go 代碼後不想回去寫 Scala 代碼。」不久以後,在他啓動 Pandora 大數據平臺項目時,就碰到了我說的選型問題。在作了很是細緻的思考以後,他決定用 Go 作 Pandora。這對我來講是一個意料以外的決策。由於我本身在這個事情上並無傾向性,而陳超我以爲他極可能仍是會優先選 Scala。就在最近(國慶節前)某次聊天中,陳超回顧起這件事,總結了一下他爲何選擇了 Go:極低的學習成本,極低的心智負擔。若是用 Scala,新人入職要培訓,還要擔憂寫出糟糕的 Scala 代碼。可是用 Go 新人不培訓直接上崗,幾回 Code Review 完後基本就可以知道怎麼寫出質量不錯的 Go 代碼了。
十年過去,Go 已經再也不是一門小衆語言。愈來愈多人在用 Go,喜歡上 Go。不記得是何時了,可是某一天經過 Google Trends 搜索 golang 發現全世界 Go 最火的地區是在中國時,那一刻真的很開心。
下一個十年會怎樣?我知道有一些人很指望 Go 語言特性的迭代。可是若是你抱有這種想法可能會失望,由於下一個十年 Go 不會發生太大的變化。對遠期需求變化的預測和把控能力,是 Go 的最大魅力之一。這一點上可以和 Go 相比的是 C 語言(C 語言不一樣版本的規範差別極少),但由於 Go 要解決的問題更多,作到這一點實際上也更難。下一個十年 Go 仍然會繼續深耕服務端開發的生態,同時積極探索其餘潛在的應用市場。