1. 程式人生 > >企業專案遷移go-zero全攻略(二)

企業專案遷移go-zero全攻略(二)

承接上篇:上篇文章講到 `go-zero` 架構設計和專案設計。本篇文章接著這個專案設計,將生成的 `app 模組` 中 `gateway` 和 `RPC` 進行改造。廢話不多說,讓我們開始! ## gateway service gateway 中我做了一些自定義,在端請求我們後臺介面情況下,雖然多數情況是不需要關心錯誤碼的,但是避免不了要某些場景還是需要根據固定錯誤碼去做特殊處理,我自己定義了一個錯誤類,這個錯誤類只在 gateway 中使用: err.go: ```go package xerr import "fmt" type CodeError struct { errCode int errMsg string } // 屬性 func (e *CodeError) GetErrCode() int { return e.errCode } func (e *CodeError) GetErrMsg() string { return e.errMsg } func (e *CodeError) Error() string { return fmt.Sprintf("ErrCode:%d,ErrMsg:%s", e.errCode, e.errMsg) } func New(errCode int, errMsg string) *CodeError { return &CodeError{errCode: errCode, errMsg: errMsg} } func NewErrCode(errCode int) *CodeError { return &CodeError{errCode: errCode, errMsg: MapErrMsg(errCode)} } func NewErrMsg(errMsg string) *CodeError { return &CodeError{errCode: BAD_REUQEST_ERROR, errMsg: errMsg} } ``` errmsg.go ```go package xerr var message map[int]string func init() { message = make(map[int]string) message[OK] = "SUCCESS" message[BAD_REUQEST_ERROR] = "伺服器繁忙,請稍後再試" message[REUQES_PARAM_ERROR] = "引數錯誤" message[USER_NOT_FOUND] = "使用者不存在" } func MapErrMsg(errcode int) string { if msg, ok := message[errcode]; ok { return msg } else { return "伺服器繁忙,請稍後再試" } } ``` errcode.go ```go package xerr // 成功返回 const OK = 200 // 全域性錯誤碼 // 前3位代表業務,後三位代表具體功能 const BAD_REUQEST_ERROR = 100001 const REUQES_PARAM_ERROR = 100002 // 使用者模組 const USER_NOT_FOUND = 200001 ``` 我將三個檔案統一放在 `lib/xerr` 目錄 ![](https://img2020.cnblogs.com/other/14470/202101/14470-20210125093904020-864783176.jpg) 有了錯誤碼還不行,還要定義統一返回http的結果,`goctl` 生成的預設的是挺好的,但是沒法符合我這種返回自定義錯誤碼需求,於是我自己有寫了一個統一返回結果的檔案: httpresult: ```go package xhttp import ( "fishtwo/lib/xerr" "fmt" "github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/rest/httpx" "google.golang.org/grpc/status" "net/http" "github.com/pkg/errors" ) // http方法 func HttpResult(r *http.Request,w http.ResponseWriter,resp interface{},err error) { if err == nil { // 成功返回 r:= Success(resp) httpx.WriteJson(w, http.StatusOK, r) } else { // 錯誤返回 errcode := xerr.BAD_REUQEST_ERROR errmsg := "伺服器繁忙,請稍後再試" if e,ok := err.(*xerr.CodeError);ok{ // 自定義CodeError errcode = e.GetErrCode() errmsg = e.GetErrMsg() } else { originErr := errors.Cause(err) // err型別 if gstatus, ok := status.FromError(originErr);ok{ // grpc err錯誤 errmsg = gstatus.Message() } } logx.WithContext(r.Context()).Error("【GATEWAY-SRV-ERR】 : %+v ",err) httpx.WriteJson(w, http.StatusBadRequest, Error(errcode,errmsg)) } } // http引數錯誤返回 func ParamErrorResult(r *http.Request,w http.ResponseWriter,err error) { errMsg := fmt.Sprintf("%s ,%s", xerr.MapErrMsg(xerr.REUQES_PARAM_ERROR), err.Error()) httpx.WriteJson(w, http.StatusBadRequest, Error(xerr.REUQES_PARAM_ERROR,errMsg)) } ``` responsebean ```go package xhttp type ( NullJson struct {} ResponseSuccessBean struct { Code int `json:"code"` Msg string `json:"msg"` Data interface{} `json:"data"` } ) func Success(data interface{}) *ResponseSuccessBean { return &ResponseSuccessBean{200, "OK", data} } type ResponseErrorBean struct { Code int `json:"code"` Msg string `json:"msg"` } func Error(errCode int,errMsg string) *ResponseErrorBean { return &ResponseErrorBean{errCode, errMsg} } ``` 放在 lib/xhttp下 ![](https://img2020.cnblogs.com/other/14470/202101/14470-20210125093904490-78890272.jpg) 然後改造了internal/handler/下通過goctl生成的程式碼: ![](https://img2020.cnblogs.com/other/14470/202101/14470-20210125093904817-739151985.jpg) 當然你會說,每次生成完都要手動去改,好麻煩! 噹噹噹當~~~ `goctl` 的 `template` 來咯 https://www.yuque.com/tal-tech/go-zero/mkpuit 然後修改 `~/.goctl/api/handler.tpl`: ```go package handler import ( "net/http" {{.ImportPackages}} ) func {{.HandlerName}}(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { {{if .HasRequest}}var req types.{{.RequestType}} if err := httpx.Parse(r, &req); err != nil { xhttp.ParamErrorResult(r,w,err) return }{{end}} l := logic.New{{.LogicType}}(r.Context(), ctx) resp, err := l.Login(req) xhttp.HttpResult(r,w,resp,err) } } ``` 再重新生成看看,是不是就 `beautiful` 了,哈哈 然後在說我們的 `gateway log`,如果眼神好的使用者,在上面的 `httpresult.go` 中已經看到了 `log` 的身影: ![](https://img2020.cnblogs.com/other/14470/202101/14470-20210125093905156-1733900319.jpg) 是的是的,這樣處理就可以啦,這樣只要有錯誤就會列印日誌了,`go-zero` 已經把 `trace-id` 帶進去了,啥?`trace-id` 不知道是啥?嗯,其實就是把一次請求通過此 `id` 串聯起來,比如你 `user-api` 呼叫 `user->srv` 或者其他 `srv`,那要把他們這一次請求都串聯起來,需要一個唯一標識別,這個 `id` 就是做這個,做鏈路追蹤有很多,比如 `jaeger`、`zipkin`。 ## RPC service ![](https://img2020.cnblogs.com/other/14470/202101/14470-20210125093905433-268942471.jpg) `model`:`rpc` 服務中,官方文件推薦是將 `model` 放在 `services` 目錄下,與每個 `rpc` 服務一層,但是個人感覺每個 `model` 對應一張表,一張表只能由一個服務去控制,哪個服務控制這張表就哪個服務擁有控制這個 `model` 權利,其他服務想訪問就要通過 `grpc`,這是個人的想法,所以我把每個服務自己管控的 `model` 放在了 `internal` 中 `enum`:另外我在服務下加了 `enum` 列舉目錄,因為其他 `rpc` 服務或者 `api` 服務會呼叫這個列舉去比對,我就放在 `internal` 外部 ![](https://img2020.cnblogs.com/other/14470/202101/14470-20210125093905705-1478202430.jpg) ## 框架地址 [https://github.com/tal-tech/go-zero](https://github.com/tal-tech/go-zero) 歡迎使用 `go-zero` 並 **star** 支援我們