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