近一兩年來,微服務架構已經成爲熱門話題(microservices.io),與傳統的一體化應用架構相比,微服務架構在開發、測試、部署方面都有衆多吸引人之處,愈來愈多沒有歷史包袱的新項目都啓用微服務架構的模式來開發。html
咱們這個團隊通過深刻思考以後,決定在一塊兒美這個APP的後端開發中,選擇go做爲開發語言,採用微服務模式來實現,通過近半年的實踐,造成了一些心得,簡單總結後分享出來,但願可以給你們一些幫助。git
不一樣的團隊在選擇基礎框架(庫)時考慮的要素不一樣,咱們團隊更喜歡小而美的框架,儘量不要讓框架侵入業務,易於升級、維護和替換,因此咱們更願意選擇Library而不是Framework。github
在web方面,咱們選擇了negroni做爲middleware庫,採用性能不錯的httprouter替換go標準庫的mux,而沒有用任何web相關的框架。golang
在微服務之間的rpc調用方面,爲了未來的擴展性、跨語言調用等因素,咱們沒有直接用go標準庫的rpc模塊,而是採納了google最新推出的grpc。但grpc自己屬於比較重型的rpc框架,對業務代碼有必定的侵入性, 咱們作了一系列的庫(包括worpc、worc、wonaming等)來屏蔽這些沒必要要的業務代碼侵入 ,保持了業務代碼自己的整潔。web
在微服務體系中,如何切分微服務也是一個重要的話題,在咱們的實踐中,咱們遵循了以下一些原則:後端
Gateway是微服務對外提供服務的一個屏障,它的核心點在於:架構
微服務體系中,服務的註冊和發現對總體架構很是重要,尤爲對於同步的rpc調用,每一個服務有多少實例,每一個實例的地址等,都須要有一個統一的管理。咱們採用etcd保存服務信息,同時封裝了wonaming做爲微服務註冊和發現的中間件,它的主要功能包括:負載均衡
/wonaming
目錄進行監控,當有服務註冊或者解註冊時,動態維護可用服務清單。r := wonaming.NewResolver(name)
b := grpc.RoundRobin(r)
conn, err := grpc.Dial(etcd, grpc.WithInsecure(), grpc.WithBalancer(b))
grpc是一個比較重的rpc框架,當客戶端經過grpc調用服務端時,須要大量的重複性代碼來創建鏈接、調用、處理錯誤返回等,影響業務代碼的整潔性,而且對業務代碼具備很強的侵入性,爲了規避這個問題,咱們封裝了worc,以實現便捷的grpc調用:框架
resp, err := worc.CallRPC(ctx, "hello", "Hello", req)
grpc提供了interceptor機制,但並無提供chain來實現不一樣的中間件的順序執行,爲了將不一樣的中間件功能(如鑑權、日誌、recover)封裝在不一樣的函數裏,worpc提供了組合gprc interceptor爲一個chain的能力,能夠根據自身業務的須要,撰寫不一樣的grpc中間件進行組合,好比實現 grpc 的 recovery 與 log 中間件:運維
func Recovery(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
defer func() {
if r := recover(); r != nil {
// log stack
stack := make([]byte, MAXSTACKSIZE)
stack = stack[:runtime.Stack(stack, false)]
log.CtxErrorf(ctx, "panic grpc invoke: %s, err=%v, stack:\n%s", info.FullMethod, r, string(stack))
// if panic, set custom error to 'err', in order that client and sense it.
err = grpc.Errorf(codes.Internal, "panic error: %v", r)
}
}()
return handler(ctx, req)
}
func Logging(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
start := time.Now()
log.CtxInfof(ctx, "calling %s, req=%s", info.FullMethod, marshal(req))
resp, err = handler(ctx, req)
log.CtxInfof(ctx, "finished %s, took=%v, resp=%v, err=%v", info.FullMethod, time.Since(start), marshal(resp), err)
return resp, err
}
s := grpc.NewServer(grpc.UnaryInterceptor(worpc.UnaryInterceptorChain(worpc.Recovery, worpc.Logging)))
經過以上組合,可爲微服務提供panic恢復能力,保障服務穩定可用;同時還將上文中提到的注入context中的trace id取出,這樣Gateway與微服務的日誌經過就銜接了起來,方便查錯、調優等。
grpc.Errorf
封裝業務中的邏輯錯誤,隨grpc服務調用一塊兒返回,將業務response與error 分離。以上是微服務架構在咱們團隊的實踐方案,麻雀雖小,五臟俱全。經過各中間件的靈活組合,保障業務有序與服務的高可用,還不抓緊實踐起來?在後續的文章中,咱們還會介紹目前微服務測試、運維及部署方案。