前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go-kit 构建微服务(1)

go-kit 构建微服务(1)

原创
作者头像
Johns
修改2022-06-30 10:31:44
1.6K0
修改2022-06-30 10:31:44
举报
文章被收录于专栏:代码工具代码工具

简介

Go是一种很好的通用语言,但是微服务需要一定量的专门支持。RPC安全性、系统可观察性、基础设施集成,甚至程序设计等都需要认真考虑。go-kit工具包填补了标准库留下的空白,使Go成为任何组织编写微服务的一流语言。为了快速入门,我们先用go-kit工具包实现一个简单的整数乘法计算服务。

项目的架构分层(官方推荐使用):

|─ go-kit-microservice. # 项目名称

├── cmd # 服务入口

│ ├── client ## 测试服务 Client

| ├── http_client.go

│ └── main.go ### 服务main函数入口

├── internal # 业务代码

│ ├── endpoint ## endpoint 层

| ├── endpoint.go

│ ├── service ## service 层, 用来存放我们的业务逻辑代码

| ├── service.go

│ └── transport ## transport 层

| ├── transport.go

实现

service层

首先,我们在 internal/service包下service.go 里面定义我们的服务接口,同时定义请求和响应的结构体,并且对Service接口进行了一个实现。

代码语言:txt
复制
// service interface
type Service interface {
	Multiply(ctx context.Context, in MultiplyRequest) MultiplyResponse
}

// request struct
type MultiplyRequest struct {
	A int `json:"a"`
	B int `json:"b"`
}

// response struct
type MultiplyResponse struct {
	Res int `json:"res"`
}

// Service struct, has an implementation of Service interface
type baseService struct {
}

func NewService() Service {
	return &baseService{}
}

func (bs baseService) Multiply(ctx context.Context, in MultiplyRequest) MultiplyResponse {
	return MultiplyResponse{
		Res: in.A * in.B,
	}
} 

endpoint层

go-kit通过一个抽象层EndPoint来提供它的大部分功能。这里定义的EndPoints对象就是我们暴露的一个出口,通过这个出口来暴露一个MultiplyEndPoint的端点提供了整数乘法计算的功能。

代码语言:txt
复制
package endpoint

import (
	"context"
	"github.com/go-kit/kit/endpoint"
	"go-kit-microservice/internal/service"
)

type EndPoints struct {
	MultiplyEndPoint endpoint.Endpoint
}

func NewEndpoints(svc service.Service) EndPoints {
	var multiplyEndpoint endpoint.Endpoint
	{
		multiplyEndpoint = makeMultiplyMultiplyEndPoint(svc)
	}
	return EndPoints{MultiplyEndPoint: multiplyEndpoint}
}

func makeMultiplyMultiplyEndPoint(s service.Service) endpoint.Endpoint {
	return func(ctx context.Context, request interface{}) (response interface{}, err error) {
		req := request.(service.MultiplyRequest)
		resp := s.Multiply(ctx, req)
		return resp, nil
	}
}

transport层

由于我们服务可能是给内部用的一些接口,也有可能是给外部调用接口,所以我们还需要一个transport层来抽象这一部分的细节。一般情况下,不同的协议其实只是在请求参数的解码和响应内容的编码上存在差异。 大部分情况可能我们的接口既要支持grpc, 又要支持使用http来访问。为了简单,我们的服务目前仅支持http来访问。

代码语言:txt
复制
package transport

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-kit/kit/endpoint"
	httptransport "github.com/go-kit/kit/transport/http"
	endpoints "go-kit-microservice/internal/endpoint"
	"go-kit-microservice/internal/service"
	"net/http"
	"strconv"
)

func MewHttpHandler(endpoints endpoints.EndPoints) http.Handler {
	options := []httptransport.ServerOption{
		httptransport.ServerErrorEncoder(errorEncoder),
	}

	m := http.NewServeMux()
	m.Handle("/multiply", httptransport.NewServer(
		endpoints.MultiplyEndPoint,
		decodeMultiplyRequest,
		encodeMultiplyResponse,
		options...,
	))

	return m
}

// decode the request to service layer request params
func decodeMultiplyRequest(_ context.Context, r *http.Request) (interface{}, error) {
	var (
		in  service.MultiplyRequest
		err error
	)

	if in.A, err = strconv.Atoi(r.FormValue("a")); err != nil {
		return in, err
	}

	if in.B, err = strconv.Atoi(r.FormValue("b")); err != nil {
		return in, err
	}

	return in, nil
}

// encode the response data to user
func encodeMultiplyResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
	if f, ok := response.(endpoint.Failer); ok && f.Failed() != nil {
		errorEncoder(ctx, f.Failed(), w)
		return nil
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	return json.NewEncoder(w).Encode(response)
}

// error EncodeHandler
func errorEncoder(ctx context.Context, err error, w http.ResponseWriter) {
	w.WriteHeader(http.StatusOK)
	fmt.Println("response failed: ", err.Error())
	e := json.NewEncoder(w).Encode(errorWrapper{Error: err.Error()})
	if e != nil {
		fmt.Println("json encode failed: ", e.Error())
	}

}

type errorWrapper struct {
	Error string `json:"error"`
}

启动

所有的细节都准备好了,我们只需要把服务注册到一个端口上跑起来就可以了

代码语言:txt
复制
package main

import (
	"fmt"
	"github.com/oklog/oklog/pkg/group"
	"go-kit-microservice/internal/endpoint"
	"go-kit-microservice/internal/service"
	"go-kit-microservice/internal/transport"
	"net"
	"net/http"
)

func main() {

	svc := service.NewService()

	endpoints := endpoint.NewEndpoints(svc)

	httpHandler := transport.MewHttpHandler(endpoints)

	var g group.Group
	{
		httpListener, err := net.Listen("tcp", ":8082")
		if err != nil {
			fmt.Println("http server start failed:" + err.Error())
		}

		g.Add(func() error {
			return http.Serve(httpListener, httpHandler)
		}, func(e error) {
			err = httpListener.Close()
			if err != nil {
				fmt.Println("http server close failed", err.Error())
			}
		})
	}
	_ = g.Run()
}

测试验证

go提倡我们为每个接口都写一个客户端去验证启动后接口的情况, 所以我在cmd/client下面新增了一个客户端代码,用来验证程序启动后实际的效果

代码语言:txt
复制
package client

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

const baseUrl = "http://localhost:8081/"

func main() {
	multiplyUrl := baseUrl + "multiply?a=4&b=9"
	resp, err := http.Get(multiplyUrl)
	if err != nil {
		fmt.Println(err.Error())
	} else {
		resp := getHttpResponse(resp)
		fmt.Println("get multiply result:" + resp)
	}

}

func getHttpResponse(resp *http.Response) string {
	data, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		fmt.Println(err.Error())
	}
	return string(data)
}

响应结果:我们可以通过客户端的返回结果知道我们的程序输出是否正常。

代码语言:txt
复制
$ go build http_client.go 
$ ls
http_client     http_client.go
$ ./http_client 
get multiply result:{"res":36}

参考链接:

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 实现
    • service层
      • endpoint层
        • transport层
          • 启动
            • 测试验证
              • 响应结果:我们可以通过客户端的返回结果知道我们的程序输出是否正常。
            • 参考链接:
            相关产品与服务
            测试服务
            测试服务 WeTest 包括标准兼容测试、专家兼容测试、手游安全测试、远程调试等多款产品,服务于海量腾讯精品游戏,涵盖兼容测试、压力测试、性能测试、安全测试、远程调试等多个方向,立体化安全防护体系,保卫您的信息安全。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档