自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

產(chǎn)品環(huán)境中 Go 語言的最佳實(shí)踐

開發(fā) 項(xiàng)目管理
我們也是通曉多種語言的組織,因?yàn)槲覀兪褂昧撕芏嗾Z言。并且這些服務(wù)(和基礎(chǔ)設(shè)施支持)的許多部分是使用Golang開發(fā)的。事實(shí)上,我們都是早期Golang的使用者:目前,我們已在產(chǎn)品中使用Golang有兩年半的時間。

在SoundCloud,我們?yōu)榭蛻魳?gòu)建了產(chǎn)品的API。或者說,我們主要的網(wǎng)站、手機(jī)客戶端和手機(jī)應(yīng)用是該API的***批客戶。該API背后是一個領(lǐng)域性的服務(wù):SoundCloud基本上以面向服務(wù)體系結(jié)構(gòu)的形式運(yùn)作。

我們也是通曉多種語言的組織,因?yàn)槲覀兪褂昧撕芏嗾Z言。并且這些服務(wù)(和基礎(chǔ)設(shè)施支持)的許多部分是使用Golang開發(fā)的。事實(shí)上,我們都是早期Golang的使用者:目前,我們已在產(chǎn)品中使用Golang有兩年半的時間。相關(guān)項(xiàng)目包括:

  • Bazooka,我們內(nèi)部服務(wù)平臺;產(chǎn)品思想非常類似于Keroku或Flynn。

  • 我們外圍的傳輸層使用通用的nginx, HAProxy等等,但是它們要和Golang服務(wù)協(xié)作。

  • 我們的音頻存儲在AWS S3上,但是上傳、轉(zhuǎn)碼和生成鏈接等需要Golang服務(wù)協(xié)調(diào)處理。

  • 搜索采用了Elasticsearch, 探測使用復(fù)雜的機(jī)器學(xué)習(xí)模型,但是它們都與由Golang開發(fā)的基礎(chǔ)設(shè)施相集成。

  • Prometheus,一個早期階段的遙測系統(tǒng)純粹是有Golang開發(fā)。

  • 當(dāng)前,流處理采用Cassandra,但是我們正打算(幾乎)完全使用Golang代替。

  • 我們也正在試驗(yàn)用Golnag開發(fā)的HTTP流媒體直播服務(wù)。

  • 許多其他面向產(chǎn)品的小服務(wù)。

這些項(xiàng)目大概有六個團(tuán)隊(duì)開發(fā),包括十多人的SoundCloud勤雜工,他們中的大部分會全職使用Golang。畢竟在這個時候,這些項(xiàng)目和這樣混雜的工程師中,我們已經(jīng)逐漸形成了在產(chǎn)品中使用Golang的***實(shí)踐方法。我們的這些教訓(xùn)將對其他開始大舉投資Golang的組織提供幫助。

開發(fā)環(huán)境

在我們的筆記本上,我們已經(jīng)設(shè)定了單一、全局的GOPATH。就個人而言,我喜歡使用$HOME,但是許多其他人使用$HOME下的一個子目錄。我們克隆倉庫進(jìn)入GOPATH的相對路徑,然后就可直接工作。即,

  1. $ mkdir -p $GOPATH/src/github.com/soundcloud  
  2. $ cd $GOPATH/src/github.com/soundcloud  
  3. $ git clone git@github.com:soundcloud/roshi 

我們中的許多人在早期一直和約定俗成的事情做斗爭,以保持我們自己特有的代碼組織方法。事實(shí)上,它根本不值得如此麻煩。

對于編輯器,許多用戶使用Vim以及各種插件。(我使用的vim-go就不錯。)還有許多人,包括我自己也是,結(jié)合GoSublime使用Sublime Text。也有少數(shù)人使用Emacs,但沒有人用IDE。我不確定這是不是個***的實(shí)踐,但標(biāo)出來挺有趣的。

庫結(jié)構(gòu)

