標籤:獨立 erro 多公司 hub 特性 不同的 允許 控制 好的
摘自:http://www.infoq.com/cn/news/2017/06/freewheel-experience-on-go
Go語言是FreeWheel公司目前主要力推的一個方向,在其看來,面向服務的架構的大環境中,Go非常適合做一些功能相對獨立、功能比較明確的微服務的語言。在結合已有的各種程式設計語言,計算架構(如Hadoop、Java、Ruby、C++)的基礎上,FreeWheel把Go語言定位成用來實現輕量級服務或API的預設程式設計語言,將之與用來完成更小粒度工作的Python結合在一起,就構成了FreeWheel的整個技術語言棧 。
FreeWheel在Go上所經曆的“坑”
雖然從2012年Go 1.0發布到團隊相繼採用Go來編寫項目,這中間經曆了大致三年左右的時間,但由於在GC等許多問題的克服上需要Go本身去做一部分迭代,FreeWheel也需要把技術對客戶的影響控制在一個可控的範圍內,因此作為一家B-to-B企業,其採用了更為漸進的方式將Go語言應用到自身的生產平台上。
在這個過程中,FreeWheel也經曆過兩個較為重要的“坑”。
如多數人所知道的一樣,Go語言記憶體回收行程存在一定的缺陷,特別是容易導致整個進程不可預知的間歇性停頓。像某些大型後台服務程式,如遊戲伺服器、APP容器等,由於佔用記憶體巨大,其記憶體對象數量極多,GC完成一次回收周期,可能需要數秒甚至更長時間,這段時間內,整個服務進程是阻塞的、停頓的,在外界看來就是服務中斷、無響應。FreeWheel在使用Go 1.4版本時也遇到過類似問題:廣告預測團隊用Go來實現調度器,平常啟動並執行時候沒有問題,但一旦觸發1.4版本下GC的時候,該系統的downgrade非常厲害,導致任務的堆積非常嚴重,觸發警示,同時其處理性會下降很多,也會影響其他上下遊系統的正常運轉。
於是,FreeWheel在當時主要採取了三種對策:一、並不把Go用在非常關鍵的、對服務進程穩定性要求較高的系統裡;二、引入Kafka之類的能夠持久化的訊息佇列,能夠緩衝和重釋這樣的方式去解決這個問題,使系統能扛住衝擊,並在後面把它消化掉;三、盡量複用已經建立的對象,防止Go頻繁的建立了回收對象。
Go 1.5到1.7版本相繼出來後,GC的系統效能得到不斷改進和持續提升(從秒級到毫秒級)。對於目前FreeWheel內生存環境不太關鍵的系統來說,Go 1.7之後的GC已經可以達到可接受的範圍和程度以內。
很多人都知道,Go語言提供的字典類型並不是並發安全的,此外由於Go語言發展較快,有些內建的資料結構如Map的行為也發生了變化。因為Map為參考型別,所以即使函數傳值調用,參數副本依然指向映射m,所以多個goroutine並發寫同一個映射m。例如,如果map由多協程同時讀和寫就會出現 fatal error:concurrent map read and map write的錯誤。
Go 1.6版本之前Map可以支援並發讀寫,但FreeWheel開發的程式在升級到1.6之後也就發現Map產生了讀寫競爭的問題。
對於這一問題,常用的有兩種解決方案,一是如上所說的加鎖(包括通用鎖和讀寫鎖),二是利用channel序列化處理。FreeWheel的做法也主要靠兩方面,其一是將鎖粒度設計的更細,使得並發的依賴更少;另外是在不同的資料結構中,選擇效能更高的一方。比如array和slice中,前者就是更優選擇。
FreeWheel首席架構師劉昊植認為,並發的時候不能假設Go能完美處理所有的工作,工程師需要結合并借鑒傳統(成熟)的程式設計語言,比如Java或者C對並發的經驗,在動手之前就想清楚並發的規模、鎖的粒度等,並對系統會如何運行有非常明確的設計和理解。
同時,劉昊植也坦言,隨著Go的快速發展階段,FreeWheel內部也有多種不同的聲音,例如營運團隊就會覺得快速發展階段的語言穩定性不夠,它的特性和資料結構會因為版本升級等原因產生很大變化,並且部分變化不能完全保持向前相容。在這個過程中FreeWheel的總結是:不管Go怎樣實現,都要對系統並發做很好的支援,在應用程式層面做保護和控制,這樣才保證這個系統能夠正常的運行。
為什麼FreeWheel沒有全部用系統重寫Go
從編程範式的角度來說, Go語言是變革派,而不是改良派。對於C++、Java和C#等語言為代表的物件導向的思想體系,總體來說全球範圍內許多公司對Go語言的態度更為保守,多數持有限吸收的觀念(這可從中Go的熱度分布情況看出)。
即使FreeWheel在實踐中發現Go比其他類程式設計語言具有許多更為明顯的優勢,如在寫並行上,相比Python這樣的解釋語言要高一個數量級;如前期由Python開發的很多輕量級API,因為全域解釋鎖GIL的關係而面臨著處理序間通訊帶來額外開銷,所以就把輕量級API遷移到Go上;又比如Go在並發上的優勢,適合面向多使用者同時上傳、同時調用API的情境……但其內部團隊也並沒有用Go來重寫全部系統。原因主要有兩點:
第一,FreeWheel有很多已有的演算法實現,想全部切入到Go上會面臨巨大的開銷和成本;第二,相比C或者C++,Go在高效能方面還沒有完全的證明自己。在Web伺服器端,它目前也沒有一個特別好的像RoR、Django、或者PHP的流行架構。對於FreeWheel來說,整個廣告伺服器是不允許出現明顯的downgrade情況(尤其是當GC時),所以對這種非常關鍵的系統,目前還不能完全用Go去寫。所以劉昊植也認為,Go在擴充使用情境層面可能還需要做一些較大變革。
此外,FreeWheel基於Go的Web程式,目前使用的是Gorilla架構。但從Martini、Revel、Gocraft/web等幾款主流架構的使用和評價上看(可以github上的資料做個參考),Go社區還沒有一款處於統治地位的Web架構。如果Go想把它的觸角伸得更長,這可能是其未來發力的一個方向。
但總的來說,Go在高並發、開發效率等特性上的優勢,決定了Go在FreeWheel內的採用程度會越來越深。劉昊植說:“除了一些已有業務依賴於Hadoop、Spark這樣的基礎設施,對於新增的業務和功能,Go語言會是我們的首選。”
另外,FreeWheel希望其使用的程式設計語言是能夠得到跨大陸、跨時區、受所有工程師共同認可的,所以Go或許會是其最好的選擇。這個過程中,FreeWheel也評估過很多其他語言如Scala、Rust等,但最終因為Go在學習成本、統一實踐、社區規模等方面的優勢而勝出。
FreeWheel基於Go的實踐經驗漫談——GC是大坑(關鍵業務情境不用),web架構尚未統一,和c++效能相比難說