微服务

微服务是一种软件开发技术——面向服务架构(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主要结构:

quark微服务_Endpoint

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"`
}
  1. 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
}
  1. 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
}
  1. 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,
)
......
  1. 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
}
  1. Circuit breaker 断路器
    Circuit breaker模块提供了很多流行的回路断路lib的端点(endpoint)适配器,回路断路器可以避免雪崩,并且提高了针对间歇性错误的弹性,每一个client的端点都应该封装在回路断路器中。
  2. Request tracing 请求追踪
    随着你的基础设施的增长,能够跟踪一个请求变得越来越重要,因为它可以在多个服务中进行穿梭并回到用户。Go kit的 tracing 模块提供了为端点和传输的增强性的绑定功能,以捕捉关于请求的信息,并把它们发送到跟踪系统中。
  3. Service Discovery 服务发现
    如果你的服务调用了其他的服务,需要知道如何找到它(另一个服务),并且应该智能的将负载在这些发现的实例上铺开(即,让被发现的实例智能的分担服务压力)。Go kit的 loadbalancer 模块提供了客户端端点的中间件来解决这类问题