go-kit微服务:HTTP REST

本文将使用gokit构建一个简单的算术运算(两个整数的加减乘除运算)微服务实例,该服务将以REST方式对外暴露接口,具体要求如下:

  • 使用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可以根据个人喜好选择。

Step-1:创建Service

按照gokit的设计理念,Service将作为核心业务逻辑实现部分。所以,该Service将用于实现本文开头所说的两个整数之间的加减乘除运算,其中包含4个方法分别用于加减乘除计算。

GOPATH下创建目录gokit-article-demo/arithmetic_rest_demo,然后新建go文件service.go,定义接口Service,代码如下所示:

// 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接口。加减乘除的实现非常简单,只有除法运算需要做下异常判断,代码如下所示:

//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接口可以发现四个接口方法的输入参数均为两个整数,区别在于运算类型不同,所以请求模型只需包含三个字段,即:请求类型、第一个整数、第二个整数。

响应模型:用于向客户端响应结果。对于响应模型可以设置两个字段:一是结果,用于表示正常情况下的运算结果;二是错误描述,用于表示异常时的错误描述。

创建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文件中增加以下代码:

// 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需要具备两个工具方法:

  • 解码器:把用户的请求内容转换为请求对象(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进行测试了,效果如下所示:

本文首发于本人微信公众号【兮一昂吧】,欢迎扫码关注!

转载于:https://juejin.im/post/5c6eaff8e51d4572c9581069

你可能感兴趣的:(go-kit微服务:HTTP REST)