企業專案遷移go-zero全攻略(二)
阿新 • • 發佈:2021-01-25
承接上篇:上篇文章講到 `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** 支援我們