我們的***實(shí)踐是確保任何事情簡單。許多服務(wù)源碼半打包在main包中。

  1. github.com/soundcloud/simple/  
  2.     README.md  
  3.     Makefile  
  4.     main.go  
  5.     main_test.go  
  6.     support.go  
  7.     support_test.go 

比如我們的搜索調(diào)度器,兩年后仍然是這樣。在確定需要前不要創(chuàng)建新結(jié)構(gòu)。

也許在某些時候你需要創(chuàng)建一個新的支持包。在你的main庫中使用子目錄,并使用完整的限定名導(dǎo)入。如果該包只有一個文件或一個結(jié)構(gòu),那么它肯定不需要分拆出來。

有時一個倉庫中需要包含多個二進(jìn)制文件;比如這個任務(wù)需要一個服務(wù),一個工作進(jìn)程,或一個監(jiān)控。在這種情況下,將每個二進(jìn)制文件放在特定main包的單獨(dú)的子目錄中,并使用其他的子目錄(或包)來實(shí)現(xiàn)共享的功能。

  1. github.com/soundcloud/complex/  
  2.     README.md  
  3.     Makefile  
  4.     complex-server/  
  5.         main.go  
  6.         main_test.go  
  7.         handlers.go  
  8.         handlers_test.go  
  9.     complex-worker/  
  10.         main.go  
  11.         main_test.go  
  12.         process.go  
  13.         process_test.go  
  14.     shared/  
  15.         foo.go  
  16.         foo_test.go  
  17.         bar.go  
  18.         bar_test.go 

請注意,不要引入asrc目錄。由于vendor子目錄異常(下面介紹更多內(nèi)容)不要在倉庫中包含src目錄,或?qū)⑵涮砑拥紾OPATH。

格式及樣式

通常來說,首先配置你的編輯器保存代碼交給go fmt(或goimports),使用默認(rèn)參數(shù)。這意味使用tab縮進(jìn),用空格對齊。格式不正確的代碼將不能提交。

過去的風(fēng)格指南非常廣泛,但谷歌最近發(fā)布了他們的 代碼審查意見 文檔,這幾乎就是我們應(yīng)遵守的公約。因此,我們使用它。

實(shí)際上我們把它推進(jìn)了一點(diǎn):

  • 避免命名返回參數(shù),除非他們能明確和顯著地提高透明度。

  • 避免用 make 和 new,除非他們是必要的(new(int),或 make(Chan int)),或者我們能提前知道要分配的東西的尺寸( make(map[int]string,n),或 make([]int,0,256))。

  • 使用 struct{} 作為標(biāo)記值,而不是布爾或接口{}。例如,集合是 map[string]struct{};信道是 chan struct{}。它明確標(biāo)明了信息的明確缺乏。

