本文将使用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进行测试了,效果以下所示:
本文首发于本人微信公众号【兮一昂吧】,欢迎扫码关注!