在生產環境用了一個月Go語言,我有4點體會

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最近,我獲得了一份DevOps工作,工作內容主要涉及用Go完全從頭開始編寫一個新的後端系統。此前,我從未在生產環境使用過Go,從個人項目中有過了解。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"你(可能)應該使用一個Web框架"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一開始,我們決定只使用Go的http庫和一個簡單的路由庫——"},{"type":"codeinline","content":[{"type":"text","text":"mux"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,我很快就遇到了現實生活中的生產問題:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"恢復中間件"},{"type":"text","text":"——用來日誌打印和靜默處理程序代碼中的死機。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"日誌"},{"type":"text","text":"——我想要某個方案,可以打印每個請求的信息,包含body params、auth tokens等等(用於調試目的)。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"更好的錯誤處理"},{"type":"text","text":"——我希望錯誤仍然是帶有錯誤信息和代碼的JSON響應。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"其它常用的中間件"},{"type":"text","text":"——包含JWT驗證和CORS。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我有兩個選擇:自己實現上述問題的解決方案,針對每個問題使用不同的第三方庫,或者選擇一個Web框架,基本上已經做了大部分(如果不是全部)這些事情。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我最終決定使用Echo這個Web框架。據悉,它在GitHub上有近2萬個點贊,有一個非常活躍的社區,還有很棒的文檔。我認爲它是完成這份工作的一個很棒的工具。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/23\/cc\/23ce53db89926a4ec71c7e0cfea8cccc.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我還發現,用"},{"type":"codeinline","content":[{"type":"text","text":"echo"}]},{"type":"text","text":"編寫應用程序時有一些比較精簡的樣板(主要是解析json body、編寫errors以及手動設置headers等),讓代碼的可讀性有所提高。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/d7\/ae\/d738630299bde59f7f54e84e5799faae.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,當你有一些比較複雜的端點時,你就會注意到生產率的真正差異。你經常會遇到需要驗證某些JSON字段的情況,並且需要有意義的錯誤信息來描述錯誤。如果你想要在不使用任何庫的情況下完成這些,你的代碼將很快變得很難閱讀:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/48\/61\/486024d41cff85ab4f78d0fb23416a61.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"你需要一個好的代碼結構"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Go的Web框架(或者一般的go項目)不強制任何特定的文件結構。如果你使用過ASP.NET\/ASP.NET Core之類的東西,當我說一些框架是緊密結構的,而且很多事情都是通過約定而不是顯式指定來完成的時,你就會知道我在說什麼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於Go的問題是,你很容易跳過關於構建代碼結構的學習,使得代碼很難閱讀和維護。如果你還不知道我在說什麼,下面是我不久前寫的一個("},{"type":"text","marks":[{"type":"strong"}],"text":"糟糕的"},{"type":"text","text":")Go端點例子:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/b9\/86\/b98603e588e67a3d936ab02f4fab3a86.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你明白我的意思嗎?在添加了所有的"},{"type":"codeinline","content":[{"type":"text","text":"CreateUser"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"CreateAgency"}]},{"type":"text","text":"方法後,“更好的”方法很可能會包含更多的行,但是...它以後會非常容易理解、重用、調試和修改,因爲每個方法都有單獨的用途。如果你還沒有明白,我強烈建議你看一看下面關於良好代碼結構的資源:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/github.com\/ribice\/gorsk","title":"","type":null},"content":[{"type":"text","text":"https:\/\/github.com\/ribice\/gorsk"}]},{"type":"text","text":"- 基礎REST API的良好例子"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/github.com\/bxcodec\/go-clean-arch","title":"","type":null},"content":[{"type":"text","text":"https:\/\/github.com\/bxcodec\/go-clean-arch"}]},{"type":"text","text":"- 也是一個REST API例子,但是更嚴格地遵循“Clean Architecture”理念"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般來說,這個理念很簡單。你應該將與數據庫通信的代碼與實際的應用程序邏輯本身分開,而且應用邏輯也應該與傳輸\/端點邏輯(在本例中是HTTP端點)分開。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"明智地選擇你的SQL driver"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當我第一次用Go開始編程時,我希望儘可能使用最新的庫,因此我選擇使用"},{"type":"codeinline","content":[{"type":"text","text":"database\/sql"}]},{"type":"text","text":"包(使用Postgres)。雖然這個體驗還可以,但在查詢數據時,我遇到很多樣本,特別是不得不使用Scan語法。這導致我有下面2個選項:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"sqlx"}]},{"type":"text","text":"- 一個基於"},{"type":"codeinline","content":[{"type":"text","text":"database\/sql"}]},{"type":"text","text":"的輕量包裝器,做了一些擴展,使得做查詢更容易。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"gorm"}]},{"type":"text","text":"- 一個針對Go的ORM(Object-Relational Mapping,對象-關係映射)庫,根據你的Go models生成SQL models和查詢。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我不認爲有一個明確的“更好的”庫,最終取決於使用場景和個人偏好。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"gorm"}]},{"type":"text","text":"可能會讓你輕鬆一些,特別是如果你經常在修改數據庫之後忘記在查詢中增加字段的話(因爲在gorm中,你根本不需要做這些)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另一方面,"},{"type":"codeinline","content":[{"type":"text","text":"sqlx"}]},{"type":"text","text":"更以SQL爲中心,它更像是寫Go代碼來調用SQL接口,而不是gorm方案那樣根據Go代碼生成SQL。如果你喜歡完全掌控SQL並且不必學習GORM的新語法,那麼這是一個不錯的方案。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Docker"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我遇到的一個挑戰是配置這個項目的生產環境。開發環境和生產環境總會有一些差別,例如這個應用程序在哪個端口上運行、數據庫的主機和憑證,等等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我見過有人通過JSON、YAML甚至git忽略的.go文件來配置應用程序變量。我個人發現env文件最好用,特別是配合docker-compose使用:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/70\/73\/708b9f588yyc8e83ff9645f380174d73.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/96\/3a\/96386e0a7212141a4bcb5fccb59af63a.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我通常將這些與以下實用的函數結合使用:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/47\/41\/47613e382533632c1039c66fa4983f41.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用Go構建Docker鏡像也超級簡單:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/62\/11\/62b7f3cb7f7b35b972d58b0b82504e11.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"其它?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我還想到了其它一些東西,但我不認爲它們值得單獨用章節來討論:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/tour.golang.org\/basics\/7","title":"","type":null},"content":[{"type":"text","text":"Named returns in Go"}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/golang.org\/doc\/effective_go.html","title":"","type":null},"content":[{"type":"text","text":"Effective Go"}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/blog.golang.org\/using-go-modules","title":"","type":null},"content":[{"type":"text","text":"Go Modules"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文鏈接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/tdom.dev\/go-in-production","title":"","type":null},"content":[{"type":"text","text":"https:\/\/tdom.dev\/go-in-production"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章