微服务架构 | Go-Kit 初探

微服务

微服务是一种软件开发技术——面向服务架构(SOA)架构风格的变体,它将应用程序构建为松散耦合服务的集合。微服务是指开发一个单个小型的但有业务功能的服务,每个服务都有自己的处理和轻量通讯机制,可以部署在单个或多个服务器上。当服务发生错误时,如果每个服务都要同时修改,那么它们就不是微服务,因为它们紧耦合在一起。

  • 在微服务架构中,服务是细粒度的,协议是轻量级的。将应用程序分解成较小服务实现模块化。这使得应用程序更容易理解、开发、测试,架构更有弹性。
  • 小型自治团队能够独立开发、部署和扩展各自的服务,从而实现开发的并行化。
  • 基于微服务的体系结构支持连续交付和部署。

Microservices are a software development technique—a variant of the service-oriented architecture (SOA) architectural style that structures an application as a collection of loosely coupled services. --wikipedia

Go-Kit(A toolkit for microservices)

go-kit自称为toolkit,并不是framework,也就是go-kit是将一系列的服务集合在一起,提供接口,从而让开发者自由组合搭建自己的微服务项目。go-kit使用者可以将精力集中在业务逻辑上。
go-kit主要结构:
在这里插入图片描述

Go-Kit组件
  1. Endpoint 端点
    go-kit使用一个抽象的Endpoint来表示每个服务提供的方法。在这个定义的类型里面会去调用service层的方法,组装成response返回。

    import "github.com/go-kit/kit/endpoint"
    // type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error)
    
    // MakeGetSpecEndpoint returns an endpoint that invokes GetSpec on the service.
    func MakeGetSpecEndpoint(s CptService) endpoint.Endpoint {
    	return func(ctx context.Context, request interface{}) (interface{}, error) {
    		req := request.(Request)
    		status, errinfo, data := s.GetSpec(ctx, req.TaskId)
    		return Response{
    			Status:  status,
    			Errinfo: errinfo,
    			Data:    data,
    		}, nil
    	}
    }
    

    其中,GetSpecRequest是我们的服务CptService在处理此请求时所需要参数对应的结构体。

    // 在业务逻辑中,处理此请求只需要Id一个参数,所以GetSpecRequest对应如下
    type GetSpecRequest struct {
    	TaskId string `json:"taskId"`
    }
    
  2. Transport 数据传输(序列化和反序列化)
    transport 模块提供将特定的序列化算法绑定到端点的辅助方法。

    // decodeGetSpecRequest is a transport/http.DecodeRequestFunc that decodes a
    // JSON-encoded request from the HTTP request body.
    func decodeGetSpecRequest(_ context.Context, r *http.Request) (interface{}, error) {
    	vars := mux.Vars(r)
    	taskId, ok := vars["taskId"]
    	if !ok {
    		return nil, errors.New("not a valid taskId")
    	}
    	req := endpoint.GetSpecRequest{
    		TaskId: taskId,
    	}
    	return req, nil
    }
    
    // encodeGetSpecResponse is a transport/http.EncodeResponseFunc that encodes
    // the response as JSON to the response writer
    func encodeGetSpecResponse(ctx context.Context, w http.ResponseWriter, response interface{}) (err error) {
    	w.Header().Set("Content-Type", "application/json; charset=utf-8")
    	err = json.NewEncoder(w).Encode(response)
    	return
    }
    
  3. Logging 日志
    日志可以方便我们追踪服务调用的过程,同时方便我们调式错误。go-kit为我们提供了易用的log模块。

    // 此代码实现的是应用层日志,也可以和下方Rate Limiter实现方法一样,实现tranport logging
    // log middleware
    import 	"github.com/go-kit/kit/log"
    type LoggingMiddleware struct {
    	Logger log.Logger
    	Next   CptService
    }
    
    func (l *LoggingMiddleware) GetSpec(ctx context.Context, taskId string) (status bool, errinfo string, data interface{}) {
    	defer func(begin time.Time) {
    		l.Logger.Log(
    			"method", "GetSpec",
    			"input", fmt.Sprintf("%#v", Request{TaskId: taskId}),			# log模块并不支持复杂的数据类型,会产生`unsuported value type`的问题,我们可以使用fmt.Sprintf()来将其转化为字符串再输出。
    			"output", fmt.Sprintf("%#v", Response{status, errinfo, data}),
    			"took", time.Since(begin),
    		)
    	}(time.Now())
    	status, errinfo, data = l.Next.GetSpec(ctx, taskId)
    	return
    }
    
  4. Rate Limiter 限流器
    限流器模块提供了到限流器代码包的端点适配器。限流器对服务端和客户端同等生效,使用限流器可以强制进、出请求量在阈值以下。使用限流器能限制访问后端的流量,起到一个保护作用,被限制的流量,可以根据具体的业务逻辑去处理,直接返回错误或者返回默认值。
    go-kit的限流器模块是封装了golang自带的golang.org/x/time/rate包来实现的,提供了两种限流:DelayingLimiter【限流延迟访问】ErroringLimiter【限流错误返回】

    // NewDelayingLimiter是go-kit实现的ratelimiter,我们可以直接使用。
    func NewDelayingLimiter(limit ratelimit.Waiter) endpoint.Middleware {
       return func(next endpoint.Endpoint) endpoint.Endpoint {
          return func(ctx context.Context, request interface{}) (interface{}, error) {
             if err := limit.Wait(ctx); err != nil {
                return nil, err
             }
             return next(ctx, request)
          }
       }
    }
    // 如何使用此中间件
    import (
    	"golang.org/x/time/rate"
    	"github.com/go-kit/kit/ratelimit"
    )
    limiter := rate.NewLimiter(rate.Every(time.Second * 1), 1) 
    GetSpecEndpoint = ratelimit.NewDelayingLimiter(limiter)(GetSpecEndpoint) 
    getSpecHandler := httptransport.NewServer(
    	GetSpecEndpoint,
    	decodes.GetSpecDecode,
    	encodes.GetSpecEncode,
    )
    ......
    
  5. Metrics 指标度量
    直到服务经过了跟踪计数、延迟、健康状况和其他的周期性的或针对每个请求信息的仪表盘化,才能被认为是“生产环境”完备的。Go kit 的 metric 模块为你的服务提供了通用并健壮的接口集合。

    import 	"github.com/go-kit/kit/metrics"
    type InstrumentingMiddleware struct {
    	RequestCount   metrics.Counter
    	RequestLatency metrics.Histogram
    	Next           CptService
    }
    
    func (i *InstrumentingMiddleware) GetSpec(ctx context.Context, taskId string) (status bool, errinfo string, data interface{}) {
    	defer func(begin time.Time) {
    		lvs := []string{"method", "GetSpec", "error", errinfo}
    		i.RequestCount.With(lvs...).Add(1)
    		i.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds())
    	}(time.Now())
    	status, errinfo, data = i.Next.GetSpec(ctx, taskId)
    	return
    }
    
  6. Circuit breaker 断路器
    Circuit breaker模块提供了很多流行的回路断路lib的端点(endpoint)适配器,回路断路器可以避免雪崩,并且提高了针对间歇性错误的弹性,每一个client的端点都应该封装在回路断路器中。

  7. Request tracing 请求追踪
    随着你的基础设施的增长,能够跟踪一个请求变得越来越重要,因为它可以在多个服务中进行穿梭并回到用户。Go kit的 tracing 模块提供了为端点和传输的增强性的绑定功能,以捕捉关于请求的信息,并把它们发送到跟踪系统中。

  8. Service Discovery 服务发现
    如果你的服务调用了其他的服务,需要知道如何找到它(另一个服务),并且应该智能的将负载在这些发现的实例上铺开(即,让被发现的实例智能的分担服务压力)。Go kit的 loadbalancer 模块提供了客户端端点的中间件来解决这类问题

