本文將使用gokit構建一個簡單的算術運算(兩個整數的加減乘除運算)微服務實例,該服務將以REST方式對外暴露接口,具體要求以下:git
/calculate/{type}/{a}/{b}
,請求方法爲POST
;go get github.com/go-kit/kit
;go get github.com/gorilla/mux
;golang開發環境搭建方式能夠自行搜索,IDE能夠根據我的喜愛選擇。github
按照gokit的設計理念,Service將做爲核心業務邏輯實現部分。因此,該Service將用於實現本文開頭所說的兩個整數之間的加減乘除運算,其中包含4個方法分別用於加減乘除計算。golang
在GOPATH
下建立目錄gokit-article-demo/arithmetic_rest_demo
,而後新建go文件service.go
,定義接口Service
,代碼以下所示:json
// Service Define a service interface type Service interface { // Add calculate a+b Add(a, b int) int // Subtract calculate a-b Subtract(a, b int) int // Multiply calculate a*b Multiply(a, b int) int // Divide calculate a/b Divide(a, b int) (int, error) } 複製代碼
接下來建立結構ArithmeticService
實現Service
接口。加減乘除的實現很是簡單,只有除法運算須要作下異常判斷,代碼以下所示:bash
//ArithmeticService implement Service interface type ArithmeticService struct { } // Add implement Add method func (s ArithmeticService) Add(a, b int) int { return a + b } // Subtract implement Subtract method func (s ArithmeticService) Subtract(a, b int) int { return a - b } // Multiply implement Multiply method func (s ArithmeticService) Multiply(a, b int) int { return a * b } // Divide implement Divide method func (s ArithmeticService) Divide(a, b int) (int, error) { if b == 0 { return 0, errors.New("the dividend can not be zero!") } return a / b, nil } 複製代碼
請求模型:接收http客戶端的請求後,把請求參數轉爲請求模型對象,用於後續業務邏輯處理。觀察Service接口能夠發現四個接口方法的輸入參數均爲兩個整數,區別在於運算類型不一樣,因此請求模型只需包含三個字段,即:請求類型、第一個整數、第二個整數。微信
響應模型:用於向客戶端響應結果。對於響應模型能夠設置兩個字段:一是結果,用於表示正常狀況下的運算結果;二是錯誤描述,用於表示異常時的錯誤描述。markdown
建立go文件endpoints.go
,編寫以下代碼:網絡
// ArithmeticRequest define request struct type ArithmeticRequest struct { RequestType string `json:"request_type"` A int `json:"a"` B int `json:"b"` } // ArithmeticResponse define response struct type ArithmeticResponse struct { Result int `json:"result"` Error error `json:"error"` } 複製代碼
在gokit中Endpoint是能夠包裝到http.Handler
中的特殊方法,gokit採用裝飾着模式,把Service應該執行的邏輯封裝到Endpoint方法中執行。Endpoint的做用是:調用Service中相應的方法處理請求對象(ArithmeticRequest),返回響應對象(ArithmeticResponse)。接下來在endpoints.go
文件中增長如下代碼:app
// MakeArithmeticEndpoint make endpoint func MakeArithmeticEndpoint(svc Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(ArithmeticRequest) var ( res, a, b int calError error ) a = req.A b = req.B if strings.EqualFold(req.RequestType, "Add") { res = svc.Add(a, b) } else if strings.EqualFold(req.RequestType, "Substract") { res = svc.Subtract(a, b) } else if strings.EqualFold(req.RequestType, "Multiply") { res = svc.Multiply(a, b) } else if strings.EqualFold(req.RequestType, "Divide") { res, calError = svc.Divide(a, b) } else { return nil, ErrInvalidRequestType } return ArithmeticResponse{Result: res, Error: calError}, nil } } 複製代碼
Transport層用於接收用戶網絡請求並將其轉爲Endpoint能夠處理的對象,而後交由Endpoint執行,最後將處理結果轉爲響應對象向用戶響應。爲了完成這項工做,Transport須要具有兩個工具方法:ide
下面建立新的go文件,命名爲transports.go
,並添加以下代碼:
// decodeArithmeticRequest decode request params to struct func decodeArithmeticRequest(_ context.Context, r *http.Request) (interface{}, error) { vars := mux.Vars(r) requestType, ok := vars["type"] if !ok { return nil, ErrorBadRequest } pa, ok := vars["a"] if !ok { return nil, ErrorBadRequest } pb, ok := vars["b"] if !ok { return nil, ErrorBadRequest } a, _ := strconv.Atoi(pa) b, _ := strconv.Atoi(pb) return ArithmeticRequest{ RequestType: requestType, A: a, B: b, }, nil } // encodeArithmeticResponse encode response to return func encodeArithmeticResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { w.Header().Set("Content-Type", "application/json;charset=utf-8") return json.NewEncoder(w).Encode(response) } 複製代碼
decodeArithmeticRequest
從用戶請求中解析請求參數type、a、b,並將三個參數轉換爲請求對象ArithmeticRequest
;encodeArithmeticResponse
把響應內容轉爲json結構,向用戶回寫響應內容。完成以上工做後,就可使用解碼器、編碼器建立HTTP處理方法了,代碼以下所示:
var ( ErrorBadRequest = errors.New("invalid request parameter") ) // MakeHttpHandler make http handler use mux func MakeHttpHandler(ctx context.Context, endpoint endpoint.Endpoint, logger log.Logger) http.Handler { r := mux.NewRouter() options := []kithttp.ServerOption{ kithttp.ServerErrorLogger(logger), kithttp.ServerErrorEncoder(kithttp.DefaultErrorEncoder), } r.Methods("POST").Path("/calculate/{type}/{a}/{b}").Handler(kithttp.NewServer( endpoint, decodeArithmeticRequest, encodeArithmeticResponse, options..., )) return r } 複製代碼
到目前爲止,咱們已經爲該服務完成了Service、Endpoint、Transport三個層次的構建工做,只須要經過main方法將它們按照gokit的要求組織起來,而後使用http庫將服務發佈便可。組織步驟以下:
建立go文件main.go
添加如下代碼:
func main() { ctx := context.Background() errChan := make(chan error) var svc Service svc = ArithmeticService{} endpoint := MakeArithmeticEndpoint(svc) var logger log.Logger { logger = log.NewLogfmtLogger(os.Stderr) logger = log.With(logger, "ts", log.DefaultTimestampUTC) logger = log.With(logger, "caller", log.DefaultCaller) } r := MakeHttpHandler(ctx, endpoint, logger) go func() { fmt.Println("Http Server start at port:9000") handler := r errChan <- http.ListenAndServe(":9000", handler) }() go func() { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) errChan <- fmt.Errorf("%s", <-c) }() fmt.Println(<-errChan) } 複製代碼
在控制檯中打開項目所在的目錄,執行指令go build
,而後運行可執行文件arithmetic_rest_demo.exe
,看到如下內容就代表服務啓動成功了:
Http Server start at port:9000
複製代碼
這個時候就能夠經過PostMain進行測試了,效果以下所示:
本文首發於本人微信公衆號【兮一昂吧】,歡迎掃碼關注!