打斷長行的參數(shù)也很好。那更象是Java的風(fēng)格:

  1. // 不要這樣。  
  2. func process(dst io.Writer, readTimeout,  
  3.     writeTimeout time.Duration, allowInvalid bool,  
  4.         max int, src <-chan util.Job) {  
  5.     // ...  

這樣會更好:

  1. func process(  
  2.     dst io.Writer,  
  3.     readTimeout, writeTimeout time.Duration,  
  4.     allowInvalid bool,  
  5.     max int,  
  6.     src <-chan util.Job,  
  7. ) {  
  8.     // ...  

當(dāng)構(gòu)造對象時也同樣分為多行:

  1. f := foo.New(foo.Config{
  
  2.     Site: "zombo.com",
  
  3.     Out:  os.Stdout,
  
  4.     Dest: conference.KeyPair{
  
  5.         Key:   "gophercon",  
  6.         Value: 2014,  
  7.     },  
  8. }) 

另外,當(dāng)分配新的對象時,在初始化部分傳遞成員值(如上面)比下面這樣過后設(shè)置要好。

  1. // 不要這樣。  
  2. f := &Foo{} // or, even worse: new(Foo)  
  3. f.Site = "zombo.com" 
  4. f.Out = os.Stdout  
  5. f.Dest.Key = "gophercon" 
  6. f.Dest.Value = 2014 

配置

我們嘗試了通過多種方式向Go程序傳遞配置:解析配置文件,用 os.Getenv 直接從環(huán)境中提取配置,各種增值flag解析包。***,最合乎經(jīng)濟(jì)原則的就是普通的package flag,它的嚴(yán)格類型和簡單語義對我們所需的一切都絕對夠用而且夠好。

我們主要部署12-Factor 的應(yīng)用,12-Factor 應(yīng)用程序通過環(huán)境傳遞配置。但即使這樣,我們也使用一個啟動腳本來把環(huán)境變量轉(zhuǎn)換為flags。Flags作為程序及其運(yùn)行環(huán)境之間的一個明確和全文檔化的表面區(qū)域。他們對于了解和操作程序來說是非常寶貴的。

一個關(guān)于flags的不錯的習(xí)慣是把他們定義到你的main函數(shù)中。這樣就能防止你在代碼中隨意的將他們作為全局變量使用,這使你嚴(yán)格的遵守依賴注入從而方便測試。

  1. func main() {  
  2.     var (  
  3.         payload = flag.String("payload""abc""payload data")  
  4.         delay   = flag.Duration("delay", 1*time.Second, "write delay")  
  5.     )  
  6.     flag.Parse()  
  7.     // ...  

日志和遙測

我們嘗試過幾個日志框架,他們提供像日志級別,調(diào)試,路由輸出,自定義格式化等等功能。最終我們選定package log。因?yàn)槲覀冎挥涗浛刹僮餍畔ⅰ?這意味著需要人工處理的 serious, panic級別的錯誤,或者結(jié)構(gòu)化數(shù)據(jù)會被其他機(jī)器消耗。 舉個例子,搜索轉(zhuǎn)發(fā)器發(fā)送每一個它使用上下文信息處理的請求,因此我們的分析工作流可以看到新西蘭的人們經(jīng)常搜索 Lorde, 或者隨便什么。

我們考慮到遙測,在一個運(yùn)行過程中釋放出的任何其他量:請求響應(yīng)時間,QPS,運(yùn)行錯誤,隊(duì)列深度等等。并且遙測基本上包括兩種模式:push和pull。

  • push意味著釋放指標(biāo)到一個已知的系統(tǒng)。例如Graphite, Statsd, and AirBrake

  • pull意味著在一些已知的位置暴露指標(biāo),并允許已知的系統(tǒng)去擦除它們。例如,expvar和Prometheus(或許還有其他的)

當(dāng)然兩種方式都有自己的存在性。當(dāng)你開始使用時,push是直觀和簡單的。但是推送指標(biāo)的增長卻有悖常理:你得到的越大,成本越高。我們過去發(fā)現(xiàn)在特定規(guī)模大小的基礎(chǔ)設(shè)施上,pull是該尺度下的唯一模型。那也有許多值能反映一個運(yùn)行的系統(tǒng)。所以,***的實(shí)踐是:expvar或者類似風(fēng)格的。

測試和驗(yàn)證

在一年的過程中我們嘗試了許多的測試庫和框架,但是很快放棄了他們中的大部分,今天我們所有的測試通過數(shù)據(jù)驅(qū)動(表驅(qū)動)測試,用普通的包測試。我們沒有強(qiáng)烈或者明確的抱怨測試/檢查包,除此之外,他們根本沒有提供巨大的價值。有一件事情是有幫助的:reflect.DeepEqual讓你更簡單的對任意值進(jìn)行比較(例如expected對got)。

包測試是面向單元測試的,對于集成測試,就會有點(diǎn)麻煩。運(yùn)行的外部服務(wù)依賴于你的集成環(huán)境,但是我們找到了一個好的方式集成他們。寫一個integration_test.go,給它一個integration的構(gòu)建標(biāo)簽。定義(全局)標(biāo)志,比如服務(wù)地址和連接字符串,用他們在你的測試中。

  1. // +build integration  
  2.    
  3. var fooAddr = flag.String(...)  
  4.    
  5. func TestToo(t *testing.T) {  
  6.     f, err := foo.Connect(*fooAddr)  
  7.     // ...  

go test 和 go build 一樣建立標(biāo)簽,所以你可以調(diào)用 go test -tags=integration 。它也綜合了 flag.Parse 包的 main,所以任何被聲明和可見的 flags 將被處理和提供給你的測試。

通過驗(yàn)證,我的意思是靜態(tài)代碼驗(yàn)證。幸運(yùn)的是,Go 有一些很好的工具。我發(fā)現(xiàn)當(dāng)考慮使用哪種工具時考慮編寫代碼的階段很有用。

當(dāng)做這種事時 使用這個
保存 go fmt(或 goimports)
構(gòu)建 go vet,golint, 或者 go test
部署 go test -tags=integration

 

插曲
 

到目前為止,還沒東西過于瘋狂。當(dāng)做調(diào)查編撰這個列表的時候,讓我注意的只是如何。。。。。。結(jié)論如何的無趣。讓人沉悶。我想強(qiáng)調(diào)這些非常輕量,純標(biāo)準(zhǔn)庫的約定能真正推廣到大群體的開發(fā)人員和多元化的項(xiàng)目生態(tài)系統(tǒng)。你絕對不會僅僅因?yàn)槟愕拇a庫已經(jīng)超過一定的規(guī)模,或者只是因?yàn)樗?em>可能 增長超過一定行數(shù), 而需要你自己的查錯框架,或者測試庫。你真的是不會需要它的。標(biāo)準(zhǔn)的語法和用法在代碼大規(guī)模時仍然功能優(yōu)雅。 

依賴管理

依賴管理! 呃! ᕕ( ᐛ )ᕗ

依賴管理的狀態(tài)在 Go 生態(tài)系統(tǒng)中是一個熱門的爭論點(diǎn),我們還沒有想到***的解決方案。但是,我們選用了一個似乎不錯的妥協(xié)方案。

你的項(xiàng)目有多么重要? 你的依賴管理方案是…
嗯… go get -d,然后祈禱!
很好. VENDORING

(值得提出的是,我們有令人震驚數(shù)量的長期產(chǎn)品服務(wù),依然依賴于***個選項(xiàng).然而,因?yàn)槲覀円话銢]有使用太多第三方代碼,以及主要問題通常在編譯階段就被檢測到,我們僥幸規(guī)避了這個問題.)

