go-kit微服務:HTTP REST

本文將使用gokit構建一個簡單的算術運算(兩個整數的加減乘除運算)微服務實例,該服務將以REST方式對外暴露接口,具體要求以下:git

  • 使用gokit構建REST接口;
  • URL格式爲:/calculate/{type}/{a}/{b},請求方法爲POST

Step-0:準備工做

  • golang開發環境(個人環境go1.11.4+Goland+Windows 10);
  • gokit工具集:go get github.com/go-kit/kit
  • http請求路由組件:go get github.com/gorilla/mux

golang開發環境搭建方式能夠自行搜索,IDE能夠根據我的喜愛選擇。github

Step-1:建立Service

按照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
}
複製代碼

Step-2:建立請求、響應模型

請求模型:接收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"`
}
複製代碼

Step-3:建立Endpoint

在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
	}
}
複製代碼

Step-4:建立Transport

Transport層用於接收用戶網絡請求並將其轉爲Endpoint能夠處理的對象,而後交由Endpoint執行,最後將處理結果轉爲響應對象向用戶響應。爲了完成這項工做,Transport須要具有兩個工具方法:ide

  • 解碼器:把用戶的請求內容轉換爲請求對象(ArithmeticRequest);
  • 編碼器:把處理結果轉換爲響應對象(ArithmeticResponse);

下面建立新的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,並將三個參數轉換爲請求對象ArithmeticRequestencodeArithmeticResponse把響應內容轉爲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
}
複製代碼

Step-5:編寫Main方法

到目前爲止,咱們已經爲該服務完成了Service、Endpoint、Transport三個層次的構建工做,只須要經過main方法將它們按照gokit的要求組織起來,而後使用http庫將服務發佈便可。組織步驟以下:

  • 建立Service對象:聲明Service接口,實例化爲ArithmeticService;
  • 建立Endpoint對象;
  • 建立http處理對象handlder;
  • 啓動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)
}
複製代碼

Step-6:編譯&運行

在控制檯中打開項目所在的目錄,執行指令go build,而後運行可執行文件arithmetic_rest_demo.exe,看到如下內容就代表服務啓動成功了:

Http Server start at port:9000
複製代碼

這個時候就能夠經過PostMain進行測試了,效果以下所示:

postman測試效果

本文首發於本人微信公衆號【兮一昂吧】,歡迎掃碼關注!

相關文章
相關標籤/搜索