不知不覺寫 Go
已經快一年了,上線了大大小小好幾個專案;心態也經歷了幾輪變化。
因為我個人大概前五年時間寫的是 Java
,中途寫過一年多的 Python
,所以剛接觸到 Go 時的感覺如下圖:
既沒有 Java
的生態,也沒有 Python
這麼多語法糖。
寫到現在的感覺就是:
這裡就不討論這幾門語言誰強誰弱了;重點和大家分享下我們日常開發中所使用到的一些第三方庫與工具。
這裡我主要將這些庫分為兩類:
- 業務開發
- 基礎工具開發
業務開發
首先是業務開發,主要包含了 web
、資料庫、Redis
等。
Gin ⭐️⭐️⭐️⭐️⭐️
首先是 Gin,一款 HTTP 框架,使用簡單、效能優秀、資料眾多;你還在猶豫選擇哪款框架時,那就選擇它吧,基本沒錯。
當然和它配套的 github.com/swaggo/gin-swagger swagger 工具也是剛需;利用它可以生成 swagger 文件。
GORM ⭐️⭐️⭐️⭐️⭐️
GORM 也沒啥好說的,如果你喜歡 orm
的方式運算元據庫,那就選它吧;同樣的也是使用簡單、資料較多。
如果有讀寫分離需求,也可以使用 GORM
官方提供的外掛 https://github.com/go-gorm/dbresolver ,配合 GORM
使用也是非常簡單。
errors ⭐️⭐️⭐️⭐️⭐️
Go 語言自身提供的錯誤處理比較簡單,https://github.com/pkg/errors 提供了更強大的功能,比如:
- 包裝異常
- 包裝堆疊等。
常用的有以下 API:
// WithMessagef annotates err with the format specifier.
func WithMessagef(err error, format string, args ...interface{}) error
// WithStack annotates err with a stack trace at the point WithStack was called.
func WithStack(err error) error
zorolog ⭐️⭐️⭐️⭐️⭐️
Go 裡的日誌列印庫非常多,日誌在日常開發中最好就是存在感低;也就是說效能強(不能影響到業務程式碼)、使用 API 簡單。
"github.com/rs/zerolog/log"
log.Debug().Msgf("OrderID :%s", "12121")
excelize
https://github.com/qax-os/excelize是一個讀寫 Excel 的庫,基本上你能遇到的 Excel 操作它都能實現。
now ⭐️⭐️⭐️⭐️
https://github.com/jinzhu/now 是一個時間工具庫:
- 獲取當前的年月日、時分秒。
- 不同時區支援。
- 最後一週、最後一個月等。
import "github.com/jinzhu/now"
time.Now() // 2013-11-18 17:51:49.123456789 Mon
now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon
now.BeginningOfHour() // 2013-11-18 17:00:00 Mon
now.BeginningOfDay() // 2013-11-18 00:00:00 Mon
now.BeginningOfWeek() // 2013-11-17 00:00:00 Sun
now.BeginningOfMonth() // 2013-11-01 00:00:00 Fri
now.BeginningOfQuarter() // 2013-10-01 00:00:00 Tue
now.BeginningOfYear() // 2013-01-01 00:00:00 Tue
now.EndOfMinute() // 2013-11-18 17:51:59.999999999 Mon
now.EndOfHour() // 2013-11-18 17:59:59.999999999 Mon
now.EndOfDay() // 2013-11-18 23:59:59.999999999 Mon
now.EndOfWeek() // 2013-11-23 23:59:59.999999999 Sat
now.EndOfMonth() // 2013-11-30 23:59:59.999999999 Sat
now.EndOfQuarter() // 2013-12-31 23:59:59.999999999 Tue
now.EndOfYear() // 2013-12-31 23:59:59.999999999 Tue
now.WeekStartDay = time.Monday // Set Monday as first day, default is Sunday
now.EndOfWeek() // 2013-11-24 23:59:59.999999999 Sun
Decimal ⭐️⭐️⭐️⭐️
當業務上需要精度計算時 https://github.com/shopspring/decimal 可以幫忙。
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
price, err := decimal.NewFromString("136.02")
quantity := decimal.NewFromInt(3)
fee, _ := decimal.NewFromString(".035")
taxRate, _ := decimal.NewFromString(".08875")
subtotal := price.Mul(quantity)
preTax := subtotal.Mul(fee.Add(decimal.NewFromFloat(1)))
total := preTax.Mul(taxRate.Add(decimal.NewFromFloat(1)))
fmt.Println("Subtotal:", subtotal) // Subtotal: 408.06
fmt.Println("Pre-tax:", preTax) // Pre-tax: 422.3421
fmt.Println("Taxes:", total.Sub(preTax)) // Taxes: 37.482861375
fmt.Println("Total:", total) // Total: 459.824961375
fmt.Println("Tax rate:", total.Sub(preTax).Div(preTax)) // Tax rate: 0.08875
}
基本上你能想到的精度轉換它都能做到;配合上 GORM
也可以將 model
欄位宣告為 decimal
的型別,資料庫對應的也是 decimal
,這樣使用起來時會更方便。
Amount decimal.Decimal `gorm:"column:amout;default:0.0000;NOT NULL" json:"amout"`
configor ⭐️⭐️⭐️⭐️
https://github.com/jinzhu/configor 是一個配置檔案讀取庫,支援 YAML/JSON/TOML
等格式。
go-cache ⭐️⭐️⭐️
https://github.com/patrickmn/go-cache 是一個類似於 Java 中的 Guava cache
,執行緒安全,使用簡單;不需要分散式快取的簡單場景可以考慮。
c := cache.New(5*time.Minute, 10*time.Minute)
// Set the value of the key "foo" to "bar", with the default expiration time
c.Set("foo", "bar", cache.DefaultExpiration)
copier ⭐️⭐️⭐️
https://github.com/jinzhu/copier 看名字就知道這是一個資料複製的庫,與 Java
中的 BeanUtils.copy()
類似;可以將兩個欄位相同但物件不同的 struct
進行資料複製,也支援深拷貝。
func Copy(toValue interface{}, fromValue interface{}) (err error)
在我們需要一個臨時 struct 來存放資料時很有用,特別是一個 struct 中欄位非常多時,一個個來回賦值確實有點費手指。
但也要注意不要什麼情況都使用,會帶來一些弊端:
- 當刪除欄位時,不能利用編譯器提示。
- 當一些欄位需要額外人工處理時,程式碼不易閱讀。
- 反射賦值,有一定效能損耗。
總之在業務開發時,還是建議人工編寫,畢竟程式碼是給人看的。
env ⭐️⭐️⭐️
https://github.com/caarlos0/env 這個庫可以將我們的環境變數轉換為一個 struct
.
type config struct {
Home string `env:"HOME"`
}
func main() {
cfg := config{}
if err := env.Parse(&cfg); err != nil {
fmt.Printf("%+v\n", err)
}
fmt.Printf("%+v\n", cfg)
}
這個在我們打包程式碼到不同的執行環境時非常有用,利用它可以方便的獲取不同環境變數。
user_agent ⭐️⭐️⭐️
https://github.com/mssola/user_agent 是一個格式化 user-agent
的小工具。
當我們需要在服務端收集 user-agen
時可以更快的讀取資料。
func main() {
ua := user_agent.New("Mozilla/5.0 (Linux; U; Android 2.3.7; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1")
fmt.Printf("%v\n", ua.Mobile()) // => true
fmt.Printf("%v\n", ua.Bot()) // => false
fmt.Printf("%v\n", ua.Mozilla()) // => "5.0"
fmt.Printf("%v\n", ua.Model()) // => "Nexus One"
fmt.Printf("%v\n", ua.Platform()) // => "Linux"
fmt.Printf("%v\n", ua.OS())
}
phonenumbers ⭐️⭐️⭐️
https://github.com/nyaruka/phonenumbers 手機號碼驗證庫,可以不用自己寫正規表示式了。
// parse our phone number
num, err := phonenumbers.Parse("6502530000", "US")
基礎工具
接下來是一些基礎工具庫,包含一些主流的儲存的客戶端、中介軟體等。
gomonkey ⭐️⭐️⭐️⭐️⭐️
github.com/agiledragon/gomonkey 是一個 mock
打樁工具,當我們寫單元測試時,需要對一些非介面函式進行 mock
會比較困難,這時就需要用到它了。
由於它是修改了呼叫對應函式時機器跳轉指令,而 CPU 架構的不同對應的指令也不同,所以在我們使用時還不相容蘋果的 M1 晶片,不過目前應該已經相容了,大家可以試試。
goconvey ⭐️⭐️⭐️⭐️⭐️
https://github.com/smartystreets/goconvey 也是配合單元測試的庫,可以相容 go test
命令。
- 提供視覺化 web UI。
- 與 IDE 整合顯示單元覆蓋率。
dig ⭐️⭐️⭐️⭐️⭐️
https://github.com/uber-go/dig 這是一個依賴注入庫,我們這裡暫不討論是否應該使用依賴注入,至少目前我們使用下來還是有幾個好處:
- 所有的物件都是單例。
- 有一個統一的地方管理物件。
- 使用時直接傳遞物件當做引數進來即可(容器會自動注入)。
當然也有一些不太方便的地方:
- 不熟悉時,一個物件是如何建立的不清楚。
- 程式碼不是很好理解。
我們內部有自己開發一個業務框架,其中所有的物件都交由 dig 進行管理,使用起來倒也是比較方便。
cobra ⭐️⭐️⭐️⭐️
https://github.com/spf13/cobra是一個功能強大的命令列工具庫,我們用它來實現內部的命令列工具,同時也推薦使用 https://github.com/urfave/cli/ 我個人會更習慣用後者,要簡潔一些。
BloomRPC ⭐️⭐️⭐️⭐️
https://github.com/uw-labs/bloomrpc 一個 gRPC
視覺化工具,比起自己寫 gRPC
客戶端的程式碼那確實是要簡單許多。
但也有些小問題,比如精度。如果是 int64 超過了 2^56 服務端拿到的值會發生錯誤,這點目前還未解決。
redis ⭐️⭐️⭐️⭐️
https://github.com/go-redis/redis/ Redis 客戶端,沒有太多可說的;發展了許多年,該有的的功能都有了。
elastic ⭐️⭐️⭐️⭐️
https://github.com/olivere/elastic 這也是一個非常成熟的 elasticsearch
庫。
resty ⭐️⭐️⭐️⭐️
https://github.com/go-resty/resty/ 一個 http client, 使用起來非常簡單:
// Create a Resty Client
client := resty.New()
resp, err := client.R().
EnableTrace().
Get("https://httpbin.org/get")
有點 Python requests
包那味了。
pulsar-client-go ⭐️⭐️⭐️
Pulsar 官方出品的 go 語言客戶端,相對於 Java 來說其他語言的客戶端幾乎都是後孃養的;功能會比較少,同時更新也沒那麼積極;但卻沒得選。
go-grpc-middleware ⭐️⭐️⭐️
https://github.com/grpc-ecosystem/go-grpc-middleware 官方提供的 gRPC
中介軟體,可以自己實現內部的一些鑑權、後設資料、日誌等功能。
go-pilosa ⭐️⭐️⭐️
https://github.com/pilosa/go-pilosa 是一個點陣圖資料庫的客戶端,點陣圖資料庫的場景應用比較有限,通常是有標籤需求時才會用到;比如求 N 個標籤的交併補集;資料有一定規模後運營一定會提相關需求;可以備著以備不時之需。
pb ⭐️⭐️⭐️
https://github.com/cheggaaa/pb 一個命令列工具進度條,編寫命令列工具時使用它互動會更優雅。
總結
最後我彙總了一個表格,方便檢視:
名稱 | 型別 | 功能 | 星級 |
---|---|---|---|
Gin | 業務開發 | HTTP 框架 | ⭐️⭐️⭐️⭐️⭐️ |
GORM | 業務開發 | ORM 框架 | ⭐️⭐️⭐️⭐️⭐️ |
errors | 業務開發 | 異常處理庫 | ⭐️⭐️⭐️⭐️⭐️ |
zorolog | 業務開發 | 日誌庫 | ⭐️⭐️⭐️⭐️⭐️ |
excelize | 業務開發 | Excel 相關需求 | ⭐️⭐️⭐️⭐️⭐️ |
now | 業務開發 | 時間處理 | ⭐️⭐️⭐️⭐️️ |
Decimal | 業務開發 | 精度處理 | ⭐️⭐️⭐️⭐️️ |
configor | 業務開發 | 配置檔案 | ⭐️⭐️⭐️⭐️️ |
go-cache | 業務開發 | 本地快取 | ⭐️⭐️⭐️ |
copier | 業務開發 | 資料複製 | ⭐️⭐️⭐️️️ |
env | 業務開發 | 環境變數 | ⭐️⭐️⭐️️️ |
user_agent | 業務開發 | 讀取 user-agent | ⭐️⭐️⭐️️️ |
phonenumbers | 業務開發 | 手機號碼驗證 | ⭐️⭐️⭐️️️ |
gomonkey | 基礎工具 | mock 工具 | ⭐️⭐️⭐️⭐️⭐ |
goconvey | 基礎工具 | 單測覆蓋率 | ⭐️⭐️⭐️⭐️⭐ |
dig | 基礎工具 | 依賴注入 | ⭐️⭐️⭐️⭐️⭐ |
cobra | 基礎工具 | 命令列工具 | ⭐️⭐️⭐️⭐ |
cli | 基礎工具 | 命令列工具 | ⭐️⭐️⭐️⭐ |
BloomRPC | 基礎工具 | gRPC 除錯客戶端 | ⭐️⭐️⭐️⭐ |
redis | 基礎工具 | Redis 客戶端 | ⭐️⭐️⭐️⭐ |
elastic | 基礎工具 | elasticsearch 客戶端 | ⭐️⭐️⭐️⭐ |
resty | 基礎工具 | http 客戶端 | ⭐️⭐️⭐️⭐ |
pulsar-client-go | 基礎工具 | Pulsar 客戶端 | ⭐️⭐️⭐️ |
go-grpc-middleware | 基礎工具 | gRPC 中介軟體 | ⭐️⭐️⭐ |
go-pilosa | 基礎工具 | pilosa 客戶端 | ⭐️⭐️⭐️ |
pb | 基礎工具 | 命令列工具進度條 | ⭐️⭐️⭐️ |
星級評分的規則主要是看實際使用的頻次。
最後夾帶一點私貨(其實也談不上)
文中提到了我們內部有基於以上庫整合了一個業務開發框架;也基於該框架上線了大大小小10幾個專案,改進空間依然不少,目前還是在快速迭代中。
大概的用法,入口 main.go
:
最後擷取我在內部的分享就概括了整體的思想--引用自公司一司姓同事
。
也許我們內部經過多次迭代,覺得有能力開放出來給社群帶來一些幫助時也會嘗試開源;現階段就不嫌醜了。
這些庫都是我們日常開發最常用到的,也歡迎大家在評論區留下你們常用的庫與工具。