Vendoring意味著拷貝依賴到項(xiàng)目代碼庫,然后在編譯的時候使用它們.依賴于你下載的內(nèi)容,這里有兩個vendoring的***實(shí)踐.

下載  Vendor目錄名 過程
二進(jìn)制 _vendor 加GOPATH前綴編譯
vendor 重寫import語句

如果下載二進(jìn)制,就在代碼庫的根目錄創(chuàng)建一個_vendor子目錄.(帶上下劃線,這樣,go工具就會在處理時忽略它,例如go test ./...)對待它就像對待GOPATH一樣; 例如,拷貝這個依賴github.com/user/dep 到 _vendor/src/github.com/user/dep. 然后,編寫一個所謂的神圣的編譯過程,它將_vendor加入到可能存在的GOPATH之中. (記住: GOPATH 實(shí)際是一個路徑的列表,當(dāng)go工具處理import時,會按順序搜索這個列表.)例如,你可能擁有一個頂層的Makefile文件,如下所示:

  1. GO ?= go  
  2. GOPATH := $(CURDIR)/_vendor:$(GOPATH)  
  3.    
  4. all: build  
  5.    
  6. build:  
  7.     $(GO) build 

如果你正在下載某個類庫在你的根存儲庫上創(chuàng)建一個vendor子目錄。處理這件事就像在包目錄上加一個前綴。舉例來說,拷貝來自于github.com/user/dep的項(xiàng)目放到vendor/user/dep。在這之后,重寫你所有的引入(import),及其相互關(guān)系。此時是很痛苦的,當(dāng)剩下的內(nèi)容需要go get兼容的時候,看起來最有效的方式是確保事實(shí)上可重新構(gòu)建(actually-reproducible build)。值得注意的是,我們在實(shí)踐中很少去下載類庫,因此這個辦法雖然麻煩卻很有效。