【参考链接】

  1. go-kit 示例
  2. https://en.wikipedia.org/wiki/Microservices
  3. https://www.cnblogs.com/wintersun/p/6219259.html
  4. https://blog.csdn.net/warrior_0319/article/details/77548958
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于go-kit的简单例子: ``` package main import ( "context" "fmt" "net/http" "os" "os/signal" "syscall" "time" "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" "github.com/go-kit/kit/transport/http" ) func main() { var logger log.Logger { logger = log.NewLogfmtLogger(os.Stderr) logger = log.With(logger, "timestamp", log.DefaultTimestampUTC) logger = log.With(logger, "caller", log.DefaultCaller) } var ( listenAddr = ":8080" ) var svc Service { svc = myService{} svc = loggingMiddleware(logger)(svc) } var uppercaseHandler http.Handler { uppercaseHandler = http.NewServer( makeUppercaseEndpoint(svc), decodeUppercaseRequest, encodeResponse, ) } http.Handle("/uppercase", uppercaseHandler) errs := make(chan error, 2) go func() { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) errs <- fmt.Errorf("%s", <-c) }() go func() { level.Info(logger).Log("msg", "HTTP", "addr", listenAddr) errs <- http.ListenAndServe(listenAddr, nil) }() level.Error(logger).Log("exit", <-errs) } // Service provides a simple string manipulation service. type Service interface { Uppercase(context.Context, string) (string, error) } type myService struct{} func (myService) Uppercase(_ context.Context, s string) (string, error) { if s == "" { return "", ErrEmpty } return strings.ToUpper(s), nil } func makeUppercaseEndpoint(svc Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(uppercaseRequest) v, err := svc.Uppercase(ctx, req.S) if err != nil { return uppercaseResponse{v, err.Error()}, nil } return uppercaseResponse{v, ""}, nil } } type uppercaseRequest struct { S string `json:"s"` } type uppercaseResponse struct { V string `json:"v"` Err string `json:"err,omitempty"` } func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) { var request uppercaseRequest if err := json.NewDecoder(r.Body).Decode(&request); err != nil { return nil, err } return request, nil } func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { return json.NewEncoder(w).Encode(response) } func loggingMiddleware(logger log.Logger) Middleware { return func(next Service) Service { return logmw{logger, next} } } type Middleware func(Service) Service type logmw struct { logger log.Logger next Service } func (mw logmw) Uppercase(ctx context.Context, s string) (output string, err error) { defer func(begin time.Time) { level.Info(mw.logger).Log( "method", "uppercase", "input", s, "output", output, "err", err, "took", time.Since(begin), ) }(time.Now()) output, err = mw.next.Uppercase(ctx, s) return } var ErrEmpty = errors.New("empty string") ``` 这个例子中定义了一个Service,提供了一个Uppercase方法,将输入字符串转换为大写。使用go-kit的httptransport将该方法封装成HTTP API。另外,还增加了一个loggingMiddleware,用于日志记录。 其中,Service、Endpoint和Middleware是go-kit的核心概念,用于实现微服务的组件化。Middleware用于对Service进行装饰,Endpoint用于将Service的方法封装成一个HTTP API,具有了统一的传输层约定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值