Home / PostsPost

golang consul 实现简简易的api网关

嘟噜聪2022/04/02 11:03:05 [Golang] [kplcloud] [go] [go-kit] [consul] [registry] [注册中心] [服务发现] [gateway] [网关] 642人已阅

简介 将consul作为服务作为注册中心实现服务发现> 本期介绍如何通过go-kit写一个简易的网关代理## 微服务> 微服务架构是一种应用架构类型,其中应用会开发为一系列服务。它提供了独立

golang consul 实现简简易的api网关

将consul作为服务作为注册中心实现服务发现

本期介绍如何通过go-kit写一个简易的网关代理

微服务

微服务架构是一种应用架构类型,其中应用会开发为一系列服务。它提供了独立开发、部署和维护微服务架构图和服务的框架。

在微服务架构中,每个微服务都是独立的服务,旨在容纳一种应用特性并处理离散的任务。每个微服务都通过简单的接口与其他服务通信,以解决业务问题。

简单的微服务架构

简单的架构就是服务网关作为统一出入口。

服务网关不紧紧对流量进行转发还能增加鉴权、审计、告警等功能。

以下案例是将用户中心、订单中心、支付中心拆成了多个服务,每个服务可以独立部署、扩容等。

微服务启动时向consul将自己的信息如ip:端口、名称、check地址等等信息发送给consul,consul会对你这注册的服务进行健康检查等。

网关服务除了将自己注册到consul之外还需实时监听相关服务的变更信息并同步到网关服务。

所有外部流量经过网关鉴权之后根据相关规则转发到应用的服务。

实践

网关实践

本案例主要以http的方式作为演示,grpc的方式这边暂时不做演示,以后有机会再来讲解go-kit grpc网关的使用。

// main.go
import (
	"log"
	consulsd "github.com/go-kit/kit/sd/consul"
	consulapi "github.com/hashicorp/consul/api"
	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/sd"
	"github.com/go-kit/kit/sd/lb"
	"github.com/gorilla/mux"
	"net/http"
)

func main() {
	logger = log.NewLogfmtLogger(log.StdlibWriter{})

	// 连接到consul
	consulConfig := consulapi.DefaultConfig()
	consulConfig.Address = "consul address"
	consulConfig.Token = "consul token"	consulClient, err := consulapi.NewClient(consulConfig)
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}
	client = consulsd.NewClient(consulClient)
	
	var (
		opts = []kithttp.ServerOption{
			kithttp.ServerErrorEncoder(encode.JsonError),
			kithttp.ServerBefore(func(ctx context.Context, request *http.Request) context.Context {
				return ctx
			}),
		}
		clientOpts = []kithttp.ClientOption{
			kithttp.SetClient(nil), // 设置请求客户端
			// kithttp.ClientBefore(kittracing.ContextToHTTP(tracer, logger)), // 链路追踪设置
		}
		authEms = [] endpoint.Middleware{
			// 审计等操作
			// 用户鉴权验证
		}
		retryMax = 2
		retryTimeout = time.Second * 5
	)
	
	userInstancer := consulsd.NewInstancer(client, logger, "your user service name", []string{}, true)
	userFactory := serviceFactory(clientOpts)
	userEndpointer := sd.NewEndpointer(userInstancer, userFactory, logger)
	userBalancer := lb.NewRoundRobin(userEndpointer)  // 负载均衡
	userRetry := lb.Retry(retryMax, retryTimeout, userBalancer) // 重试策略
	
	// kittracing.TraceClient(tracer, "paas-api")(userRetry) 如果需要tracer的话,加上
	
	r := mux.NewRouter()
	
	r.PathPrefix("/user").
		Handler(http.StripPrefix("/user", userMakeHTTPHandler(userRetry, authEms, opts)))
	http.Handle("/", r)
	
	return http.ListenAndServe(":8080", nil)
}
// factory.go
import (
	"context"
	"encoding/json"
	"github.com/go-kit/kit/endpoint"
	"github.com/go-kit/kit/sd"
	kithttp "github.com/go-kit/kit/transport/http"
	"github.com/pkg/errors"
	"io"
	"net/http"
	"net/url"
	"strings"
)

func serviceFactory(opts []kithttp.ClientOption) sd.Factory {
	return func(instance string) (endpoint.Endpoint, io.Closer, error) {
		if !strings.HasPrefix(instance, "http") {
			instance = "http://" + instance
		}
		tgt, err := url.Parse(instance)
		if err != nil {
			return nil, nil, err
		}

		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			req := request.(*http.Request)
			tgt.Path = req.URL.Path
			tgt.RawQuery = req.URL.RawQuery
			eps := kithttp.NewClient(req.Method, tgt, func(ctx context.Context, r *http.Request, request interface{}) error {
				r.WithContext(ctx)
				sourceReq := request.(*http.Request)
				r.Header = sourceReq.Header
				r.Body = sourceReq.Body
				return nil
			}, func(ctx context.Context, res *http.Response) (response interface{}, err error) {
				if res.StatusCode != http.StatusOK {
					return nil, errors.New(res.Status)
				}
				// 这里可以做一些 操作,如果返回的结果有问题直接向上抛err
				// 这只是个例子,有些情况是各个子服务返回的结果不一致,需要在这里进行处理
				var b []byte
				if res.Body != nil {
					// 返回的数据结构没有统一无法做解析
					b, err = ioutil.ReadAll(res.Body)
				}
				return Response{
					Code:        res.StatusCode,
					Body:        b,
					HttpHeaders: res.Header.Clone(),
				}, err
			},
				opts...,
			).Endpoint()

			resp, err := eps(ctx, req)
			if err != nil {
				return nil, errors.Wrap(err, "kithttp.client")
			}
			return resp, nil
		}, nil, nil
	}
}
import (
	"context"
	"github.com/go-kit/kit/endpoint"
	kithttp "github.com/go-kit/kit/transport/http"
	"gitlab.creditease.corp/paas/gateway/src/encode"
	"net/http"
	"github.com/pkg/errors"
	"strings"
)

func userMakeHTTPHandler(svc endpoint.Endpoint, dmw []endpoint.Middleware, opts []kithttp.ServerOption) http.Handler {

	for _, m := range dmw {
		svc = m(svc)
	}
	return kithttp.NewServer(
		svc,
		func(ctx context.Context, req *http.Request) (request interface{}, err error) {
			return req, nil
		},
		JsonResponse,
		opts...,
	)

}


func JsonResponse(ctx context.Context, w http.ResponseWriter, response interface{}) (err error) {
	// 对返回结果前的处理
	return kithttp.EncodeJSONResponse(ctx, w, resp)
}

type Response struct {
	Success     bool        `json:"success"`
	Code        int         `json:"code"`
	Data        interface{} `json:"data,omitempty"`
	Error       string      `json:"message,omitempty"`
	TraceId     string      `json:"traceId"`
	HttpHeaders http.Header `json:"-"`
	StatusCode  int         `json:"-"`
	Body        []byte      `json:"-"`
}

func (r Response) Headers() http.Header {
	return r.HttpHeaders
}

func (r Response) StatusCoder() int {
	return r.StatusCode
}


尾巴

不用多说了,懂的都懂。

很赞哦! (5)

文章评论

站点信息

  • 微信公众号