如何在實(shí)際中拷貝一個依賴關(guān)系到你自己的存儲庫是另外一個熱門的話題。最簡單的方法是從一個克?。╟lone)中手動復(fù)制文件,如果你不關(guān)心上游部門的推送,這可能是***的答案。有些人使用git子模塊,但我們發(fā)現(xiàn)它們非常違反直覺并難以管理(對許多 來說也是這樣,這是有記錄的)。我們對于git子目錄(的管理)已經(jīng)很成功,他工作起來就像是子模塊。還有大量的工具是用來自動處理這項(xiàng)工作的。現(xiàn)在,它看起來就像godep發(fā)展非常積極,而且還很值得研究。

構(gòu)建與部署

構(gòu)建與部署有其技巧性,因此它與你的操作環(huán)境耦合緊密。我要描述下我們的場景,因?yàn)槲艺J(rèn)為它是個好模型,但它可能無法直接應(yīng)用到你的組織機(jī)構(gòu)中。

就構(gòu)建而言,我們通常直接使用 go build 來開發(fā),以及一個 Makefile 用于剪裁官方構(gòu)建。這主要是因?yàn)槲覀兪煜ざ喾N語言,并且我們的工具使用需要做到最小功能合集(最小公倍數(shù))。并且,我們的構(gòu)建系統(tǒng)始于一個空環(huán)境,也需要自備編譯器( Makefile 文件很難看?。?/p>

對部署而言,對我們***的吸引是無狀態(tài)之于有狀態(tài)。

模式 樣例 模型 部署名稱 部署形式
無狀態(tài) Request router 12-Factor Scaling Containers
有狀態(tài) Redis None, really Provisioning Containers?

我們主要部署無狀態(tài)的服務(wù),方式類似于 Heroku。

  1. $ git push bazooka master  
  2. $ bazooka scale -r <new> -n 4 ...  
  3. $ # validate  
  4. $ bazooka scale -r <old> -n 0 ... 

英文原文:Go: Best Practices for Production Environments

譯文鏈接:http://www.oschina.net/translate/go-in-production

責(zé)任編輯:林師授 來源: 開源中國社區(qū) 編譯
相關(guān)推薦

2018-08-28 07:30:50

云安全云服務(wù)多云

2022-04-18 09:41:14

Go架構(gòu)設(shè)計

2022-10-30 23:13:30

contextGo語言

2017-04-26 15:00:00

機(jī)器學(xué)習(xí)TensorFlow模型部署

2017-09-07 15:53:51

Go支付Java

2021-01-27 11:53:08

工具Go 開發(fā)

2015-04-28 09:12:10

云計算軟件無序

2022-10-25 12:11:13

2023-09-21 22:02:22

Go語言高級特性

2014-03-05 17:17:09

LuapythonR

2012-07-18 15:30:58

iOS交互原型

2014-12-23 14:36:32

PHP

2009-11-26 10:31:55

配置IPS最佳實(shí)踐

2024-04-28 14:46:55

gozero微服務(wù)技巧

2019-11-01 10:27:48

GoJava語言

2010-11-23 13:56:46

伊頓云計算

2011-12-01 15:54:56

機(jī)房環(huán)境數(shù)據(jù)中心

2011-09-14 10:38:39

2013-04-01 09:39:06

JavaJava異常

2023-09-13 08:00:00

JavaScript循環(huán)語句
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號