1 介绍
1.1 定义
01.技术定位
a.工具库特性
a.功能说明
go-kit是一个用于构建微服务的Go语言工具库,提供了一套标准化的组件和模式来解决分布式系统开发中的常见问题。它不是一个框架,而是一组可组合的库,开发者可以根据需求选择性使用。go-kit遵循Go语言的设计哲学,强调简洁性和可组合性,通过提供传输层抽象、服务端点定义、中间件机制等核心组件,帮助开发者快速构建可维护、可扩展的微服务系统。
b.代码示例
---
// go-kit基础服务定义示例
package main
import (
"context"
"github.com/go-kit/kit/endpoint"
)
// 定义服务接口
type StringService interface {
Uppercase(context.Context, string) (string, error)
Count(context.Context, string) int
}
// 实现服务接口
type stringService struct{}
func (stringService) Uppercase(_ context.Context, s string) (string, error) {
return strings.ToUpper(s), nil
}
func (stringService) Count(_ context.Context, s string) int {
return len(s)
}
---
b.设计理念
a.功能说明
go-kit采用洋葱架构模式,将服务分为三个核心层次:Service层负责业务逻辑实现,Endpoint层处理请求响应转换,Transport层负责协议适配。这种分层设计使得业务逻辑与传输协议解耦,同一个服务可以轻松支持HTTP、gRPC、Thrift等多种传输协议。中间件机制贯穿各个层次,提供日志、监控、限流、熔断等横切关注点的统一处理方式。
b.代码示例
---
// Endpoint层定义
func makeUppercaseEndpoint(svc StringService) 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"`
}
---
02.核心价值
a.标准化开发
a.功能说明
go-kit为微服务开发提供了一套标准化的开发模式和最佳实践,包括服务定义、端点创建、传输层绑定、中间件应用等环节都有明确的规范。这种标准化降低了团队协作成本,新成员可以快速理解项目结构,代码审查和维护也更加容易。同时,标准化的模式使得服务间的集成更加顺畅,减少了因实现差异导致的问题。
b.代码示例
---
// 标准化的服务构建流程
func main() {
// 1. 创建服务实例
var svc StringService
svc = stringService{}
// 2. 添加中间件
svc = loggingMiddleware{logger, svc}
svc = instrumentingMiddleware{requestCount, requestLatency, countResult, svc}
// 3. 创建端点
uppercaseEndpoint := makeUppercaseEndpoint(svc)
countEndpoint := makeCountEndpoint(svc)
// 4. 添加端点中间件
uppercaseEndpoint = loggingMiddleware(logger)(uppercaseEndpoint)
uppercaseEndpoint = rateLimitMiddleware(rate.NewLimiter(1, 100))(uppercaseEndpoint)
// 5. 绑定传输层
uppercaseHandler := httptransport.NewServer(
uppercaseEndpoint,
decodeUppercaseRequest,
encodeResponse,
)
}
---
b.可观测性
a.功能说明
go-kit内置了对日志、监控指标、分布式追踪的完整支持,通过中间件机制可以无侵入地为服务添加可观测性能力。支持与Prometheus、Zipkin、Jaeger等主流监控系统集成,提供请求计数、延迟统计、错误率监控等关键指标。这些能力对于生产环境的服务运维至关重要,帮助开发者快速定位问题、分析性能瓶颈。
b.代码示例
---
// 监控中间件示例
import (
"github.com/go-kit/kit/metrics"
"github.com/go-kit/kit/metrics/prometheus"
stdprometheus "github.com/prometheus/client_golang/prometheus"
)
func instrumentingMiddleware(
requestCount metrics.Counter,
requestLatency metrics.Histogram,
countResult metrics.Histogram,
) ServiceMiddleware {
return func(next StringService) StringService {
return instrmw{requestCount, requestLatency, countResult, next}
}
}
type instrmw struct {
requestCount metrics.Counter
requestLatency metrics.Histogram
countResult metrics.Histogram
StringService
}
func (mw instrmw) Uppercase(ctx context.Context, s string) (output string, err error) {
defer func(begin time.Time) {
mw.requestCount.With("method", "uppercase").Add(1)
mw.requestLatency.With("method", "uppercase").Observe(time.Since(begin).Seconds())
}(time.Now())
output, err = mw.StringService.Uppercase(ctx, s)
return
}
---
1.2 核心概念
01.服务层Service
a.业务逻辑封装
a.功能说明
Service层是go-kit架构的核心,负责封装所有业务逻辑。它通常定义为Go接口,包含服务提供的所有业务方法。Service层不关心传输协议、序列化方式或中间件逻辑,只专注于业务功能的实现。这种设计使得业务逻辑可以独立测试,不依赖于任何外部框架或传输机制。Service接口的方法签名通常接收context.Context作为第一个参数,用于传递请求上下文和超时控制。
b.代码示例
---
// Service层接口定义
package service
import "context"
// UserService 用户服务接口
type UserService interface {
// CreateUser 创建用户
CreateUser(ctx context.Context, username, email string) (User, error)
// GetUser 获取用户信息
GetUser(ctx context.Context, id string) (User, error)
// UpdateUser 更新用户信息
UpdateUser(ctx context.Context, id string, updates UserUpdates) error
// DeleteUser 删除用户
DeleteUser(ctx context.Context, id string) error
}
// User 用户模型
type User struct {
ID string `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Created int64 `json:"created"`
}
// UserUpdates 用户更新字段
type UserUpdates struct {
Username *string `json:"username,omitempty"`
Email *string `json:"email,omitempty"`
}
---
b.服务实现
a.功能说明
Service接口的具体实现包含实际的业务逻辑处理,可能涉及数据库操作、外部API调用、业务规则验证等。实现类通常持有必要的依赖项,如数据库连接、缓存客户端、其他服务的客户端等。通过依赖注入的方式,可以方便地进行单元测试和集成测试。Service实现应该保持纯粹的业务逻辑,避免混入日志、监控等横切关注点,这些功能应该通过中间件来实现。
b.代码示例
---
// Service接口实现
package service
import (
"context"
"errors"
"time"
)
type userService struct {
db Database
cache Cache
}
// NewUserService 创建用户服务实例
func NewUserService(db Database, cache Cache) UserService {
return &userService{
db: db,
cache: cache,
}
}
func (s *userService) CreateUser(ctx context.Context, username, email string) (User, error) {
// 验证输入
if username == "" || email == "" {
return User{}, errors.New("username and email are required")
}
// 创建用户对象
user := User{
ID: generateID(),
Username: username,
Email: email,
Created: time.Now().Unix(),
}
// 保存到数据库
if err := s.db.SaveUser(ctx, user); err != nil {
return User{}, err
}
// 更新缓存
s.cache.Set(ctx, "user:"+user.ID, user, 5*time.Minute)
return user, nil
}
func (s *userService) GetUser(ctx context.Context, id string) (User, error) {
// 先查缓存
if user, ok := s.cache.Get(ctx, "user:"+id); ok {
return user.(User), nil
}
// 查数据库
user, err := s.db.GetUser(ctx, id)
if err != nil {
return User{}, err
}
// 写入缓存
s.cache.Set(ctx, "user:"+id, user, 5*time.Minute)
return user, nil
}
---
02.端点层Endpoint
a.请求响应转换
a.功能说明
Endpoint是go-kit中连接Service层和Transport层的桥梁,它将具体的业务方法抽象为统一的函数签名。每个Endpoint接收context.Context和request接口,返回response接口和error。Endpoint的主要职责是将传输层的请求数据转换为Service方法的参数,调用Service方法,然后将返回结果封装为响应对象。这种抽象使得同一个Service可以通过不同的Endpoint暴露给不同的传输协议。
b.代码示例
---
// Endpoint定义
package endpoint
import (
"context"
"github.com/go-kit/kit/endpoint"
)
// MakeCreateUserEndpoint 创建用户端点
func MakeCreateUserEndpoint(svc service.UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := svc.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err.Error()}, nil
}
return CreateUserResponse{User: user}, nil
}
}
// MakeGetUserEndpoint 获取用户端点
func MakeGetUserEndpoint(svc service.UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(GetUserRequest)
user, err := svc.GetUser(ctx, req.ID)
if err != nil {
return GetUserResponse{Err: err.Error()}, nil
}
return GetUserResponse{User: user}, nil
}
}
// 请求响应结构
type CreateUserRequest struct {
Username string `json:"username"`
Email string `json:"email"`
}
type CreateUserResponse struct {
User service.User `json:"user,omitempty"`
Err string `json:"err,omitempty"`
}
type GetUserRequest struct {
ID string `json:"id"`
}
type GetUserResponse struct {
User service.User `json:"user,omitempty"`
Err string `json:"err,omitempty"`
}
---
b.端点组合
a.功能说明
Endpoint可以通过中间件进行组合和增强,形成处理链。中间件可以在Endpoint执行前后添加额外的处理逻辑,如日志记录、性能监控、限流控制、熔断保护等。多个中间件可以链式组合,按照添加顺序依次执行。这种设计模式使得横切关注点的实现与业务逻辑完全解耦,提高了代码的可维护性和可测试性。
b.代码示例
---
// Endpoint中间件组合
package endpoint
import (
"context"
"time"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/metrics"
)
// LoggingMiddleware 日志中间件
func LoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
logger.Log("msg", "calling endpoint", "request", request)
defer func(begin time.Time) {
logger.Log(
"msg", "called endpoint",
"request", request,
"response", response,
"err", err,
"took", time.Since(begin),
)
}(time.Now())
return next(ctx, request)
}
}
}
// InstrumentingMiddleware 监控中间件
func InstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
defer func(begin time.Time) {
duration.With("success", fmt.Sprint(err == nil)).Observe(time.Since(begin).Seconds())
}(time.Now())
return next(ctx, request)
}
}
}
// 应用中间件
func ApplyMiddleware(e endpoint.Endpoint, logger log.Logger, duration metrics.Histogram) endpoint.Endpoint {
e = LoggingMiddleware(logger)(e)
e = InstrumentingMiddleware(duration)(e)
return e
}
---
1.3 优缺点
01.优势
a.架构清晰
a.功能说明
go-kit采用分层架构设计,将服务分为Service、Endpoint、Transport三层,每层职责明确,边界清晰。这种架构使得代码组织结构化,易于理解和维护。业务逻辑与传输协议完全解耦,同一个服务可以轻松支持多种传输方式。分层设计也便于团队协作,不同层次可以由不同的开发人员负责,降低了开发复杂度。
b.代码示例
---
// 清晰的分层架构示例
package main
// Service层:纯业务逻辑
type CalculatorService interface {
Add(a, b int) int
Subtract(a, b int) int
}
type calculatorService struct{}
func (calculatorService) Add(a, b int) int { return a + b }
func (calculatorService) Subtract(a, b int) int { return a - b }
// Endpoint层:请求响应转换
func makeAddEndpoint(svc CalculatorService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(addRequest)
result := svc.Add(req.A, req.B)
return addResponse{Result: result}, nil
}
}
// Transport层:HTTP绑定
func makeHTTPHandler(endpoints Endpoints) http.Handler {
r := mux.NewRouter()
r.Methods("POST").Path("/add").Handler(httptransport.NewServer(
endpoints.AddEndpoint,
decodeAddRequest,
encodeResponse,
))
return r
}
// Transport层:gRPC绑定(同一个Service)
func makeGRPCServer(endpoints Endpoints) pb.CalculatorServer {
return &grpcServer{
add: grpctransport.NewServer(
endpoints.AddEndpoint,
decodeGRPCAddRequest,
encodeGRPCAddResponse,
),
}
}
---
b.可扩展性强
a.功能说明
go-kit的中间件机制提供了强大的扩展能力,可以在不修改业务代码的情况下添加新功能。中间件可以应用在Service层、Endpoint层和Transport层,实现日志、监控、限流、熔断等横切关注点。中间件采用装饰器模式,可以灵活组合,按需启用或禁用。这种设计使得系统功能可以渐进式增强,降低了维护成本。
b.代码示例
---
// 灵活的中间件扩展
package middleware
import (
"context"
"time"
)
// Service层中间件:日志
func LoggingMiddleware(logger log.Logger) ServiceMiddleware {
return func(next StringService) StringService {
return loggingMiddleware{logger, next}
}
}
type loggingMiddleware struct {
logger log.Logger
next StringService
}
func (mw loggingMiddleware) Uppercase(ctx context.Context, s string) (output string, err error) {
defer func(begin time.Time) {
mw.logger.Log(
"method", "uppercase",
"input", s,
"output", output,
"err", err,
"took", time.Since(begin),
)
}(time.Now())
return mw.next.Uppercase(ctx, s)
}
// Endpoint层中间件:限流
func RateLimitMiddleware(limit rate.Limiter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if !limit.Allow() {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
// 组合多个中间件
func BuildService() StringService {
var svc StringService
svc = basicService{}
svc = LoggingMiddleware(logger)(svc)
svc = InstrumentingMiddleware(counter, histogram)(svc)
svc = CachingMiddleware(cache)(svc)
return svc
}
---
c.生产就绪
a.功能说明
go-kit内置了生产环境所需的各种能力,包括日志记录、指标监控、分布式追踪、服务发现、负载均衡、熔断降级等。这些功能都经过了大规模生产环境的验证,可以直接使用。go-kit与主流的监控系统如Prometheus、Zipkin、Jaeger等无缝集成,提供了开箱即用的可观测性支持。这大大降低了微服务系统从开发到生产部署的门槛。
b.代码示例
---
// 生产就绪的服务配置
package main
import (
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/metrics/prometheus"
"github.com/go-kit/kit/sd"
"github.com/go-kit/kit/sd/consul"
stdprometheus "github.com/prometheus/client_golang/prometheus"
)
func main() {
// 日志配置
var logger log.Logger
logger = log.NewLogfmtLogger(os.Stderr)
logger = log.With(logger, "ts", log.DefaultTimestampUTC)
logger = log.With(logger, "caller", log.DefaultCaller)
// 监控指标
fieldKeys := []string{"method", "error"}
requestCount := prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "my_service",
Subsystem: "string_service",
Name: "request_count",
Help: "Number of requests received.",
}, fieldKeys)
requestLatency := prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "my_service",
Subsystem: "string_service",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, fieldKeys)
// 服务发现(Consul)
consulClient, _ := api.NewClient(api.DefaultConfig())
client := consul.NewClient(consulClient)
registrar := consul.NewRegistrar(client, &api.AgentServiceRegistration{
ID: "my-service-1",
Name: "my-service",
Port: 8080,
Address: "localhost",
Check: &api.AgentServiceCheck{
HTTP: "http://localhost:8080/health",
Interval: "10s",
Timeout: "1s",
},
})
registrar.Register()
defer registrar.Deregister()
// 构建服务
var svc StringService
svc = stringService{}
svc = loggingMiddleware{logger, svc}
svc = instrumentingMiddleware{requestCount, requestLatency, svc}
}
---
02.局限性
a.学习曲线
a.功能说明
go-kit的概念较多,包括Service、Endpoint、Transport、Middleware等多个层次,初学者需要时间理解这些概念及其关系。相比于传统的Web框架,go-kit的抽象层次更高,代码结构更复杂。开发者需要理解函数式编程的思想,掌握中间件的组合模式。对于简单的应用,go-kit可能显得过于复杂,增加了不必要的开发成本。
b.代码示例
---
// 初学者可能觉得复杂的代码结构
package main
// 需要理解多个概念
type StringService interface { /* Service层 */ }
type Endpoints struct { /* Endpoint层 */ }
type Middleware func(endpoint.Endpoint) endpoint.Endpoint /* 中间件 */
// 需要编写大量样板代码
func makeUppercaseEndpoint(svc StringService) 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
}
}
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)
}
// 对于简单的Hello World,代码量较大
---
b.样板代码多
a.功能说明
使用go-kit开发服务需要编写较多的样板代码,包括请求响应结构定义、编解码函数、Endpoint创建函数等。每个服务方法都需要定义对应的请求响应类型,编写Endpoint转换逻辑和编解码函数。虽然这些代码结构清晰,但对于小型项目来说可能显得繁琐。不过,这些样板代码可以通过代码生成工具自动生成,减轻开发负担。
b.代码示例
---
// 每个方法需要的样板代码
package endpoint
// 1. 定义请求响应结构
type uppercaseRequest struct {
S string `json:"s"`
}
type uppercaseResponse struct {
V string `json:"v"`
Err string `json:"err,omitempty"`
}
// 2. 创建Endpoint
func makeUppercaseEndpoint(svc StringService) 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
}
}
// 3. HTTP编解码函数
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
}
// 4. gRPC编解码函数(如果支持gRPC)
func decodeGRPCUppercaseRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(*pb.UppercaseRequest)
return uppercaseRequest{S: req.S}, nil
}
// 每个方法都需要重复以上步骤
---
1.4 使用场景
01.微服务架构
a.分布式系统构建
a.功能说明
go-kit专为微服务架构设计,适合构建大规模分布式系统。它提供了服务发现、负载均衡、熔断降级等微服务必备功能,帮助开发者快速搭建可靠的分布式服务集群。go-kit的分层架构使得服务间的通信标准化,降低了系统集成的复杂度。支持多种传输协议,可以根据不同场景选择最合适的通信方式,如内部服务使用gRPC提高性能,对外API使用HTTP提供兼容性。
b.代码示例
---
// 微服务集群示例
package main
import (
"github.com/go-kit/kit/sd"
"github.com/go-kit/kit/sd/consul"
"github.com/go-kit/kit/sd/lb"
)
func main() {
// 服务发现配置
consulClient, _ := api.NewClient(api.DefaultConfig())
client := consul.NewClient(consulClient)
// 订阅用户服务实例
instancer := consul.NewInstancer(client, logger, "user-service", []string{}, true)
// 创建端点工厂
factory := func(instance string) (endpoint.Endpoint, io.Closer, error) {
return makeUserServiceEndpoint(instance), nil, nil
}
// 负载均衡器(轮询策略)
endpointer := sd.NewEndpointer(instancer, factory, logger)
balancer := lb.NewRoundRobin(endpointer)
// 重试机制
retry := lb.Retry(3, 500*time.Millisecond, balancer)
// 使用负载均衡的端点
response, err := retry(ctx, createUserRequest{
Username: "john",
Email: "[email protected]",
})
}
// 服务注册
func registerService() {
registrar := consul.NewRegistrar(client, &api.AgentServiceRegistration{
ID: "user-service-1",
Name: "user-service",
Port: 8080,
Address: "192.168.1.10",
Tags: []string{"v1", "production"},
Check: &api.AgentServiceCheck{
HTTP: "http://192.168.1.10:8080/health",
Interval: "10s",
Timeout: "2s",
},
})
registrar.Register()
}
---
b.服务网格集成
a.功能说明
go-kit可以与服务网格如Istio、Linkerd等无缝集成,利用服务网格提供的流量管理、安全通信、可观测性等能力。go-kit负责业务逻辑实现,服务网格负责基础设施层面的服务治理。这种组合可以充分发挥各自的优势,go-kit的中间件机制与服务网格的Sidecar模式相辅相成,共同构建健壮的微服务系统。
b.代码示例
---
// 与Istio集成的服务配置
package main
import (
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/tracing/opentracing"
stdopentracing "github.com/opentracing/opentracing-go"
)
func main() {
// OpenTracing配置(与Istio Jaeger集成)
tracer := stdopentracing.GlobalTracer()
// 创建带追踪的Endpoint
var endpoint endpoint.Endpoint
endpoint = makeCreateUserEndpoint(svc)
endpoint = opentracing.TraceServer(tracer, "CreateUser")(endpoint)
// HTTP服务器配置
handler := httptransport.NewServer(
endpoint,
decodeCreateUserRequest,
encodeResponse,
httptransport.ServerBefore(
opentracing.HTTPToContext(tracer, "CreateUser", logger),
),
)
// 健康检查端点(供Istio探测)
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
// 就绪检查端点
http.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) {
if isReady() {
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(http.StatusServiceUnavailable)
}
})
// 启动服务(Istio会自动注入Envoy Sidecar)
http.ListenAndServe(":8080", handler)
}
---
02.API网关
a.统一入口
a.功能说明
go-kit可以用于构建API网关,作为微服务集群的统一入口。API网关负责请求路由、协议转换、认证授权、限流熔断等功能。go-kit的中间件机制非常适合实现这些横切关注点,可以灵活组合各种功能模块。通过Endpoint的组合和转发,可以实现服务聚合、请求编排等高级功能,为前端提供统一的API接口。
b.代码示例
---
// API网关实现
package gateway
import (
"context"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/sd"
"github.com/go-kit/kit/sd/lb"
)
// Gateway 网关结构
type Gateway struct {
userEndpoint endpoint.Endpoint
productEndpoint endpoint.Endpoint
orderEndpoint endpoint.Endpoint
}
// NewGateway 创建网关实例
func NewGateway(
userInstancer sd.Instancer,
productInstancer sd.Instancer,
orderInstancer sd.Instancer,
) *Gateway {
// 用户服务端点
userFactory := makeUserServiceFactory()
userEndpointer := sd.NewEndpointer(userInstancer, userFactory, logger)
userBalancer := lb.NewRoundRobin(userEndpointer)
userEndpoint := lb.Retry(3, 500*time.Millisecond, userBalancer)
// 商品服务端点
productFactory := makeProductServiceFactory()
productEndpointer := sd.NewEndpointer(productInstancer, productFactory, logger)
productBalancer := lb.NewRoundRobin(productEndpointer)
productEndpoint := lb.Retry(3, 500*time.Millisecond, productBalancer)
// 订单服务端点
orderFactory := makeOrderServiceFactory()
orderEndpointer := sd.NewEndpointer(orderInstancer, orderFactory, logger)
orderBalancer := lb.NewRoundRobin(orderEndpointer)
orderEndpoint := lb.Retry(3, 500*time.Millisecond, orderBalancer)
return &Gateway{
userEndpoint: userEndpoint,
productEndpoint: productEndpoint,
orderEndpoint: orderEndpoint,
}
}
// AggregateOrderDetails 聚合订单详情(调用多个服务)
func (g *Gateway) AggregateOrderDetails(ctx context.Context, orderID string) (OrderDetails, error) {
// 并发调用多个服务
var wg sync.WaitGroup
var order Order
var user User
var products []Product
var err error
wg.Add(3)
// 获取订单信息
go func() {
defer wg.Done()
resp, e := g.orderEndpoint(ctx, getOrderRequest{ID: orderID})
if e != nil {
err = e
return
}
order = resp.(getOrderResponse).Order
}()
// 获取用户信息
go func() {
defer wg.Done()
resp, e := g.userEndpoint(ctx, getUserRequest{ID: order.UserID})
if e != nil {
err = e
return
}
user = resp.(getUserResponse).User
}()
// 获取商品信息
go func() {
defer wg.Done()
resp, e := g.productEndpoint(ctx, getProductsRequest{IDs: order.ProductIDs})
if e != nil {
err = e
return
}
products = resp.(getProductsResponse).Products
}()
wg.Wait()
if err != nil {
return OrderDetails{}, err
}
return OrderDetails{
Order: order,
User: user,
Products: products,
}, nil
}
---
b.协议转换
a.功能说明
API网关可以实现不同协议之间的转换,如将HTTP请求转换为gRPC��用后端服务,或将内部的Thrift服务暴露为RESTful API。go-kit的Transport层抽象使得协议转换变得简单,只需要为同一个Endpoint绑定不同的Transport即可。这种能力对于渐进式迁移非常有用,可以在不影响客户端的情况下逐步升级后端服务的通信协议。
b.代码示例
---
// 协议转换网关
package gateway
import (
"github.com/go-kit/kit/transport/http"
"github.com/go-kit/kit/transport/grpc"
)
// 将HTTP请求转换为gRPC调用
func makeHTTPToGRPCGateway(grpcConn *grpc.ClientConn) http.Handler {
// 创建gRPC客户端端点
createUserEndpoint := grpctransport.NewClient(
grpcConn,
"UserService",
"CreateUser",
encodeGRPCCreateUserRequest,
decodeGRPCCreateUserResponse,
pb.CreateUserResponse{},
).Endpoint()
// 绑定HTTP传输层
createUserHandler := httptransport.NewServer(
createUserEndpoint,
decodeHTTPCreateUserRequest,
encodeHTTPResponse,
httptransport.ServerBefore(
httptransport.PopulateRequestContext,
),
)
r := mux.NewRouter()
r.Methods("POST").Path("/users").Handler(createUserHandler)
return r
}
// HTTP请求解码
func decodeHTTPCreateUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
// gRPC请求编码
func encodeGRPCCreateUserRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
return &pb.CreateUserRequest{
Username: req.Username,
Email: req.Email,
}, nil
}
// gRPC响应解码
func decodeGRPCCreateUserResponse(_ context.Context, response interface{}) (interface{}, error) {
resp := response.(*pb.CreateUserResponse)
return CreateUserResponse{
User: User{
ID: resp.User.Id,
Username: resp.User.Username,
Email: resp.User.Email,
},
}, nil
}
---
03.遗留系统改造
a.渐进式迁移
a.功能说明
go-kit适合用于遗留系统的微服务化改造,可以采用渐进式迁移策略,逐步将单体应用拆分为微服务。首先将核心业务逻辑抽取为Service接口,然后通过Endpoint和Transport层暴露服务。在迁移过程中,可以保持原有系统继续运行,新功能使用微服务实现,逐步替换旧模块。go-kit的灵活性使得新旧系统可以并存,降低了迁移风险。
b.代码示例
---
// 遗留系统适配器
package adapter
import (
"context"
"legacy/oldservice"
)
// LegacyServiceAdapter 遗留服务适配器
type LegacyServiceAdapter struct {
legacyService *oldservice.OldService
}
// NewLegacyServiceAdapter 创建适配器
func NewLegacyServiceAdapter(legacy *oldservice.OldService) UserService {
return &LegacyServiceAdapter{
legacyService: legacy,
}
}
// CreateUser 适配旧接口到新接口
func (a *LegacyServiceAdapter) CreateUser(ctx context.Context, username, email string) (User, error) {
// 调用遗留系统的方法
oldUser, err := a.legacyService.AddUser(username, email)
if err != nil {
return User{}, err
}
// 转换数据结构
return User{
ID: oldUser.UserID,
Username: oldUser.Name,
Email: oldUser.EmailAddress,
Created: oldUser.CreateTime.Unix(),
}, nil
}
// 主程序中使用适配器
func main() {
// 初始化遗留系统
legacyService := oldservice.NewOldService(db)
// 创建适配器
var svc UserService
svc = NewLegacyServiceAdapter(legacyService)
// 添加go-kit中间件
svc = loggingMiddleware{logger, svc}
svc = instrumentingMiddleware{counter, histogram, svc}
// 创建Endpoint
createUserEndpoint := makeCreateUserEndpoint(svc)
// 暴露为HTTP服务
handler := httptransport.NewServer(
createUserEndpoint,
decodeCreateUserRequest,
encodeResponse,
)
// 新旧系统共存
http.Handle("/api/v2/users", handler) // 新API
http.Handle("/api/v1/", legacyService.Handler()) // 旧API
http.ListenAndServe(":8080", nil)
}
---
b.功能增强
a.功能说明
在不修改遗留系统代码的前提下,通过go-kit的中间件机制为旧服务添加新功能。可以为遗留服务添加日志记录、性能监控、限流保护、熔断降级等现代微服务所需的能力。这种非侵入式的增强方式不会影响原有系统的稳定性,同时提升了系统的可维护性和可观测性。适配器模式使得新旧系统的集成变得简单,降低了技术债务。
b.代码示例
---
// 为遗留服务添加现代化能力
package main
import (
"github.com/go-kit/kit/circuitbreaker"
"github.com/go-kit/kit/ratelimit"
"github.com/sony/gobreaker"
"golang.org/x/time/rate"
)
func enhanceLegacyService(legacy UserService) UserService {
// 1. 添加日志
legacy = loggingMiddleware{logger, legacy}
// 2. 添加监控指标
legacy = instrumentingMiddleware{
requestCount: requestCounter,
requestLatency: requestHistogram,
next: legacy,
}
// 3. 添加限流(每秒100个请求)
limiter := rate.NewLimiter(100, 100)
legacy = rateLimitMiddleware{limiter, legacy}
// 4. 添加熔断器
breaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{
Name: "UserService",
MaxRequests: 3,
Interval: time.Minute,
Timeout: 30 * time.Second,
})
legacy = circuitBreakerMiddleware{breaker, legacy}
// 5. 添加缓存
cache := NewRedisCache(redisClient)
legacy = cachingMiddleware{cache, 5 * time.Minute, legacy}
return legacy
}
// 限流中间件
type rateLimitMiddleware struct {
limiter *rate.Limiter
next UserService
}
func (mw rateLimitMiddleware) CreateUser(ctx context.Context, username, email string) (User, error) {
if !mw.limiter.Allow() {
return User{}, ErrRateLimitExceeded
}
return mw.next.CreateUser(ctx, username, email)
}
// 熔断中间件
type circuitBreakerMiddleware struct {
breaker *gobreaker.CircuitBreaker
next UserService
}
func (mw circuitBreakerMiddleware) CreateUser(ctx context.Context, username, email string) (User, error) {
result, err := mw.breaker.Execute(func() (interface{}, error) {
return mw.next.CreateUser(ctx, username, email)
})
if err != nil {
return User{}, err
}
return result.(User), nil
}
---
1.5 架构原理
01.洋葱架构
a.分层设计
a.功能说明
go-kit采用洋葱架构模式,将服务分为多个同心圆层次,从内到外依次是Service层、Endpoint层、Transport层和Middleware层。最内层的Service层包含纯粹的业务逻辑,不依赖任何外部框架。Endpoint层负责请求响应的转换和编排。Transport层处理具体的传输协议如HTTP、gRPC等。Middleware层横跨各层,提供日志、监控、限流等横切关注点。这种架构使得各层职责清晰,依赖关系单向,便于测试和维护。
b.代码示例
---
// 洋葱架构实现
package architecture
import (
"context"
"github.com/go-kit/kit/endpoint"
)
// 第1层:Service层(核心业务逻辑)
type OrderService interface {
CreateOrder(ctx context.Context, userID string, items []Item) (Order, error)
GetOrder(ctx context.Context, orderID string) (Order, error)
}
type orderService struct {
db Database
cache Cache
}
func (s *orderService) CreateOrder(ctx context.Context, userID string, items []Item) (Order, error) {
// 纯业务逻辑:验证、计算、存储
order := Order{
ID: generateOrderID(),
UserID: userID,
Items: items,
Total: calculateTotal(items),
Status: "pending",
}
return s.db.SaveOrder(ctx, order)
}
// 第2层:Endpoint层(请求响应转换)
func makeCreateOrderEndpoint(svc OrderService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(createOrderRequest)
order, err := svc.CreateOrder(ctx, req.UserID, req.Items)
if err != nil {
return createOrderResponse{Err: err.Error()}, nil
}
return createOrderResponse{Order: order}, nil
}
}
// 第3层:Transport层(协议绑定)
func makeHTTPHandler(endpoint endpoint.Endpoint) http.Handler {
return httptransport.NewServer(
endpoint,
decodeCreateOrderRequest,
encodeResponse,
)
}
// 第4层:Middleware层(横切关注点)
func applyMiddleware(e endpoint.Endpoint) endpoint.Endpoint {
e = loggingMiddleware(logger)(e)
e = instrumentingMiddleware(metrics)(e)
e = rateLimitMiddleware(limiter)(e)
e = circuitBreakerMiddleware(breaker)(e)
return e
}
---
b.依赖倒置
a.功能说明
go-kit遵循依赖倒置原则,高层模块不依赖低层模块,两者都依赖抽象。Service层定义为接口,具体实现可以灵活替换。Endpoint层依赖Service接口而非具体实现,使得业务逻辑可以独立测试。Transport层依赖Endpoint抽象,可以支持多种传输协议。这种设计使得系统各部分松耦合,易于扩展和维护。依赖注入机制使得组件的组装和配置变得灵活。
b.代码示例
---
// 依赖倒置示例
package main
import "context"
// 定义抽象接口(高层模块依赖)
type UserRepository interface {
Save(ctx context.Context, user User) error
FindByID(ctx context.Context, id string) (User, error)
}
type EmailService interface {
SendWelcomeEmail(ctx context.Context, email string) error
}
// Service层依赖抽象接口
type userService struct {
repo UserRepository // 依赖抽象
email EmailService // 依赖抽象
}
func NewUserService(repo UserRepository, email EmailService) UserService {
return &userService{
repo: repo,
email: email,
}
}
func (s *userService) CreateUser(ctx context.Context, username, email string) (User, error) {
user := User{
ID: generateID(),
Username: username,
Email: email,
}
// 使用抽象接口
if err := s.repo.Save(ctx, user); err != nil {
return User{}, err
}
// 使用抽象接口
if err := s.email.SendWelcomeEmail(ctx, email); err != nil {
// 记录错误但不影响用户创建
logger.Log("err", err)
}
return user, nil
}
// 具体实现(低层模块)
type postgresRepository struct {
db *sql.DB
}
func (r *postgresRepository) Save(ctx context.Context, user User) error {
_, err := r.db.ExecContext(ctx, "INSERT INTO users ...")
return err
}
// 可以轻松替换实现
type mongoRepository struct {
client *mongo.Client
}
func (r *mongoRepository) Save(ctx context.Context, user User) error {
_, err := r.client.Database("app").Collection("users").InsertOne(ctx, user)
return err
}
// 依赖注入组装
func main() {
// 可以注入不同的实现
repo := &postgresRepository{db: db}
// repo := &mongoRepository{client: mongoClient}
email := &smtpEmailService{config: smtpConfig}
svc := NewUserService(repo, email)
}
---
02.中间件模式
a.装饰器模式
a.功能说明
go-kit的中间件采用装饰器模式实现,通过函数包装的方式为原有功能添加额外的行为。中间件是一个接收Endpoint或Service并返回增强后的Endpoint或Service的函数。多个中间件可以链式组合,形成处理管道。每个中间件负��单一职责,如日志记录、性能监控、错误处理等。这种设计使得功能模块化,易于组合和复用。中间件的执行顺序由添加顺序决定,形成洋葱式的调用链。
b.代码示例
---
// 装饰器模式中间件
package middleware
import (
"context"
"time"
"github.com/go-kit/kit/endpoint"
)
// Middleware 中间件类型定义
type Middleware func(endpoint.Endpoint) endpoint.Endpoint
// LoggingMiddleware 日志中间件
func LoggingMiddleware(logger log.Logger) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
defer func(begin time.Time) {
logger.Log(
"method", "endpoint",
"took", time.Since(begin),
"err", err,
)
}(time.Now())
return next(ctx, request)
}
}
}
// InstrumentingMiddleware 监控中间件
func InstrumentingMiddleware(duration metrics.Histogram) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
defer func(begin time.Time) {
duration.Observe(time.Since(begin).Seconds())
}(time.Now())
return next(ctx, request)
}
}
}
// RateLimitMiddleware 限流中间件
func RateLimitMiddleware(limiter *rate.Limiter) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if !limiter.Allow() {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
// 链式组合中间件
func ChainMiddleware(outer Middleware, others ...Middleware) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
for i := len(others) - 1; i >= 0; i-- {
next = others[i](next)
}
return outer(next)
}
}
// 使用示例
func BuildEndpoint(svc Service) endpoint.Endpoint {
var endpoint endpoint.Endpoint
endpoint = makeEndpoint(svc)
// 应用多个中间件(从内到外执行)
endpoint = LoggingMiddleware(logger)(endpoint)
endpoint = InstrumentingMiddleware(histogram)(endpoint)
endpoint = RateLimitMiddleware(limiter)(endpoint)
// 或使用链式组��
endpoint = ChainMiddleware(
LoggingMiddleware(logger),
InstrumentingMiddleware(histogram),
RateLimitMiddleware(limiter),
)(endpoint)
return endpoint
}
---
b.管道模式
a.功能说明
中间件形成的处理管道类似于Unix管道,数据从一个中间件流向下一个中间件。每个中间件可以在调用下一个中间件之前或之后执行操作,也可以决定是否继续传递请求。这种模式使得请求处理流程清晰可控,便于调试和监控。管道中的每个环节都可以独立测试,提高了代码质量。中间件的组合顺序会影响最终行为,需要根据实际需求合理安排。
b.代码示例
---
// 管道模式示例
package pipeline
import (
"context"
"errors"
"time"
)
// 认证中间件(最外层)
func AuthMiddleware(tokenValidator TokenValidator) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
// 前置处理:验证token
token := extractToken(ctx)
if !tokenValidator.Validate(token) {
return nil, ErrUnauthorized
}
// 将用户信息注入context
userID := tokenValidator.GetUserID(token)
ctx = context.WithValue(ctx, "userID", userID)
// 继续管道
return next(ctx, request)
}
}
}
// 限流中间件(第二层)
func RateLimitMiddleware(limiter *rate.Limiter) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
// 前置处理:检查限流
if !limiter.Allow() {
return nil, ErrRateLimitExceeded
}
// 继续管道
return next(ctx, request)
}
}
}
// 日志中间件(第三层)
func LoggingMiddleware(logger log.Logger) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
// 前置处理:记录请求
logger.Log("msg", "request received", "request", request)
// 继续管道
response, err := next(ctx, request)
// 后置处理:记录响应
logger.Log("msg", "request completed", "response", response, "err", err)
return response, err
}
}
}
// 重试中间件(第四层)
func RetryMiddleware(maxRetries int, backoff time.Duration) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
var response interface{}
var err error
for i := 0; i <= maxRetries; i++ {
response, err = next(ctx, request)
if err == nil {
return response, nil
}
// 可重试的错误才重试
if !isRetryable(err) {
return response, err
}
// 等待后重试
if i < maxRetries {
time.Sleep(backoff * time.Duration(i+1))
}
}
return response, err
}
}
}
// 构建完整管道
func BuildPipeline(svc Service) endpoint.Endpoint {
var e endpoint.Endpoint
e = makeEndpoint(svc)
// 从内到外应用中间件
// 执行顺序:Auth -> RateLimit -> Logging -> Retry -> Endpoint
e = RetryMiddleware(3, 100*time.Millisecond)(e)
e = LoggingMiddleware(logger)(e)
e = RateLimitMiddleware(limiter)(e)
e = AuthMiddleware(validator)(e)
return e
}
// 请求流程:
// 1. Auth验证token
// 2. RateLimit检查限流
// 3. Logging记录请求
// 4. Retry执行重试逻���
// 5. Endpoint处理业务
// 6. Logging记录响应
// 7. 返回结果
---
03.传输抽象
a.协议无关
a.功能说明
go-kit的Transport层提供了协议无关的抽象,使得业务逻辑与传输协议完全解耦。同一个Endpoint可以绑定到HTTP、gRPC、Thrift等多种传输协议上,无需修改业务代码。Transport层负责协议特定的编解码、错误处理、上下文传递等工作。这种抽象使得服务可以同时支持多种协议,满足不同客户端的需求。协议的切换和升级不会影响核心业务逻辑,降低了系统演进的成本。
b.代码示例
---
// 协议无关的传输层
package transport
import (
"context"
"encoding/json"
"github.com/go-kit/kit/transport/http"
"github.com/go-kit/kit/transport/grpc"
)
// 同一个Endpoint
var createUserEndpoint endpoint.Endpoint = makeCreateUserEndpoint(svc)
// HTTP传输绑定
func MakeHTTPHandler(endpoint endpoint.Endpoint) http.Handler {
return httptransport.NewServer(
endpoint,
decodeHTTPCreateUserRequest,
encodeHTTPResponse,
httptransport.ServerBefore(httptransport.PopulateRequestContext),
)
}
// HTTP编解码
func decodeHTTPCreateUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
func encodeHTTPResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
// gRPC传输绑定(同一个Endpoint)
func MakeGRPCServer(endpoint endpoint.Endpoint) pb.UserServiceServer {
return &grpcServer{
createUser: grpctransport.NewServer(
endpoint,
decodeGRPCCreateUserRequest,
encodeGRPCCreateUserResponse,
),
}
}
// gRPC编解码
func decodeGRPCCreateUserRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(*pb.CreateUserRequest)
return CreateUserRequest{
Username: req.Username,
Email: req.Email,
}, nil
}
func encodeGRPCCreateUserResponse(_ context.Context, response interface{}) (interface{}, error) {
resp := response.(CreateUserResponse)
return &pb.CreateUserResponse{
User: &pb.User{
Id: resp.User.ID,
Username: resp.User.Username,
Email: resp.User.Email,
},
}, nil
}
// 主程序:同时支持HTTP和gRPC
func main() {
// 创建Endpoint
endpoint := makeCreateUserEndpoint(svc)
// HTTP服务器
httpHandler := MakeHTTPHandler(endpoint)
go http.ListenAndServe(":8080", httpHandler)
// gRPC服务器
grpcServer := MakeGRPCServer(endpoint)
lis, _ := net.Listen("tcp", ":9090")
s := grpc.NewServer()
pb.RegisterUserServiceServer(s, grpcServer)
s.Serve(lis)
}
---
b.编解码分离
a.功能说明
Transport层将编解码逻辑与业务逻辑分离,每个传输协议有独立的编解码器。编码器负责将Endpoint的响应转换为协议特定的格式,解码器负责将协议请求转换为Endpoint的请求对象。这种分离使得编解码逻辑可以独立测试和优化,不会影响业务逻辑。支持自定义编解码器,可以实现特殊的序列化需求,如使用Protobuf、MessagePack等高效格式。
b.代码示例
---
// 编解码分离示例
package codec
import (
"context"
"encoding/json"
"io"
)
// HTTP JSON编解码器
type HTTPJSONCodec struct{}
func (c HTTPJSONCodec) Decode(ctx context.Context, r io.Reader) (interface{}, error) {
var req CreateUserRequest
if err := json.NewDecoder(r).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
func (c HTTPJSONCodec) Encode(ctx context.Context, w io.Writer, response interface{}) error {
return json.NewEncoder(w).Encode(response)
}
// HTTP Protobuf编解码器
type HTTPProtobufCodec struct{}
func (c HTTPProtobufCodec) Decode(ctx context.Context, r io.Reader) (interface{}, error) {
data, err := io.ReadAll(r)
if err != nil {
return nil, err
}
var req pb.CreateUserRequest
if err := proto.Unmarshal(data, &req); err != nil {
return nil, err
}
return CreateUserRequest{
Username: req.Username,
Email: req.Email,
}, nil
}
func (c HTTPProtobufCodec) Encode(ctx context.Context, w io.Writer, response interface{}) error {
resp := response.(CreateUserResponse)
pbResp := &pb.CreateUserResponse{
User: &pb.User{
Id: resp.User.ID,
Username: resp.User.Username,
Email: resp.User.Email,
},
}
data, err := proto.Marshal(pbResp)
if err != nil {
return err
}
_, err = w.Write(data)
return err
}
// 可配置的编解码器
func MakeHTTPHandler(endpoint endpoint.Endpoint, codec Codec) http.Handler {
return httptransport.NewServer(
endpoint,
func(ctx context.Context, r *http.Request) (interface{}, error) {
return codec.Decode(ctx, r.Body)
},
func(ctx context.Context, w http.ResponseWriter, response interface{}) error {
contentType := "application/json"
if _, ok := codec.(HTTPProtobufCodec); ok {
contentType = "application/protobuf"
}
w.Header().Set("Content-Type", contentType)
return codec.Encode(ctx, w, response)
},
)
}
// 根据Content-Type选择编解码器
func SelectCodec(contentType string) Codec {
switch contentType {
case "application/json":
return HTTPJSONCodec{}
case "application/protobuf":
return HTTPProtobufCodec{}
default:
return HTTPJSONCodec{}
}
}
---
1.6 核心特性
01.服务发现
a.多种注册中心支持
a.功能说明
go-kit提供了对主流服务注册中心的支持,包括Consul、Etcd、Eureka、ZooKeeper等。服务实例可以自动注册到注册中心,并定期发送心跳保持注册状态。客户端通过订阅注册中心获取服务实例列表,实现动态服务发现。支持健康检查机制,自动剔除不健康的服务实例。服务发现与负载均衡紧密集成,为客户端提供透明的服务调用体验。
b.代码示例
---
// Consul服务发现
package discovery
import (
"github.com/go-kit/kit/sd"
"github.com/go-kit/kit/sd/consul"
consulapi "github.com/hashicorp/consul/api"
)
func SetupConsulDiscovery() (sd.Instancer, *consul.Registrar, error) {
// 创建Consul客户端
consulConfig := consulapi.DefaultConfig()
consulConfig.Address = "localhost:8500"
consulClient, err := consulapi.NewClient(consulConfig)
if err != nil {
return nil, nil, err
}
client := consul.NewClient(consulClient)
// 服务注册
registration := &consulapi.AgentServiceRegistration{
ID: "user-service-1",
Name: "user-service",
Port: 8080,
Address: "192.168.1.10",
Tags: []string{"v1.0", "production"},
Meta: map[string]string{
"version": "1.0.0",
"region": "us-west",
},
Check: &consulapi.AgentServiceCheck{
HTTP: "http://192.168.1.10:8080/health",
Interval: "10s",
Timeout: "2s",
DeregisterCriticalServiceAfter: "1m",
},
}
registrar := consul.NewRegistrar(client, registration, logger)
registrar.Register()
// 服务发现
instancer := consul.NewInstancer(
client,
logger,
"user-service",
[]string{"production"},
true,
)
return instancer, registrar, nil
}
// Etcd服务发现
func SetupEtcdDiscovery() (sd.Instancer, error) {
// 创建Etcd客户端
etcdClient, err := clientv3.New(clientv3.Config{
Endpoints: []string{"localhost:2379"},
DialTimeout: 5 * time.Second,
})
if err != nil {
return nil, err
}
// 服务注册
prefix := "/services/user-service/"
instance := prefix + "192.168.1.10:8080"
value := `{"address":"192.168.1.10:8080","version":"1.0.0"}`
// 创建租约
lease, err := etcdClient.Grant(context.Background(), 10)
if err != nil {
return nil, err
}
// 注册服务
_, err = etcdClient.Put(
context.Background(),
instance,
value,
clientv3.WithLease(lease.ID),
)
if err != nil {
return nil, err
}
// 保持租约
ch, err := etcdClient.KeepAlive(context.Background(), lease.ID)
go func() {
for range ch {
// 租约续期
}
}()
// 服务发现
client := etcdv3.NewClient(etcdClient, prefix, logger)
instancer, err := etcdv3.NewInstancer(client, logger)
return instancer, err
}
---
b.负载均衡策略
a.功能说明
go-kit内置了多种负载均衡策略,包括轮询、随机、一致性哈希等。负载均衡器���服务发现集成,自动从注册中心获取服务实例列表,根据策略选择目标实例。支持动态更新实例列表,当服务实例上下线时自动调整。可以自定义负载均衡策略,实现基于权重、响应时间等高级算法。负载均衡器还支持重试机制,当请求失败时自动选择其他实例重试。
b.代码示例
---
// 负载均衡实现
package loadbalancer
import (
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/sd"
"github.com/go-kit/kit/sd/lb"
)
// 轮询负载均衡
func SetupRoundRobinBalancer(instancer sd.Instancer) endpoint.Endpoint {
// 端点工厂
factory := func(instance string) (endpoint.Endpoint, io.Closer, error) {
return makeRemoteEndpoint(instance), nil, nil
}
// 创建端点管理器
endpointer := sd.NewEndpointer(instancer, factory, logger)
// 轮询策略
balancer := lb.NewRoundRobin(endpointer)
// 重试机制
retry := lb.Retry(3, 500*time.Millisecond, balancer)
return retry
}
// 随机负载均衡
func SetupRandomBalancer(instancer sd.Instancer) endpoint.Endpoint {
factory := func(instance string) (endpoint.Endpoint, io.Closer, error) {
return makeRemoteEndpoint(instance), nil, nil
}
endpointer := sd.NewEndpointer(instancer, factory, logger)
balancer := lb.NewRandom(endpointer, time.Now().UnixNano())
retry := lb.Retry(3, 500*time.Millisecond, balancer)
return retry
}
// 自定义负载均衡:基于权重
type WeightedBalancer struct {
endpoints []weightedEndpoint
mu sync.RWMutex
}
type weightedEndpoint struct {
endpoint endpoint.Endpoint
weight int
}
func NewWeightedBalancer(endpointer sd.Endpointer, weights map[string]int) *WeightedBalancer {
wb := &WeightedBalancer{}
// 监听端点变化
go func() {
for endpoints := range endpointer.Endpoints() {
wb.mu.Lock()
wb.endpoints = make([]weightedEndpoint, 0, len(endpoints))
for i, ep := range endpoints {
weight := weights[fmt.Sprintf("instance-%d", i)]
if weight == 0 {
weight = 1
}
wb.endpoints = append(wb.endpoints, weightedEndpoint{
endpoint: ep,
weight: weight,
})
}
wb.mu.Unlock()
}
}()
return wb
}
func (wb *WeightedBalancer) Endpoint() (endpoint.Endpoint, error) {
wb.mu.RLock()
defer wb.mu.RUnlock()
if len(wb.endpoints) == 0 {
return nil, lb.ErrNoEndpoints
}
// 计算总权重
totalWeight := 0
for _, ep := range wb.endpoints {
totalWeight += ep.weight
}
// 随机选择
r := rand.Intn(totalWeight)
for _, ep := range wb.endpoints {
r -= ep.weight
if r < 0 {
return ep.endpoint, nil
}
}
return wb.endpoints[0].endpoint, nil
}
---
02.可观测性
a.日志记录
a.功能说明
go-kit提供了结构化日志支持,采用键值对格式记录日志信息。日志接口简洁统一,支持多种日志后端如logfmt、JSON等。通过日志中间件可以自动记录请求响应、执行时间、错误信息等关键数据。支持日志级别控制和上下文传递,便于问题排查和审计。日志记录对性能影响小,适合生产环境使用。
b.代码示例
---
// 结构化日志
package logging
import (
"context"
"time"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
)
// 创建日志记录器
func NewLogger() log.Logger {
var logger log.Logger
logger = log.NewLogfmtLogger(os.Stderr)
logger = log.With(logger, "ts", log.DefaultTimestampUTC)
logger = log.With(logger, "caller", log.DefaultCaller)
return logger
}
// Service层日志中间件
type loggingMiddleware struct {
logger log.Logger
next UserService
}
func LoggingMiddleware(logger log.Logger) ServiceMiddleware {
return func(next UserService) UserService {
return &loggingMiddleware{
logger: logger,
next: next,
}
}
}
func (mw loggingMiddleware) CreateUser(ctx context.Context, username, email string) (user User, err error) {
defer func(begin time.Time) {
level.Info(mw.logger).Log(
"method", "CreateUser",
"username", username,
"email", email,
"user_id", user.ID,
"err", err,
"took", time.Since(begin),
)
}(time.Now())
return mw.next.CreateUser(ctx, username, email)
}
// Endpoint层日志中间件
func EndpointLoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
defer func(begin time.Time) {
level.Debug(logger).Log(
"transport", "endpoint",
"request", fmt.Sprintf("%+v", request),
"response", fmt.Sprintf("%+v", response),
"err", err,
"took", time.Since(begin),
)
}(time.Now())
return next(ctx, request)
}
}
}
// 带级别的日志
func LogWithLevel(logger log.Logger, err error, msg string, keyvals ...interface{}) {
if err != nil {
level.Error(logger).Log(append(keyvals, "err", err, "msg", msg)...)
} else {
level.Info(logger).Log(append(keyvals, "msg", msg)...)
}
}
// 使用示例
func main() {
logger := NewLogger()
var svc UserService
svc = newUserService(db)
svc = LoggingMiddleware(logger)(svc)
endpoint := makeCreateUserEndpoint(svc)
endpoint = EndpointLoggingMiddleware(logger)(endpoint)
}
---
b.指标监控
a.功能说明
go-kit与Prometheus深度集成,提供了丰富的监控指标支持。内置Counter、Gauge、Histogram、Summary等指标类型,可以记录请求计数、延迟分布、错误率等关键指标。通过监控中间件自动采集服务运行数据,无需手动埋点。支持多维度标签,可以按方法、状态码、错误类型等维度聚合分析。监控数据可以导出到Prometheus、Graphite等监控系统。
b.代码示例
---
// Prometheus监控
package metrics
import (
"context"
"time"
"github.com/go-kit/kit/metrics"
"github.com/go-kit/kit/metrics/prometheus"
stdprometheus "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// 定义监控指标
type Metrics struct {
RequestCount metrics.Counter
RequestLatency metrics.Histogram
ErrorCount metrics.Counter
}
func NewMetrics() *Metrics {
fieldKeys := []string{"method", "error"}
return &Metrics{
RequestCount: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "user_service",
Subsystem: "api",
Name: "request_count",
Help: "Number of requests received.",
}, fieldKeys),
RequestLatency: prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "user_service",
Subsystem: "api",
Name: "request_latency_seconds",
Help: "Total duration of requests in seconds.",
Objectives: map[float64]float64{
0.5: 0.05,
0.9: 0.01,
0.99: 0.001,
},
}, fieldKeys),
ErrorCount: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "user_service",
Subsystem: "api",
Name: "error_count",
Help: "Number of errors occurred.",
}, []string{"method", "error_type"}),
}
}
// 监控中间件
type instrumentingMiddleware struct {
metrics *Metrics
next UserService
}
func InstrumentingMiddleware(m *Metrics) ServiceMiddleware {
return func(next UserService) UserService {
return &instrumentingMiddleware{
metrics: m,
next: next,
}
}
}
func (mw instrumentingMiddleware) CreateUser(ctx context.Context, username, email string) (user User, err error) {
defer func(begin time.Time) {
lvs := []string{"method", "CreateUser", "error", fmt.Sprint(err != nil)}
mw.metrics.RequestCount.With(lvs...).Add(1)
mw.metrics.RequestLatency.With(lvs...).Observe(time.Since(begin).Seconds())
if err != nil {
mw.metrics.ErrorCount.With(
"method", "CreateUser",
"error_type", classifyError(err),
).Add(1)
}
}(time.Now())
return mw.next.CreateUser(ctx, username, email)
}
// 暴露Prometheus指标端点
func ExposeMetrics() {
http.Handle("/metrics", promhttp.Handler())
go http.ListenAndServe(":9090", nil)
}
// 自定义指标
var (
activeConnections = prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
Namespace: "user_service",
Name: "active_connections",
Help: "Number of active connections.",
}, []string{})
cacheHitRate = prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
Namespace: "user_service",
Name: "cache_hit_rate",
Help: "Cache hit rate distribution.",
Buckets: []float64{0.5, 0.7, 0.8, 0.9, 0.95, 0.99, 1.0},
}, []string{"cache_type"})
)
func RecordCacheHit(cacheType string, hit bool) {
rate := 0.0
if hit {
rate = 1.0
}
cacheHitRate.With("cache_type", cacheType).Observe(rate)
}
---
c.分布式追踪
a.功能说明
go-kit支持OpenTracing标准,可以与Zipkin、Jaeger等分布式追踪系统集成。通过追踪中间件自动创建和传播Span,记录请求在微服务间的调用链路。支持采样策略,控制追踪数据的采集比例。追踪信息包含时间戳、标签、日志等详细数据,帮助分析性能瓶颈和定位问题。追踪上下文通过HTTP Header或gRPC Metadata自动传递,实现跨服务的链路追踪。
b.代码示例
---
// 分布式追踪
package tracing
import (
"context"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/tracing/opentracing"
"github.com/go-kit/kit/tracing/zipkin"
stdopentracing "github.com/opentracing/opentracing-go"
zipkinot "github.com/openzipkin-contrib/zipkin-go-opentracing"
zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"
)
// 初始化Zipkin追踪
func InitZipkinTracer(serviceName, zipkinURL string) (stdopentracing.Tracer, error) {
reporter := zipkinhttp.NewReporter(zipkinURL)
endpoint, err := zipkin.NewEndpoint(serviceName, "localhost:8080")
if err != nil {
return nil, err
}
nativeTracer, err := zipkin.NewTracer(
reporter,
zipkin.WithLocalEndpoint(endpoint),
zipkin.WithSharedSpans(false),
)
if err != nil {
return nil, err
}
tracer := zipkinot.Wrap(nativeTracer)
stdopentracing.SetGlobalTracer(tracer)
return tracer, nil
}
// Endpoint追踪中间件
func TracingMiddleware(tracer stdopentracing.Tracer, operationName string) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return opentracing.TraceServer(tracer, operationName)(next)
}
}
// HTTP传输层追踪
func MakeHTTPHandler(endpoint endpoint.Endpoint, tracer stdopentracing.Tracer) http.Handler {
return httptransport.NewServer(
endpoint,
decodeRequest,
encodeResponse,
httptransport.ServerBefore(
opentracing.HTTPToContext(tracer, "CreateUser", logger),
),
httptransport.ServerAfter(
opentracing.ContextToHTTP(tracer, logger),
),
)
}
// gRPC传输层追踪
func MakeGRPCServer(endpoint endpoint.Endpoint, tracer stdopentracing.Tracer) pb.UserServiceServer {
return &grpcServer{
createUser: grpctransport.NewServer(
endpoint,
decodeGRPCRequest,
encodeGRPCResponse,
grpctransport.ServerBefore(
opentracing.GRPCToContext(tracer, "CreateUser", logger),
),
),
}
}
// 客户端追踪
func MakeTracedClient(tracer stdopentracing.Tracer, instance string) endpoint.Endpoint {
var endpoint endpoint.Endpoint
endpoint = makeRemoteEndpoint(instance)
endpoint = opentracing.TraceClient(tracer, "CreateUser")(endpoint)
return endpoint
}
// 手动创建Span
func DoSomethingWithTracing(ctx context.Context) error {
span, ctx := stdopentracing.StartSpanFromContext(ctx, "DoSomething")
defer span.Finish()
// 添加标签
span.SetTag("user_id", "12345")
span.SetTag("operation", "database_query")
// 添加日志
span.LogKV("event", "query_start", "table", "users")
// 执行操作
result, err := db.Query(ctx, "SELECT * FROM users")
if err != nil {
span.SetTag("error", true)
span.LogKV("event", "error", "message", err.Error())
return err
}
span.LogKV("event", "query_complete", "rows", result.RowCount())
return nil
}
// 完整示例
func main() {
// 初始化追踪器
tracer, _ := InitZipkinTracer("user-service", "http://localhost:9411/api/v2/spans")
// 创建服务
var svc UserService
svc = newUserService(db)
// 创建Endpoint
endpoint := makeCreateUserEndpoint(svc)
endpoint = TracingMiddleware(tracer, "CreateUser")(endpoint)
// 创建HTTP处理器
handler := MakeHTTPHandler(endpoint, tracer)
http.ListenAndServe(":8080", handler)
}
---
1.7 生态系统
01.官方组件
a.传输层组件
a.功能说明
go-kit官方提供了HTTP、gRPC、Thrift等主流传输协议的支持包。HTTP传输基于标准库net/http,提供了请求解码、响应编码、错误处理等完整功能。gRPC传输支持双向流、元数据传递、拦截器等高级特性。Thrift传输兼容Apache Thrift协议,可以与其他语言的Thrift服务互操作。每个传输层组件都提供了统一的接口,便于在不同协议间切换。
b.代码示例
---
// 多传输层支持
package transport
import (
"github.com/go-kit/kit/transport/http"
"github.com/go-kit/kit/transport/grpc"
"github.com/go-kit/kit/transport/thrift"
)
// HTTP传输
func MakeHTTPHandler(endpoints Endpoints) http.Handler {
r := mux.NewRouter()
r.Methods("POST").Path("/users").Handler(httptransport.NewServer(
endpoints.CreateUser,
decodeHTTPCreateUserRequest,
encodeHTTPResponse,
))
r.Methods("GET").Path("/users/{id}").Handler(httptransport.NewServer(
endpoints.GetUser,
decodeHTTPGetUserRequest,
encodeHTTPResponse,
))
return r
}
// gRPC传输
type grpcServer struct {
createUser grpctransport.Handler
getUser grpctransport.Handler
}
func NewGRPCServer(endpoints Endpoints) pb.UserServiceServer {
return &grpcServer{
createUser: grpctransport.NewServer(
endpoints.CreateUser,
decodeGRPCCreateUserRequest,
encodeGRPCCreateUserResponse,
),
getUser: grpctransport.NewServer(
endpoints.GetUser,
decodeGRPCGetUserRequest,
encodeGRPCGetUserResponse,
),
}
}
func (s *grpcServer) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.CreateUserResponse, error) {
_, resp, err := s.createUser.ServeGRPC(ctx, req)
if err != nil {
return nil, err
}
return resp.(*pb.CreateUserResponse), nil
}
// Thrift传输
func NewThriftServer(endpoints Endpoints) *thrift.Server {
return thrift.NewServer(
makeThriftHandler(endpoints),
)
}
---
b.中间件组件
a.功能说明
go-kit提供了丰富的中间件组件库,包括限流、熔断、重试、超时等常用功能。限流中间件基于golang.org/x/time/rate实现令牌桶算法。熔断中间件集成sony/gobreaker,支持自动熔断和恢复。重试中间件支持指数退避策略。这些中间件都经过生产环境验证,可以直接使用。中间件之间可以自由组合,构建复杂的处理链。
b.代码示例
---
// 官方中间件使用
package middleware
import (
"github.com/go-kit/kit/circuitbreaker"
"github.com/go-kit/kit/ratelimit"
"github.com/sony/gobreaker"
"golang.org/x/time/rate"
)
func BuildEndpointWithMiddleware(svc Service) endpoint.Endpoint {
var e endpoint.Endpoint
e = makeEndpoint(svc)
// 限流中间件
limiter := rate.NewLimiter(rate.Every(time.Second), 100)
e = ratelimit.NewErroringLimiter(limiter)(e)
// 熔断中间件
breaker := gobreaker.NewCircuitBreaker(gobreaker.Settings{
Name: "UserService",
MaxRequests: 3,
Interval: time.Minute,
Timeout: 30 * time.Second,
ReadyToTrip: func(counts gobreaker.Counts) bool {
failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
return counts.Requests >= 3 && failureRatio >= 0.6
},
})
e = circuitbreaker.Gobreaker(breaker)(e)
// 超时中间件
e = func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
return next(ctx, request)
}
}(e)
return e
}
// 重试中间件
func RetryMiddleware(max int, timeout time.Duration) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
var resp interface{}
var err error
for i := 0; i <= max; i++ {
resp, err = next(ctx, request)
if err == nil {
return resp, nil
}
if i < max {
select {
case <-time.After(timeout * time.Duration(i+1)):
case <-ctx.Done():
return nil, ctx.Err()
}
}
}
return resp, err
}
}
}
---
02.第三方集成
a.服务注册中心
a.功能说明
go-kit社区提供了与主流服务注册中心的集成包,包括Consul、Etcd、Eureka、ZooKeeper等。这些集成包封装了服务注册、发现、健康检查等功能,提供统一的API接口。支持自动注册和注销,处理网络异常和重连。与go-kit的负载均衡器无缝集成,实现动态服务发现。社区维护活跃,及时跟进各注册中心的新特性。
b.代码示例
---
// 多注册中心集成
package registry
import (
"github.com/go-kit/kit/sd"
"github.com/go-kit/kit/sd/consul"
"github.com/go-kit/kit/sd/etcdv3"
"github.com/go-kit/kit/sd/eureka"
)
// Consul集成
func SetupConsul() (sd.Instancer, sd.Registrar) {
consulClient, _ := consulapi.NewClient(consulapi.DefaultConfig())
client := consul.NewClient(consulClient)
registration := &consulapi.AgentServiceRegistration{
ID: "user-service-1",
Name: "user-service",
Port: 8080,
Address: "localhost",
}
registrar := consul.NewRegistrar(client, registration, logger)
instancer := consul.NewInstancer(client, logger, "user-service", []string{}, true)
return instancer, registrar
}
// Etcd集成
func SetupEtcd() (sd.Instancer, error) {
etcdClient, _ := clientv3.New(clientv3.Config{
Endpoints: []string{"localhost:2379"},
})
client := etcdv3.NewClient(etcdClient, "/services/user-service/", logger)
instancer, err := etcdv3.NewInstancer(client, logger)
return instancer, err
}
// Eureka集成
func SetupEureka() (sd.Instancer, sd.Registrar) {
eurekaClient := eureka.NewClient([]string{"http://localhost:8761/eureka"})
instance := &eureka.Instance{
HostName: "localhost",
Port: 8080,
App: "USER-SERVICE",
}
registrar := eureka.NewRegistrar(eurekaClient, instance, logger)
instancer := eureka.NewInstancer(eurekaClient, "USER-SERVICE", logger)
return instancer, registrar
}
// 统一服务发现接口
type ServiceDiscovery struct {
instancer sd.Instancer
registrar sd.Registrar
}
func NewServiceDiscovery(registryType string) (*ServiceDiscovery, error) {
var instancer sd.Instancer
var registrar sd.Registrar
switch registryType {
case "consul":
instancer, registrar = SetupConsul()
case "etcd":
instancer, _ = SetupEtcd()
case "eureka":
instancer, registrar = SetupEureka()
default:
return nil, errors.New("unsupported registry type")
}
return &ServiceDiscovery{
instancer: instancer,
registrar: registrar,
}, nil
}
---
b.监控系统
a.功能说明
go-kit与Prometheus、Graphite、InfluxDB等监控系统深度集成。提供了统一的metrics接口,支持Counter、Gauge、Histogram、Summary等指标类型。可以轻松切换不同的监控后端,无需修改业务代码。支持多维度标签,便于指标聚合和分析。社区提供了丰富的Grafana仪表板模板,可以快速搭建监控大屏。
b.代码示例
---
// 多监控系统集成
package monitoring
import (
"github.com/go-kit/kit/metrics"
"github.com/go-kit/kit/metrics/prometheus"
"github.com/go-kit/kit/metrics/influx"
"github.com/go-kit/kit/metrics/graphite"
)
// Prometheus监控
func SetupPrometheus() *Metrics {
fieldKeys := []string{"method", "error"}
return &Metrics{
RequestCount: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "user_service",
Name: "request_count",
Help: "Number of requests.",
}, fieldKeys),
RequestLatency: prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "user_service",
Name: "request_latency_seconds",
Help: "Request latency in seconds.",
}, fieldKeys),
}
}
// InfluxDB监控
func SetupInfluxDB() *Metrics {
influxClient := influx.New(map[string]string{
"service": "user-service",
}, influxdb.BatchPointsConfig{
Database: "metrics",
}, logger)
return &Metrics{
RequestCount: influxClient.NewCounter("request_count"),
RequestLatency: influxClient.NewHistogram("request_latency"),
}
}
// Graphite监控
func SetupGraphite() *Metrics {
graphiteClient := graphite.New("user.service.", logger)
return &Metrics{
RequestCount: graphiteClient.NewCounter("request_count"),
RequestLatency: graphiteClient.NewHistogram("request_latency"),
}
}
// 统一监控接口
type Metrics struct {
RequestCount metrics.Counter
RequestLatency metrics.Histogram
}
func NewMetrics(monitoringType string) *Metrics {
switch monitoringType {
case "prometheus":
return SetupPrometheus()
case "influxdb":
return SetupInfluxDB()
case "graphite":
return SetupGraphite()
default:
return SetupPrometheus()
}
}
// 使用示例
func main() {
metrics := NewMetrics("prometheus")
var svc UserService
svc = newUserService(db)
svc = instrumentingMiddleware{metrics, svc}
// 暴露Prometheus指标
http.Handle("/metrics", promhttp.Handler())
go http.ListenAndServe(":9090", nil)
}
---
03.社区工具
a.代码生成工具
a.功能说明
社区提供了多个代码生成工具,可以根据服务定义自动生成样板代码。kitgen工具可以从接口定义生成Endpoint、Transport等代码。truss工具支持从Protobuf定义生成完整的go-kit服务。这些工具大大减少了手工编写重复代码的工作量,提高了开发效率。生成的代码遵循go-kit最佳实践,保证了代码质量。
b.代码示例
---
// 使用truss生成代码
// 1. 定义Protobuf文件 user.proto
syntax = "proto3";
package user;
service UserService {
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
rpc GetUser(GetUserRequest) returns (GetUserResponse);
}
message CreateUserRequest {
string username = 1;
string email = 2;
}
message CreateUserResponse {
User user = 1;
}
message GetUserRequest {
string id = 1;
}
message GetUserResponse {
User user = 1;
}
message User {
string id = 1;
string username = 2;
string email = 3;
}
// 2. 运行truss生成代码
// $ truss user.proto
// 3. 生成的目录结构
// user-service/
// ├── cmd/
// │ └── user-service/
// │ └── main.go
// ├── handlers/
// │ └── handlers.go
// ├── svc/
// │ ├── server/
// │ │ ├── run.go
// │ │ └── server.go
// │ ├── client/
// │ │ └── client.go
// │ └── transport_grpc.go
// └── pb/
// └── user.pb.go
// 4. 实现业务逻辑
package handlers
import "context"
type UserService struct{}
func (s *UserService) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.CreateUserResponse, error) {
// 实现业务逻辑
user := &pb.User{
Id: generateID(),
Username: req.Username,
Email: req.Email,
}
return &pb.CreateUserResponse{User: user}, nil
}
func (s *UserService) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
// 实现业务逻辑
user, err := db.GetUser(ctx, req.Id)
if err != nil {
return nil, err
}
return &pb.GetUserResponse{User: user}, nil
}
---
b.测试工具
a.功能说明
社区提供了专门的测试工具和库,简化go-kit服务的测试。go-kit/kit/endpoint/endpointtest包提供了Endpoint测试辅助函数。可以方便地模拟Service、测试中间件、验证编解码逻辑。支持集成测试和端到端测试,可以启动完整的服务进行测试。测试工具与标准testing包兼容,可以使用熟悉的测试框架。
b.代码示例
---
// go-kit服务测试
package service
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
// Mock Service
type MockUserService struct {
mock.Mock
}
func (m *MockUserService) CreateUser(ctx context.Context, username, email string) (User, error) {
args := m.Called(ctx, username, email)
return args.Get(0).(User), args.Error(1)
}
// 测试Service层
func TestCreateUser(t *testing.T) {
mockSvc := new(MockUserService)
expectedUser := User{ID: "123", Username: "john", Email: "[email protected]"}
mockSvc.On("CreateUser", mock.Anything, "john", "[email protected]").
Return(expectedUser, nil)
user, err := mockSvc.CreateUser(context.Background(), "john", "[email protected]")
assert.NoError(t, err)
assert.Equal(t, expectedUser, user)
mockSvc.AssertExpectations(t)
}
// 测试Endpoint层
func TestCreateUserEndpoint(t *testing.T) {
mockSvc := new(MockUserService)
expectedUser := User{ID: "123", Username: "john", Email: "[email protected]"}
mockSvc.On("CreateUser", mock.Anything, "john", "[email protected]").
Return(expectedUser, nil)
endpoint := makeCreateUserEndpoint(mockSvc)
request := CreateUserRequest{
Username: "john",
Email: "[email protected]",
}
response, err := endpoint(context.Background(), request)
assert.NoError(t, err)
resp := response.(CreateUserResponse)
assert.Equal(t, expectedUser, resp.User)
}
// 测试中间件
func TestLoggingMiddleware(t *testing.T) {
var buf bytes.Buffer
logger := log.NewLogfmtLogger(&buf)
mockSvc := new(MockUserService)
mockSvc.On("CreateUser", mock.Anything, "john", "[email protected]").
Return(User{ID: "123"}, nil)
svc := LoggingMiddleware(logger)(mockSvc)
_, err := svc.CreateUser(context.Background(), "john", "[email protected]")
assert.NoError(t, err)
assert.Contains(t, buf.String(), "method=CreateUser")
}
// 集成测试
func TestHTTPServer(t *testing.T) {
// 启动测试服务器
svc := newUserService(testDB)
endpoint := makeCreateUserEndpoint(svc)
handler := httptransport.NewServer(
endpoint,
decodeCreateUserRequest,
encodeResponse,
)
server := httptest.NewServer(handler)
defer server.Close()
// 发送HTTP请求
reqBody := `{"username":"john","email":"[email protected]"}`
resp, err := http.Post(
server.URL,
"application/json",
strings.NewReader(reqBody),
)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
// 验证响应
var response CreateUserResponse
json.NewDecoder(resp.Body).Decode(&response)
assert.Equal(t, "john", response.User.Username)
}
---
2 三层架构
2.1 架构概览
01.分层职责
a.整体架构
a.功能说明
go-kit采用三层架构模式,从内到外分别是Service层、Endpoint层和Transport层。Service层位于最内层,包含纯粹的业务逻辑实现,不依赖任何框架或传输协议。Endpoint层位于中间,负责将Service方法转换为统一的请求响应模型,处理业务逻辑的编排和组合。Transport层位于最外层,负责具体传输协议的适配,如HTTP、gRPC等。这种分层设计使得各层职责清晰,依赖关系单向,便于测试和维护。
b.代码示例
---
// 三层架构完整示例
package architecture
import (
"context"
"github.com/go-kit/kit/endpoint"
httptransport "github.com/go-kit/kit/transport/http"
)
// 第1层:Service层(业务逻辑)
type StringService interface {
Uppercase(ctx context.Context, s string) (string, error)
Count(ctx context.Context, s string) int
}
type stringService struct{}
func (stringService) Uppercase(_ context.Context, s string) (string, error) {
if s == "" {
return "", ErrEmpty
}
return strings.ToUpper(s), nil
}
func (stringService) Count(_ context.Context, s string) int {
return len(s)
}
// 第2层:Endpoint层(请求响应转换)
type Endpoints struct {
UppercaseEndpoint endpoint.Endpoint
CountEndpoint endpoint.Endpoint
}
func MakeEndpoints(svc StringService) Endpoints {
return Endpoints{
UppercaseEndpoint: makeUppercaseEndpoint(svc),
CountEndpoint: makeCountEndpoint(svc),
}
}
func makeUppercaseEndpoint(svc StringService) 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
}
}
// 第3层:Transport层(协议适配)
func MakeHTTPHandler(endpoints Endpoints) http.Handler {
m := http.NewServeMux()
m.Handle("/uppercase", httptransport.NewServer(
endpoints.UppercaseEndpoint,
decodeUppercaseRequest,
encodeResponse,
))
m.Handle("/count", httptransport.NewServer(
endpoints.CountEndpoint,
decodeCountRequest,
encodeResponse,
))
return m
}
// 请求响应结构
type uppercaseRequest struct {
S string `json:"s"`
}
type uppercaseResponse struct {
V string `json:"v"`
Err string `json:"err,omitempty"`
}
---
b.数据流转
a.功能说明
请求从Transport层进入,经过解码转换为Endpoint的请求对象,Endpoint调用Service方法处理业务逻辑,Service返回结果后,Endpoint将其封装为响应对象,最后Transport层将响应编码为协议特定的格式返回给客户端。整个流程中,数据在各层之间单向流动,每层只关注自己的职责。中间件可以在各层之间插入,对数据流进行拦截和处理。这种清晰的数据流转模式使得系统行为可预测,便于调试和监控。
b.代码示例
---
// 数据流转示例
package dataflow
import (
"context"
"encoding/json"
"net/http"
)
// 完整的请求处理流程
func HandleRequest(w http.ResponseWriter, r *http.Request) {
// 1. Transport层:HTTP请求解码
request, err := decodeHTTPRequest(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// 2. Endpoint层:调用端点
ctx := r.Context()
response, err := uppercaseEndpoint(ctx, request)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 3. Transport层:HTTP响应编码
if err := encodeHTTPResponse(w, response); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
// Transport层:解码HTTP请求
func decodeHTTPRequest(r *http.Request) (interface{}, error) {
var req uppercaseRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
// Endpoint层:处理请求
var uppercaseEndpoint = func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(uppercaseRequest)
// 调用Service层
result, err := svc.Uppercase(ctx, req.S)
if err != nil {
return uppercaseResponse{Err: err.Error()}, nil
}
return uppercaseResponse{V: result}, nil
}
// Service层:业务逻辑
var svc StringService = stringService{}
func (stringService) Uppercase(ctx context.Context, s string) (string, error) {
return strings.ToUpper(s), nil
}
// Transport层:编码HTTP响应
func encodeHTTPResponse(w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
// 数据流转路径:
// HTTP Request -> decodeHTTPRequest -> uppercaseRequest
// -> uppercaseEndpoint -> svc.Uppercase -> result
// -> uppercaseResponse -> encodeHTTPResponse -> HTTP Response
---
02.层级隔离
a.依赖方向
a.功能说明
三层架构遵循严格的依赖方向规则,外层依赖内层,内层不依赖外层。Transport层依赖Endpoint层,Endpoint层依赖Service层,Service层不依赖任何外层。这种单向依赖使得内层可以独立测试,不需要启动完整的服务。Service层可以在没有HTTP服务器的情况下进行单元测试。Endpoint层可以使用Mock Service进行测试。这种隔离性大大提高了代码的可测试性和可维护性。
b.代码示例
---
// 依赖方向示例
package dependency
import "context"
// Service层:不依赖任何外层
type UserService interface {
CreateUser(ctx context.Context, username, email string) (User, error)
}
type userService struct {
db Database // 只依赖同层或更内层的组件
}
func (s *userService) CreateUser(ctx context.Context, username, email string) (User, error) {
// 纯业务逻辑,不知道HTTP、gRPC等传输协议的存在
user := User{
ID: generateID(),
Username: username,
Email: email,
}
return s.db.Save(ctx, user)
}
// Endpoint层:依赖Service层
func makeCreateUserEndpoint(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(createUserRequest)
// 调用Service层方法
user, err := svc.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return createUserResponse{Err: err.Error()}, nil
}
return createUserResponse{User: user}, nil
}
}
// Transport层:依赖Endpoint层
func MakeHTTPHandler(endpoint endpoint.Endpoint) http.Handler {
return httptransport.NewServer(
endpoint, // 依赖Endpoint
decodeHTTPCreateUserRequest,
encodeHTTPResponse,
)
}
// 测试Service层:不需要HTTP��务器
func TestCreateUser(t *testing.T) {
mockDB := &MockDatabase{}
svc := &userService{db: mockDB}
user, err := svc.CreateUser(context.Background(), "john", "[email protected]")
assert.NoError(t, err)
assert.Equal(t, "john", user.Username)
}
// 测试Endpoint层:不需要HTTP服务器
func TestCreateUserEndpoint(t *testing.T) {
mockSvc := &MockUserService{}
endpoint := makeCreateUserEndpoint(mockSvc)
request := createUserRequest{Username: "john", Email: "[email protected]"}
response, err := endpoint(context.Background(), request)
assert.NoError(t, err)
}
---
b.接口抽象
a.功能说明
各层之间通过接口进行交互,而不是具体实现。Service层定义为接口,Endpoint层依赖Service接口而非具体实现。Transport层依赖Endpoint抽象,不关心具体的业务逻辑。这种接口抽象使得各层可以独立演进,实现可以灵活替换。可以为同一个接口提供多种实现,如生产实现、测试实现、Mock实现等。接口抽象也便于依赖注入,提高了代码的灵活性和可测试性。
b.代码示例
---
// 接口抽象示例
package abstraction
import "context"
// Service层接口定义
type OrderService interface {
CreateOrder(ctx context.Context, userID string, items []Item) (Order, error)
GetOrder(ctx context.Context, orderID string) (Order, error)
CancelOrder(ctx context.Context, orderID string) error
}
// 生产环境实现
type productionOrderService struct {
db Database
cache Cache
queue MessageQueue
}
func (s *productionOrderService) CreateOrder(ctx context.Context, userID string, items []Item) (Order, error) {
// 真实的业务逻辑
order := Order{
ID: generateOrderID(),
UserID: userID,
Items: items,
}
if err := s.db.SaveOrder(ctx, order); err != nil {
return Order{}, err
}
s.queue.Publish("order.created", order)
return order, nil
}
// 测试环境实现
type mockOrderService struct {
orders map[string]Order
}
func (s *mockOrderService) CreateOrder(ctx context.Context, userID string, items []Item) (Order, error) {
// 简化的测试逻辑
order := Order{
ID: "test-order-1",
UserID: userID,
Items: items,
}
s.orders[order.ID] = order
return order, nil
}
// Endpoint层:依赖接口而非具体实现
func makeCreateOrderEndpoint(svc OrderService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(createOrderRequest)
// 调用接口方法,不关心具体实现
order, err := svc.CreateOrder(ctx, req.UserID, req.Items)
if err != nil {
return createOrderResponse{Err: err.Error()}, nil
}
return createOrderResponse{Order: order}, nil
}
}
// 依赖注入:可以注入不同的实现
func NewProductionService(db Database, cache Cache, queue MessageQueue) OrderService {
return &productionOrderService{
db: db,
cache: cache,
queue: queue,
}
}
func NewMockService() OrderService {
return &mockOrderService{
orders: make(map[string]Order),
}
}
// 使用示例
func main() {
// 生产环境
svc := NewProductionService(db, cache, queue)
endpoint := makeCreateOrderEndpoint(svc)
// 测试环境
testSvc := NewMockService()
testEndpoint := makeCreateOrderEndpoint(testSvc)
}
// 接口抽象的优势:
// 1. 可以轻松切换实现
// 2. 便于单元测试
// 3. 支持依赖注入
// 4. 降低耦合度
---
2.2 Transport层
01.HTTP传输
a.服务器端
a.功能说明
HTTP传输层负责将Endpoint绑定到HTTP协议上,处理HTTP请求的接收和响应的发送。go-kit提供了httptransport.Server类型,封装了请求解码、端点调用、响应编码的完整流程。支持自定义请求解码器和响应编码器,可以处理JSON、XML、Protobuf等多种格式。支持HTTP中间件,可以添加CORS、认证、日志等功能。与标准库net/http完全兼容,可以集成到现有的HTTP服务中。
b.代码示例
---
// HTTP服务器实现
package transport
import (
"context"
"encoding/json"
"net/http"
httptransport "github.com/go-kit/kit/transport/http"
)
func MakeHTTPHandler(endpoints Endpoints) http.Handler {
m := http.NewServeMux()
// 创建用户端点
m.Handle("/users", httptransport.NewServer(
endpoints.CreateUser,
decodeCreateUserRequest,
encodeResponse,
httptransport.ServerBefore(extractToken),
httptransport.ServerAfter(setResponseHeaders),
httptransport.ServerErrorEncoder(encodeError),
))
// 获取用户端点
m.Handle("/users/", httptransport.NewServer(
endpoints.GetUser,
decodeGetUserRequest,
encodeResponse,
))
return m
}
// 请求解码器
func decodeCreateUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req createUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
func decodeGetUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
parts := strings.Split(r.URL.Path, "/")
id := parts[len(parts)-1]
return getUserRequest{ID: id}, nil
}
// 响应编码器
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
// 错误编码器
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
code := http.StatusInternalServerError
if e, ok := err.(httptransport.Error); ok {
code = e.StatusCode()
}
w.WriteHeader(code)
json.NewEncoder(w).Encode(map[string]interface{}{
"error": err.Error(),
})
}
// ServerBefore:请求前处理
func extractToken(ctx context.Context, r *http.Request) context.Context {
token := r.Header.Get("Authorization")
return context.WithValue(ctx, "token", token)
}
// ServerAfter:响应后处理
func setResponseHeaders(ctx context.Context, w http.ResponseWriter) context.Context {
w.Header().Set("X-Service-Version", "1.0.0")
return ctx
}
---
b.客户端
a.功能说明
HTTP客户端用于调用远程HTTP服务,将本地方法调用转换为HTTP请求。go-kit提供了httptransport.Client类型,封装了请求编码、HTTP调用、响应解码的流程。支持自定义HTTP客户端,可以配置超时、重试、连接池等参数。客户端返回Endpoint类型,可以像调用本地服务一样调用远程服务。支持客户端中间件,可以添加重试、熔断、追踪等功能。
b.代码示例
---
// HTTP客户端实现
package client
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"net/url"
"time"
httptransport "github.com/go-kit/kit/transport/http"
)
// 创建HTTP客户端
func NewHTTPClient(instance string) (Service, error) {
u, err := url.Parse(instance)
if err != nil {
return nil, err
}
// 配置HTTP客户端
httpClient := &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
},
}
// 创建用户端点
createUserEndpoint := httptransport.NewClient(
"POST",
copyURL(u, "/users"),
encodeCreateUserRequest,
decodeCreateUserResponse,
httptransport.SetClient(httpClient),
httptransport.ClientBefore(setRequestHeaders),
).Endpoint()
// 获取用户端点
getUserEndpoint := httptransport.NewClient(
"GET",
copyURL(u, "/users"),
encodeGetUserRequest,
decodeGetUserResponse,
httptransport.SetClient(httpClient),
).Endpoint()
return Endpoints{
CreateUserEndpoint: createUserEndpoint,
GetUserEndpoint: getUserEndpoint,
}, nil
}
// 请求编码器
func encodeCreateUserRequest(_ context.Context, r *http.Request, request interface{}) error {
req := request.(createUserRequest)
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(req); err != nil {
return err
}
r.Body = io.NopCloser(&buf)
return nil
}
func encodeGetUserRequest(_ context.Context, r *http.Request, request interface{}) error {
req := request.(getUserRequest)
r.URL.Path = r.URL.Path + "/" + req.ID
return nil
}
// 响应解码器
func decodeCreateUserResponse(_ context.Context, r *http.Response) (interface{}, error) {
if r.StatusCode != http.StatusOK {
return nil, errors.New("request failed")
}
var resp createUserResponse
if err := json.NewDecoder(r.Body).Decode(&resp); err != nil {
return nil, err
}
return resp, nil
}
func decodeGetUserResponse(_ context.Context, r *http.Response) (interface{}, error) {
var resp getUserResponse
if err := json.NewDecoder(r.Body).Decode(&resp); err != nil {
return nil, err
}
return resp, nil
}
// ClientBefore:请求前处理
func setRequestHeaders(ctx context.Context, r *http.Request) context.Context {
r.Header.Set("Content-Type", "application/json")
if token, ok := ctx.Value("token").(string); ok {
r.Header.Set("Authorization", token)
}
return ctx
}
func copyURL(base *url.URL, path string) *url.URL {
next := *base
next.Path = path
return &next
}
---
02.gRPC传输
a.服务器端
a.功能说明
gRPC传输层提供了对gRPC协议的支持,可以将Endpoint暴露为gRPC服务。go-kit的grpctransport.Server封装了gRPC请求处理流程,包括请求解码、端点调用、响应编码。支持gRPC的流式调用、元数据传递、拦截器等特性。与Protobuf深度集成,可以使用.proto文件定义服务接口。gRPC传输性能优于HTTP,适合内部服务间通信。
b.代码示例
---
// gRPC服务器实现
package transport
import (
"context"
grpctransport "github.com/go-kit/kit/transport/grpc"
"google.golang.org/grpc"
)
// gRPC服务器结构
type grpcServer struct {
createUser grpctransport.Handler
getUser grpctransport.Handler
}
// 创建gRPC服务器
func NewGRPCServer(endpoints Endpoints) pb.UserServiceServer {
return &grpcServer{
createUser: grpctransport.NewServer(
endpoints.CreateUser,
decodeGRPCCreateUserRequest,
encodeGRPCCreateUserResponse,
grpctransport.ServerBefore(extractGRPCMetadata),
),
getUser: grpctransport.NewServer(
endpoints.GetUser,
decodeGRPCGetUserRequest,
encodeGRPCGetUserResponse,
),
}
}
// 实现gRPC服务接口
func (s *grpcServer) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.CreateUserResponse, error) {
_, resp, err := s.createUser.ServeGRPC(ctx, req)
if err != nil {
return nil, err
}
return resp.(*pb.CreateUserResponse), nil
}
func (s *grpcServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
_, resp, err := s.getUser.ServeGRPC(ctx, req)
if err != nil {
return nil, err
}
return resp.(*pb.GetUserResponse), nil
}
// 请求解码器
func decodeGRPCCreateUserRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(*pb.CreateUserRequest)
return createUserRequest{
Username: req.Username,
Email: req.Email,
}, nil
}
func decodeGRPCGetUserRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(*pb.GetUserRequest)
return getUserRequest{ID: req.Id}, nil
}
// 响应编码器
func encodeGRPCCreateUserResponse(_ context.Context, response interface{}) (interface{}, error) {
resp := response.(createUserResponse)
return &pb.CreateUserResponse{
User: &pb.User{
Id: resp.User.ID,
Username: resp.User.Username,
Email: resp.User.Email,
},
}, nil
}
func encodeGRPCGetUserResponse(_ context.Context, response interface{}) (interface{}, error) {
resp := response.(getUserResponse)
return &pb.GetUserResponse{
User: &pb.User{
Id: resp.User.ID,
Username: resp.User.Username,
Email: resp.User.Email,
},
}, nil
}
// 提取gRPC元数据
func extractGRPCMetadata(ctx context.Context, md metadata.MD) context.Context {
if tokens := md.Get("authorization"); len(tokens) > 0 {
ctx = context.WithValue(ctx, "token", tokens[0])
}
return ctx
}
// 启动gRPC服务器
func ServeGRPC(endpoints Endpoints, port string) error {
lis, err := net.Listen("tcp", port)
if err != nil {
return err
}
s := grpc.NewServer()
pb.RegisterUserServiceServer(s, NewGRPCServer(endpoints))
return s.Serve(lis)
}
---
b.客户端
a.功能说明
gRPC客户端用于调用远程gRPC服务,提供了类型安全的调用方式。go-kit的grpctransport.Client封装了gRPC调用流程,包括请求编码、gRPC调用、响应解码。支持连接复用、负载均衡、超时控制等特性。客户端返回Endpoint类型,可以与其他中间件组合使用。支持流式调用,可以处理大数据量的传输。
b.代码示例
---
// gRPC客户端实现
package client
import (
"context"
"time"
grpctransport "github.com/go-kit/kit/transport/grpc"
"google.golang.org/grpc"
)
// 创建gRPC客户端
func NewGRPCClient(conn *grpc.ClientConn) Service {
// 创建用户端点
createUserEndpoint := grpctransport.NewClient(
conn,
"pb.UserService",
"CreateUser",
encodeGRPCCreateUserRequest,
decodeGRPCCreateUserResponse,
&pb.CreateUserResponse{},
grpctransport.ClientBefore(setGRPCMetadata),
).Endpoint()
// 获取用户端点
getUserEndpoint := grpctransport.NewClient(
conn,
"pb.UserService",
"GetUser",
encodeGRPCGetUserRequest,
decodeGRPCGetUserResponse,
&pb.GetUserResponse{},
).Endpoint()
return Endpoints{
CreateUserEndpoint: createUserEndpoint,
GetUserEndpoint: getUserEndpoint,
}
}
// 请求编码器
func encodeGRPCCreateUserRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(createUserRequest)
return &pb.CreateUserRequest{
Username: req.Username,
Email: req.Email,
}, nil
}
func encodeGRPCGetUserRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(getUserRequest)
return &pb.GetUserRequest{Id: req.ID}, nil
}
// 响应解码器
func decodeGRPCCreateUserResponse(_ context.Context, response interface{}) (interface{}, error) {
resp := response.(*pb.CreateUserResponse)
return createUserResponse{
User: User{
ID: resp.User.Id,
Username: resp.User.Username,
Email: resp.User.Email,
},
}, nil
}
func decodeGRPCGetUserResponse(_ context.Context, response interface{}) (interface{}, error) {
resp := response.(*pb.GetUserResponse)
return getUserResponse{
User: User{
ID: resp.User.Id,
Username: resp.User.Username,
Email: resp.User.Email,
},
}, nil
}
// 设置gRPC元数据
func setGRPCMetadata(ctx context.Context, md *metadata.MD) context.Context {
if token, ok := ctx.Value("token").(string); ok {
(*md)["authorization"] = []string{token}
}
return ctx
}
// 创建gRPC连接
func DialGRPC(target string) (*grpc.ClientConn, error) {
return grpc.Dial(
target,
grpc.WithInsecure(),
grpc.WithTimeout(10*time.Second),
grpc.WithBlock(),
)
}
// 使用示例
func main() {
conn, err := DialGRPC("localhost:9090")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := NewGRPCClient(conn)
resp, err := client.CreateUserEndpoint(context.Background(), createUserRequest{
Username: "john",
Email: "[email protected]",
})
}
---
2.3 Endpoint层
01.端点定义
a.标准签名
a.功能说明
Endpoint是go-kit的核心抽象,定义为接收context.Context和request接口,返回response接口和error的函数。这种统一的签名使得所有业务方法都可以转换为Endpoint,便于中间件的应用和组合。Endpoint将具体的业务方法抽象为通用的请求响应模型,实现了业务逻辑与传输协议的解耦。每个Service方法对应一个Endpoint,Endpoint负责参数转换和结果封装。
b.代码示例
---
// Endpoint标准定义
package endpoint
import (
"context"
"github.com/go-kit/kit/endpoint"
)
// Endpoint类型定义
type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error)
// 创建Endpoint
func MakeCreateUserEndpoint(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := svc.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err.Error()}, nil
}
return CreateUserResponse{User: user}, nil
}
}
func MakeGetUserEndpoint(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(GetUserRequest)
user, err := svc.GetUser(ctx, req.ID)
if err != nil {
return GetUserResponse{Err: err.Error()}, nil
}
return GetUserResponse{User: user}, nil
}
}
// 请求响应结构
type CreateUserRequest struct {
Username string `json:"username"`
Email string `json:"email"`
}
type CreateUserResponse struct {
User User `json:"user,omitempty"`
Err string `json:"err,omitempty"`
}
type GetUserRequest struct {
ID string `json:"id"`
}
type GetUserResponse struct {
User User `json:"user,omitempty"`
Err string `json:"err,omitempty"`
}
---
b.端点集合
a.功能说明
通常将一个服务的所有Endpoint组织为一个结构体,便于统一管理和传递。Endpoint集合包含服务的所有业务方法对应的Endpoint,可以作为一个整体传递给Transport层。这种组织方式使得服务的接口清晰明确,便于客户端调用。Endpoint集合也可以实现Service接口,使得Endpoint可以像Service一样使用。
b.代码示例
---
// Endpoint集合
package endpoint
import (
"context"
"github.com/go-kit/kit/endpoint"
)
// Endpoints结构
type Endpoints struct {
CreateUserEndpoint endpoint.Endpoint
GetUserEndpoint endpoint.Endpoint
UpdateUserEndpoint endpoint.Endpoint
DeleteUserEndpoint endpoint.Endpoint
}
// 创建Endpoints
func MakeEndpoints(svc UserService, middlewares []endpoint.Middleware) Endpoints {
var createUserEndpoint endpoint.Endpoint
createUserEndpoint = MakeCreateUserEndpoint(svc)
for _, m := range middlewares {
createUserEndpoint = m(createUserEndpoint)
}
var getUserEndpoint endpoint.Endpoint
getUserEndpoint = MakeGetUserEndpoint(svc)
for _, m := range middlewares {
getUserEndpoint = m(getUserEndpoint)
}
return Endpoints{
CreateUserEndpoint: createUserEndpoint,
GetUserEndpoint: getUserEndpoint,
}
}
// Endpoints实现Service接口
func (e Endpoints) CreateUser(ctx context.Context, username, email string) (User, error) {
request := CreateUserRequest{Username: username, Email: email}
response, err := e.CreateUserEndpoint(ctx, request)
if err != nil {
return User{}, err
}
resp := response.(CreateUserResponse)
if resp.Err != "" {
return User{}, errors.New(resp.Err)
}
return resp.User, nil
}
func (e Endpoints) GetUser(ctx context.Context, id string) (User, error) {
request := GetUserRequest{ID: id}
response, err := e.GetUserEndpoint(ctx, request)
if err != nil {
return User{}, err
}
resp := response.(GetUserResponse)
if resp.Err != "" {
return User{}, errors.New(resp.Err)
}
return resp.User, nil
}
---
02.中间件应用
a.中间件链
a.功能说明
Endpoint中间件可以链式组合,形成处理管道。每个中间件接收一个Endpoint并返回增强后的Endpoint,多个中间件按顺序应用。中间件可以在Endpoint执行前后添加逻辑,如日志记录、性能监控、参数验证等。中间件的执行顺序由添加顺序决定,最先添加的中间件最外层,最后执行。合理组织中间件顺序可以优化性能和功能。
b.代码示例
---
// 中间件链应用
package middleware
import (
"context"
"time"
"github.com/go-kit/kit/endpoint"
)
// 日志中间件
func LoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
logger.Log("msg", "calling endpoint")
defer func(begin time.Time) {
logger.Log("msg", "called endpoint", "took", time.Since(begin))
}(time.Now())
return next(ctx, request)
}
}
}
// 监控中间件
func InstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
duration.Observe(time.Since(begin).Seconds())
}(time.Now())
return next(ctx, request)
}
}
}
// 限流中间件
func RateLimitMiddleware(limiter *rate.Limiter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if !limiter.Allow() {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
// 应用中间件链
func ApplyMiddlewares(e endpoint.Endpoint, middlewares ...endpoint.Middleware) endpoint.Endpoint {
for i := len(middlewares) - 1; i >= 0; i-- {
e = middlewares[i](e)
}
return e
}
// 使用示例
func BuildEndpoint(svc UserService) endpoint.Endpoint {
var e endpoint.Endpoint
e = MakeCreateUserEndpoint(svc)
// 应用多个中间件
e = ApplyMiddlewares(e,
LoggingMiddleware(logger),
InstrumentingMiddleware(histogram),
RateLimitMiddleware(limiter),
)
return e
}
---
b.条件中间件
a.功能说明
可以根据条件选择性地应用中间件,实现灵活的功能控制。条件中间件可以根据配置、环境变量、请求特征等条件决定是否执行。这种机制使得同一套代码可以在不同环境下有不同的行为,如开发环境启用详细日志,生产环境启用性能监控。条件中间件也可以用于A/B测试、灰度发布等场景。
b.代码示例
---
// 条件中间件
package middleware
import (
"context"
"github.com/go-kit/kit/endpoint"
)
// 条件中间件包装器
func ConditionalMiddleware(condition func() bool, middleware endpoint.Middleware) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
if condition() {
return middleware(next)
}
return next
}
}
// 环境条件中间件
func EnvironmentMiddleware(env string, middleware endpoint.Middleware) endpoint.Middleware {
return ConditionalMiddleware(
func() bool { return os.Getenv("ENV") == env },
middleware,
)
}
// 功能开关中间件
func FeatureFlagMiddleware(flag string, middleware endpoint.Middleware) endpoint.Middleware {
return ConditionalMiddleware(
func() bool { return isFeatureEnabled(flag) },
middleware,
)
}
// 使用示例
func BuildEndpointWithConditions(svc UserService) endpoint.Endpoint {
var e endpoint.Endpoint
e = MakeCreateUserEndpoint(svc)
// 开发环境启用详细日志
e = EnvironmentMiddleware("development", DetailedLoggingMiddleware(logger))(e)
// 生产环境启用性能监控
e = EnvironmentMiddleware("production", InstrumentingMiddleware(metrics))(e)
// 功能开关控制限流
e = FeatureFlagMiddleware("rate_limiting", RateLimitMiddleware(limiter))(e)
// 始终启用的中间件
e = LoggingMiddleware(logger)(e)
return e
}
// 请求特征条件中间件
func RequestBasedMiddleware(predicate func(interface{}) bool, middleware endpoint.Middleware) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
wrapped := middleware(next)
return func(ctx context.Context, request interface{}) (interface{}, error) {
if predicate(request) {
return wrapped(ctx, request)
}
return next(ctx, request)
}
}
}
// 根据用户类型应用不同中间件
func UserTypeMiddleware(svc UserService) endpoint.Endpoint {
var e endpoint.Endpoint
e = MakeCreateUserEndpoint(svc)
// VIP用户不限流
e = RequestBasedMiddleware(
func(req interface{}) bool {
r := req.(CreateUserRequest)
return !isVIPUser(r.Username)
},
RateLimitMiddleware(limiter),
)(e)
return e
}
---
2.4 Service层
01.接口设计
a.方法定义
a.功能说明
Service层接口定义了服务的所有业务方法,每个方法代表一个业务操作。方法签名应该清晰明确,第一个参数必须是context.Context用于传递请求上下文和超时控制。返回值通常包含业务对象和error,error用于表示业务异常。接口设计应该遵循单一职责原则,每个接口只负责一类业务功能。方法命名应该使用动词开头,清晰表达操作意图。
b.代码示例
---
// Service接口设计
package service
import "context"
type UserService interface {
CreateUser(ctx context.Context, username, email string) (User, error)
GetUser(ctx context.Context, id string) (User, error)
UpdateUser(ctx context.Context, id string, updates UserUpdates) error
DeleteUser(ctx context.Context, id string) error
ListUsers(ctx context.Context, offset, limit int) ([]User, error)
}
type OrderService interface {
CreateOrder(ctx context.Context, userID string, items []Item) (Order, error)
GetOrder(ctx context.Context, orderID string) (Order, error)
CancelOrder(ctx context.Context, orderID string) error
UpdateOrderStatus(ctx context.Context, orderID string, status OrderStatus) error
}
type User struct {
ID string `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Created int64 `json:"created"`
}
type UserUpdates struct {
Username *string `json:"username,omitempty"`
Email *string `json:"email,omitempty"`
}
---
b.实现类
a.功能说明
Service接口的实现类包含具体的业务逻辑,通常持有必要的依赖项如数据库连接、缓存客户端、其他服务客户端等。实现类通过构造函数接收依赖项,遵循依赖注入原则。业务逻辑应该保持纯粹,不包含日志、监控等横切关注点,这些功能通过中间件实现。实现类应该处理业务规则验证、数据转换、事务管理等核心业务逻辑。
b.代码示例
---
// Service实现
package service
import (
"context"
"errors"
"time"
)
type userService struct {
db Database
cache Cache
email EmailService
}
func NewUserService(db Database, cache Cache, email EmailService) UserService {
return &userService{
db: db,
cache: cache,
email: email,
}
}
func (s *userService) CreateUser(ctx context.Context, username, email string) (User, error) {
if username == "" || email == "" {
return User{}, errors.New("username and email required")
}
exists, err := s.db.UserExists(ctx, username)
if err != nil {
return User{}, err
}
if exists {
return User{}, errors.New("username already exists")
}
user := User{
ID: generateID(),
Username: username,
Email: email,
Created: time.Now().Unix(),
}
if err := s.db.SaveUser(ctx, user); err != nil {
return User{}, err
}
s.cache.Set(ctx, "user:"+user.ID, user, 5*time.Minute)
s.email.SendWelcomeEmail(ctx, email)
return user, nil
}
func (s *userService) GetUser(ctx context.Context, id string) (User, error) {
if cached, ok := s.cache.Get(ctx, "user:"+id); ok {
return cached.(User), nil
}
user, err := s.db.GetUser(ctx, id)
if err != nil {
return User{}, err
}
s.cache.Set(ctx, "user:"+id, user, 5*time.Minute)
return user, nil
}
func (s *userService) UpdateUser(ctx context.Context, id string, updates UserUpdates) error {
user, err := s.db.GetUser(ctx, id)
if err != nil {
return err
}
if updates.Username != nil {
user.Username = *updates.Username
}
if updates.Email != nil {
user.Email = *updates.Email
}
if err := s.db.UpdateUser(ctx, user); err != nil {
return err
}
s.cache.Delete(ctx, "user:"+id)
return nil
}
---
02.依赖管理
a.依赖注入
a.功能说明
Service层通过构造函数接收所有依赖项,实现依赖注入。依赖项应该定义为接口而非具体实现,提高灵活性和可测试性。构造函数应该验证依赖项的有效性,确保Service实例处于可用状态。依赖注入使得Service可以在不同环境下使用不同的实现,如测试环境使用Mock实现,生产环境使用真实实现。
b.代码示例
---
// 依赖注入示例
package service
import "context"
type Database interface {
SaveUser(ctx context.Context, user User) error
GetUser(ctx context.Context, id string) (User, error)
UpdateUser(ctx context.Context, user User) error
DeleteUser(ctx context.Context, id string) error
}
type Cache interface {
Get(ctx context.Context, key string) (interface{}, bool)
Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error
Delete(ctx context.Context, key string) error
}
type EmailService interface {
SendWelcomeEmail(ctx context.Context, email string) error
SendPasswordResetEmail(ctx context.Context, email, token string) error
}
type userService struct {
db Database
cache Cache
email EmailService
}
func NewUserService(db Database, cache Cache, email EmailService) UserService {
if db == nil {
panic("database is required")
}
if cache == nil {
panic("cache is required")
}
if email == nil {
panic("email service is required")
}
return &userService{
db: db,
cache: cache,
email: email,
}
}
func main() {
db := NewPostgresDatabase(dbConfig)
cache := NewRedisCache(redisConfig)
email := NewSMTPEmailService(smtpConfig)
svc := NewUserService(db, cache, email)
endpoints := MakeEndpoints(svc)
handler := MakeHTTPHandler(endpoints)
http.ListenAndServe(":8080", handler)
}
---
b.接口隔离
a.功能说明
Service层的依赖应该定义为最小化的接口,只包含Service实际需要的方法。这遵循接口隔离原则,避免Service依赖过大的接口。小接口更容易Mock和测试,也更容易理解和维护。可以为不同的Service定义不同的接口,即使它们使用同一个底层实现。接口隔离使得Service与具体实现解耦,提高了系统的灵活性。
b.代码示例
---
// 接口隔离示例
package service
import "context"
type UserRepository interface {
SaveUser(ctx context.Context, user User) error
GetUser(ctx context.Context, id string) (User, error)
UserExists(ctx context.Context, username string) (bool, error)
}
type OrderRepository interface {
SaveOrder(ctx context.Context, order Order) error
GetOrder(ctx context.Context, id string) (Order, error)
UpdateOrderStatus(ctx context.Context, id string, status OrderStatus) error
}
type UserCache interface {
GetUser(ctx context.Context, id string) (User, bool)
SetUser(ctx context.Context, id string, user User, ttl time.Duration) error
DeleteUser(ctx context.Context, id string) error
}
type userService struct {
repo UserRepository
cache UserCache
}
func NewUserService(repo UserRepository, cache UserCache) UserService {
return &userService{
repo: repo,
cache: cache,
}
}
type postgresDB struct {
db *sql.DB
}
func (p *postgresDB) SaveUser(ctx context.Context, user User) error {
_, err := p.db.ExecContext(ctx, "INSERT INTO users ...")
return err
}
func (p *postgresDB) GetUser(ctx context.Context, id string) (User, error) {
var user User
err := p.db.QueryRowContext(ctx, "SELECT * FROM users WHERE id = $1", id).Scan(&user)
return user, err
}
func (p *postgresDB) UserExists(ctx context.Context, username string) (bool, error) {
var exists bool
err := p.db.QueryRowContext(ctx, "SELECT EXISTS(SELECT 1 FROM users WHERE username = $1)", username).Scan(&exists)
return exists, err
}
func (p *postgresDB) SaveOrder(ctx context.Context, order Order) error {
_, err := p.db.ExecContext(ctx, "INSERT INTO orders ...")
return err
}
func NewPostgresDB(config DBConfig) (*postgresDB, error) {
db, err := sql.Open("postgres", config.DSN)
if err != nil {
return nil, err
}
return &postgresDB{db: db}, nil
}
func main() {
db, _ := NewPostgresDB(dbConfig)
cache := NewRedisCache(redisConfig)
userSvc := NewUserService(db, cache)
orderSvc := NewOrderService(db)
}
---
2.5 层级交互
01.请求流程
a.完整链路
a.功能说明
请求从客户端发起,经过Transport层解码为请求对象,传递给Endpoint层。Endpoint层调用Service层的业务方法处理请求,Service层返回结果后,Endpoint将其封装为响应对象。Transport层将响应对象编码为协议格式返回给客户端。整个流程中,各层职责清晰,数据单向流动。中间件可以在各个环节插入,实现日志、监控、限流等功能。
b.代码示例
---
// 完整请求流程
package flow
import (
"context"
"encoding/json"
"net/http"
)
func CompleteRequestFlow() {
// 1. 创建Service层
db := NewDatabase()
cache := NewCache()
svc := NewUserService(db, cache)
// 2. 创建Endpoint层
endpoint := MakeCreateUserEndpoint(svc)
endpoint = LoggingMiddleware(logger)(endpoint)
endpoint = InstrumentingMiddleware(metrics)(endpoint)
// 3. 创建Transport层
handler := httptransport.NewServer(
endpoint,
decodeCreateUserRequest,
encodeResponse,
)
// 4. 启动HTTP服务器
http.Handle("/users", handler)
http.ListenAndServe(":8080", nil)
}
// 请求处理流程
func HandleCreateUserRequest(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// Transport层:解码HTTP请求
request, err := decodeCreateUserRequest(ctx, r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Endpoint层:调用端点
response, err := createUserEndpoint(ctx, request)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Transport层:编码HTTP响应
encodeResponse(ctx, w, response)
}
var createUserEndpoint = func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := svc.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err.Error()}, nil
}
return CreateUserResponse{User: user}, nil
}
---
b.错误处理
a.功能说明
各层对错误的处理方式不同。Service层返回业务错误,Endpoint层将错误封装到响应对象中,Transport层将错误转换为协议特定的错误格式。可以定义自定义错误类型,携带错误码、错误消息等信息。错误处理应该区分业务错误和系统错误,业务错误返回给客户端,系统错误记录日志。错误信息应该清晰明确,便于问题排查。
b.代码示例
---
// 错误处理示例
package errors
import (
"encoding/json"
"net/http"
)
// 自定义错误类型
type ServiceError struct {
Code string `json:"code"`
Message string `json:"message"`
}
func (e ServiceError) Error() string {
return e.Message
}
var (
ErrUserNotFound = ServiceError{"USER_NOT_FOUND", "User not found"}
ErrInvalidInput = ServiceError{"INVALID_INPUT", "Invalid input"}
ErrUnauthorized = ServiceError{"UNAUTHORIZED", "Unauthorized"}
ErrInternalError = ServiceError{"INTERNAL_ERROR", "Internal server error"}
)
// Service层:返回业务错误
func (s *userService) GetUser(ctx context.Context, id string) (User, error) {
if id == "" {
return User{}, ErrInvalidInput
}
user, err := s.db.GetUser(ctx, id)
if err == sql.ErrNoRows {
return User{}, ErrUserNotFound
}
if err != nil {
return User{}, ErrInternalError
}
return user, nil
}
// Endpoint层:封装错误到响应
func MakeGetUserEndpoint(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(GetUserRequest)
user, err := svc.GetUser(ctx, req.ID)
if err != nil {
return GetUserResponse{Err: err.Error()}, nil
}
return GetUserResponse{User: user}, nil
}
}
// Transport层:转换为HTTP错误
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
code := http.StatusInternalServerError
if e, ok := err.(ServiceError); ok {
switch e.Code {
case "USER_NOT_FOUND":
code = http.StatusNotFound
case "INVALID_INPUT":
code = http.StatusBadRequest
case "UNAUTHORIZED":
code = http.StatusUnauthorized
}
}
w.WriteHeader(code)
json.NewEncoder(w).Encode(map[string]interface{}{
"error": err.Error(),
})
}
---
02.上下文传递
a.Context使用
a.功能说明
context.Context在各层之间传递请求上下文信息,包括超时控制、取消信号、请求ID、用户信息等。每个Service方法的第一个参数必须是context.Context,Endpoint和Transport层也使用Context传递信息。可以使用context.WithValue存储请求相关的数据,如认证token、追踪ID等。Context应该只用于传递请求范围的数据,不应该存储业务逻辑需要的参数。
b.代码示例
---
// Context传递示例
package context
import (
"context"
"time"
)
type contextKey string
const (
ContextKeyRequestID contextKey = "request_id"
ContextKeyUserID contextKey = "user_id"
ContextKeyToken contextKey = "token"
)
// Transport层:从HTTP请求提取信息到Context
func extractRequestContext(ctx context.Context, r *http.Request) context.Context {
requestID := r.Header.Get("X-Request-ID")
if requestID == "" {
requestID = generateRequestID()
}
ctx = context.WithValue(ctx, ContextKeyRequestID, requestID)
token := r.Header.Get("Authorization")
if token != "" {
ctx = context.WithValue(ctx, ContextKeyToken, token)
userID := extractUserIDFromToken(token)
ctx = context.WithValue(ctx, ContextKeyUserID, userID)
}
return ctx
}
// Endpoint层:使用Context信息
func MakeCreateOrderEndpoint(svc OrderService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateOrderRequest)
userID, ok := ctx.Value(ContextKeyUserID).(string)
if !ok {
return nil, ErrUnauthorized
}
order, err := svc.CreateOrder(ctx, userID, req.Items)
if err != nil {
return CreateOrderResponse{Err: err.Error()}, nil
}
return CreateOrderResponse{Order: order}, nil
}
}
// Service层:使用Context控制超时
func (s *orderService) CreateOrder(ctx context.Context, userID string, items []Item) (Order, error) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
order := Order{
ID: generateOrderID(),
UserID: userID,
Items: items,
}
if err := s.db.SaveOrder(ctx, order); err != nil {
return Order{}, err
}
select {
case <-ctx.Done():
return Order{}, ctx.Err()
default:
return order, nil
}
}
// 日志中间件:使用Context中的RequestID
func LoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
requestID := ctx.Value(ContextKeyRequestID)
logger.Log("request_id", requestID, "msg", "calling endpoint")
return next(ctx, request)
}
}
}
---
b.元数据传递
a.功能说明
元数据包括请求ID、追踪ID、用户信息、认证token等非业务数据,通过Context在各层之间传递。Transport层负责从协议中提取元数据并注入Context,如从HTTP Header或gRPC Metadata中提取。Endpoint层和Service层可以从Context中读取元数据,用于日志记录、权限验证等。元���据的传递应该是透明的,不影响业务逻辑的实现。
b.代码示例
---
// 元数据传递示例
package metadata
import (
"context"
"google.golang.org/grpc/metadata"
)
// HTTP元数据提取
func HTTPMetadataToContext(ctx context.Context, r *http.Request) context.Context {
md := make(map[string]string)
md["request-id"] = r.Header.Get("X-Request-ID")
md["user-agent"] = r.Header.Get("User-Agent")
md["client-ip"] = r.RemoteAddr
for k, v := range md {
ctx = context.WithValue(ctx, contextKey(k), v)
}
return ctx
}
// gRPC元数据提取
func GRPCMetadataToContext(ctx context.Context) context.Context {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return ctx
}
if requestID := md.Get("request-id"); len(requestID) > 0 {
ctx = context.WithValue(ctx, ContextKeyRequestID, requestID[0])
}
if token := md.Get("authorization"); len(token) > 0 {
ctx = context.WithValue(ctx, ContextKeyToken, token[0])
}
return ctx
}
// 元数据注入HTTP响应
func ContextToHTTPResponse(ctx context.Context, w http.ResponseWriter) {
if requestID, ok := ctx.Value(ContextKeyRequestID).(string); ok {
w.Header().Set("X-Request-ID", requestID)
}
if traceID, ok := ctx.Value("trace-id").(string); ok {
w.Header().Set("X-Trace-ID", traceID)
}
}
// 元数据注入gRPC响应
func ContextToGRPCMetadata(ctx context.Context) context.Context {
md := metadata.New(map[string]string{})
if requestID, ok := ctx.Value(ContextKeyRequestID).(string); ok {
md.Set("request-id", requestID)
}
return metadata.NewOutgoingContext(ctx, md)
}
// 使用示例
func MakeHTTPHandler(endpoint endpoint.Endpoint) http.Handler {
return httptransport.NewServer(
endpoint,
decodeRequest,
encodeResponse,
httptransport.ServerBefore(HTTPMetadataToContext),
httptransport.ServerAfter(func(ctx context.Context, w http.ResponseWriter) context.Context {
ContextToHTTPResponse(ctx, w)
return ctx
}),
)
}
---
2.6 最佳实践
01.分层原则
a.职责分离
a.功能说明
严格遵守各层职责分离原则,Service层只包含业务逻辑,Endpoint层只做请求响应转换,Transport层只处理协议适配。不要在Service层处理HTTP请求,不要在Transport层编写业务逻辑。保持各层的纯粹性,使得代码易于理解和维护。职责分离也便于单元测试,每层可以独立测试。遵循单一职责原则,每个组件只做一件事。
b.代码示例
---
// 职责分离示例
package practice
// ✅ 正确:Service层只包含业务逻辑
func (s *userService) CreateUser(ctx context.Context, username, email string) (User, error) {
if username == "" || email == "" {
return User{}, errors.New("invalid input")
}
user := User{
ID: generateID(),
Username: username,
Email: email,
}
return s.db.SaveUser(ctx, user)
}
// ❌ 错误:Service层不应该处理HTTP
func (s *userService) CreateUserFromHTTP(w http.ResponseWriter, r *http.Request) {
var req CreateUserRequest
json.NewDecoder(r.Body).Decode(&req)
user, _ := s.CreateUser(r.Context(), req.Username, req.Email)
json.NewEncoder(w).Encode(user)
}
// ✅ 正确:Endpoint层只做转换
func MakeCreateUserEndpoint(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := svc.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err.Error()}, nil
}
return CreateUserResponse{User: user}, nil
}
}
// ❌ 错误:Endpoint层不应该包含业务逻辑
func MakeCreateUserEndpoint(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
if req.Username == "" {
return nil, errors.New("username required")
}
if !isValidEmail(req.Email) {
return nil, errors.New("invalid email")
}
user, err := svc.CreateUser(ctx, req.Username, req.Email)
return CreateUserResponse{User: user}, err
}
}
---
b.接口优先
a.功能说明
始终使用接口而非具体实现进行依赖,提高代码的灵活性和可测试性。Service层定义为接口,依赖项也定义为接口。接口应该小而专注,只包含必要的方法。通过接口可以轻松替换实现,如测试时使用Mock实现。接口优先的设计使得代码更容易扩展和维护。
b.代码示例
---
// 接口优先示例
package practice
// ✅ 正确:使用接口定义依赖
type UserService interface {
CreateUser(ctx context.Context, username, email string) (User, error)
}
type UserRepository interface {
SaveUser(ctx context.Context, user User) error
GetUser(ctx context.Context, id string) (User, error)
}
type userService struct {
repo UserRepository
}
func NewUserService(repo UserRepository) UserService {
return &userService{repo: repo}
}
// ❌ 错误:直接依赖具体实现
type userService struct {
db *sql.DB
}
func NewUserService(db *sql.DB) *userService {
return &userService{db: db}
}
// ✅ 正确:小而专注的接口
type UserReader interface {
GetUser(ctx context.Context, id string) (User, error)
}
type UserWriter interface {
SaveUser(ctx context.Context, user User) error
}
// ❌ 错误:过大的接口
type UserRepository interface {
SaveUser(ctx context.Context, user User) error
GetUser(ctx context.Context, id string) (User, error)
DeleteUser(ctx context.Context, id string) error
ListUsers(ctx context.Context) ([]User, error)
SaveOrder(ctx context.Context, order Order) error
GetOrder(ctx context.Context, id string) (Order, error)
}
---
02.代码组织
a.目录结构
a.功能说明
合理组织代码目录结构,按照功能模块划分包。通常将Service、Endpoint、Transport分别放在不同的包中。每个服务一个独立的目录,包含该服务的所有代码。公共代码如中间件、工具函数放在shared包中。清晰的目录结构使得代码易于导航和维护。
b.代码示例
---
// 推荐的目录结构
user-service/
├── cmd/
│ └── server/
│ └── main.go # 服务入口
├── pkg/
│ ├── service/
│ │ ├── service.go # Service接口定义
│ │ ├── user.go # User服务实现
│ │ └── middleware.go # Service中间件
│ ├── endpoint/
│ │ ├── endpoint.go # Endpoint定义
│ │ ├── middleware.go # Endpoint中间件
│ │ └── request_response.go # 请求响应结构
│ ├── transport/
│ │ ├── http.go # HTTP传输
│ │ ├── grpc.go # gRPC传输
│ │ └── encode_decode.go # 编解码函数
│ └── model/
│ └── user.go # 数据模型
├── internal/
│ ├── repository/
│ │ └── postgres.go # 数据库实现
│ └── cache/
│ └── redis.go # 缓存实现
├── shared/
│ ├── middleware/
│ │ ├── logging.go
│ │ ├── metrics.go
│ │ └── tracing.go
│ └── errors/
│ └── errors.go
└── go.mod
// main.go 组织
package main
import (
"user-service/pkg/service"
"user-service/pkg/endpoint"
"user-service/pkg/transport"
"user-service/internal/repository"
)
func main() {
db := repository.NewPostgresDB(config)
svc := service.NewUserService(db)
endpoints := endpoint.MakeEndpoints(svc)
handler := transport.MakeHTTPHandler(endpoints)
http.ListenAndServe(":8080", handler)
}
---
b.命名规范
a.功能说明
遵循Go语言命名规范,使用清晰明确的命名。接口名使用名词,方法名使用动词开头。请求响应结构使用Request和Response后缀。中间件函数使用Middleware后缀。包名使用小写单数名词。避免使用缩写,除非是广为人知的缩写。好的命名使得代码自文档化,易于理解。
b.代码示例
---
// 命名规范示例
package naming
// ✅ 正确:接口使用名词
type UserService interface {
CreateUser(ctx context.Context, username, email string) (User, error)
}
type OrderService interface {
CreateOrder(ctx context.Context, userID string, items []Item) (Order, error)
}
// ❌ 错误:接口名不清晰
type IUser interface {
Create(ctx context.Context, username, email string) (User, error)
}
// ✅ 正确:方法使用动词开头
func (s *userService) CreateUser(ctx context.Context, username, email string) (User, error)
func (s *userService) GetUser(ctx context.Context, id string) (User, error)
func (s *userService) UpdateUser(ctx context.Context, id string, updates UserUpdates) error
func (s *userService) DeleteUser(ctx context.Context, id string) error
// ❌ 错误:方法名不清晰
func (s *userService) User(ctx context.Context, username, email string) (User, error)
func (s *userService) Fetch(ctx context.Context, id string) (User, error)
// ✅ 正确:请求响应使用Request/Response后缀
type CreateUserRequest struct {
Username string `json:"username"`
Email string `json:"email"`
}
type CreateUserResponse struct {
User User `json:"user,omitempty"`
Err string `json:"err,omitempty"`
}
// ❌ 错误:命名不规范
type CreateUserReq struct {
Username string
Email string
}
type CreateUserResp struct {
User User
Err string
}
// ✅ 正确:中间件使用Middleware后缀
func LoggingMiddleware(logger log.Logger) endpoint.Middleware
func InstrumentingMiddleware(metrics Metrics) endpoint.Middleware
func RateLimitMiddleware(limiter *rate.Limiter) endpoint.Middleware
// ❌ 错误:中间件命名不规范
func Logger(logger log.Logger) endpoint.Middleware
func Metrics(metrics Metrics) endpoint.Middleware
func RateLimit(limiter *rate.Limiter) endpoint.Middleware
---
3 服务端点
3.1 Endpoint定义
01.基础概念
a.端点抽象
a.功能说明
Endpoint是go-kit的核心抽象,将所有业务方法统一为相同的函数签名。接收context.Context和request接口,返回response接口和error。这种统一的签名使得所有业务方法都可以使用相同的中间件和处理逻辑,实现了业务逻辑与传输协议的完全解耦。Endpoint作为Service层和Transport层之间的桥梁,负责将具体的业务方法调用转换为通用的请求响应模型。
b.代码示例
---
// Endpoint基础定义
package endpoint
import (
"context"
"github.com/go-kit/kit/endpoint"
)
// Endpoint类型定义
type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error)
// 创建基础Endpoint
func MakeCreateUserEndpoint(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := svc.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err.Error()}, nil
}
return CreateUserResponse{User: user}, nil
}
}
func MakeGetUserEndpoint(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(GetUserRequest)
user, err := svc.GetUser(ctx, req.ID)
if err != nil {
return GetUserResponse{Err: err.Error()}, nil
}
return GetUserResponse{User: user}, nil
}
}
// 请求响应结构
type CreateUserRequest struct {
Username string `json:"username"`
Email string `json:"email"`
}
type CreateUserResponse struct {
User User `json:"user,omitempty"`
Err string `json:"err,omitempty"`
}
type GetUserRequest struct {
ID string `json:"id"`
}
type GetUserResponse struct {
User User `json:"user,omitempty"`
Err string `json:"err,omitempty"`
}
---
b.端点工厂
a.功能说明
端点工厂函数负责创建和配置Endpoint实例,接收Service作为参数,返回配置好的Endpoint。工厂函数封装了Endpoint的创建逻辑,包括请求响应转换、错误处理、中间件应用等。使用工厂模式可以标准化Endpoint的创建过程,便于维护和测试。工厂函数还可以根据配置动态调整Endpoint的行为,如启用或禁用某些中间件。
b.代码示例
---
// Endpoint工厂函数
package endpoint
import (
"context"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/metrics"
)
// EndpointFactory 端点工厂
type EndpointFactory struct {
logger log.Logger
metrics metrics.Histogram
}
func NewEndpointFactory(logger log.Logger, metrics metrics.Histogram) *EndpointFactory {
return &EndpointFactory{
logger: logger,
metrics: metrics,
}
}
// CreateEndpoint 创建带中间件的Endpoint
func (f *EndpointFactory) CreateEndpoint(svc UserService, method string) endpoint.Endpoint {
var e endpoint.Endpoint
switch method {
case "CreateUser":
e = MakeCreateUserEndpoint(svc)
case "GetUser":
e = MakeGetUserEndpoint(svc)
case "UpdateUser":
e = MakeUpdateUserEndpoint(svc)
case "DeleteUser":
e = MakeDeleteUserEndpoint(svc)
default:
panic("unknown method: " + method)
}
// 应用通用中间件
e = LoggingMiddleware(f.logger)(e)
e = InstrumentingMiddleware(f.metrics)(e)
return e
}
// MakeEndpoints 批量创建Endpoints
func MakeEndpoints(svc UserService, logger log.Logger, metrics metrics.Histogram) Endpoints {
factory := NewEndpointFactory(logger, metrics)
return Endpoints{
CreateUserEndpoint: factory.CreateEndpoint(svc, "CreateUser"),
GetUserEndpoint: factory.CreateEndpoint(svc, "GetUser"),
UpdateUserEndpoint: factory.CreateEndpoint(svc, "UpdateUser"),
DeleteUserEndpoint: factory.CreateEndpoint(svc, "DeleteUser"),
}
}
type Endpoints struct {
CreateUserEndpoint endpoint.Endpoint
GetUserEndpoint endpoint.Endpoint
UpdateUserEndpoint endpoint.Endpoint
DeleteUserEndpoint endpoint.Endpoint
}
---
02.请求响应模型
a.结构设计
a.功能说明
请求响应结构定义了Endpoint的输入输出格式,每个Endpoint都有对应的Request和Response类型。Request结构包含业务方法所需的所有参数,Response结构包含业务方法的返回值和错误信息。使用结构体而非多个参数可以提高代码的可读性和可维护性,便于添加新字段而不影响现有代码。Response结构通常包含Err字段用于传递错误信息,避免使用error返回值。
b.代码示例
---
// 请求响应模型设计
package endpoint
import "time"
// CreateUserRequest 创建用户请求
type CreateUserRequest struct {
Username string `json:"username" validate:"required,min=3,max=20"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=6"`
}
// CreateUserResponse 创建用户响应
type CreateUserResponse struct {
User User `json:"user,omitempty"`
Err string `json:"err,omitempty"`
}
// GetUserRequest 获取用户请求
type GetUserRequest struct {
ID string `json:"id" validate:"required"`
}
// GetUserResponse 获取用户响应
type GetUserResponse struct {
User User `json:"user,omitempty"`
Err string `json:"err,omitempty"`
}
// UpdateUserRequest 更新用户请求
type UpdateUserRequest struct {
ID string `json:"id" validate:"required"`
Username *string `json:"username,omitempty" validate:"omitempty,min=3,max=20"`
Email *string `json:"email,omitempty" validate:"omitempty,email"`
}
// UpdateUserResponse 更新用户响应
type UpdateUserResponse struct {
Err string `json:"err,omitempty"`
}
// DeleteUserRequest 删除用户请求
type DeleteUserRequest struct {
ID string `json:"id" validate:"required"`
}
// DeleteUserResponse 删除用户响应
type DeleteUserResponse struct {
Err string `json:"err,omitempty"`
}
// ListUsersRequest 列表查询请求
type ListUsersRequest struct {
Offset int `json:"offset" validate:"min=0"`
Limit int `json:"limit" validate:"min=1,max=100"`
Filter string `json:"filter,omitempty"`
}
// ListUsersResponse 列表查询响应
type ListUsersResponse struct {
Users []User `json:"users,omitempty"`
Total int `json:"total"`
Err string `json:"err,omitempty"`
}
// User 用户模型
type User struct {
ID string `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
---
b.参数验证
a.功能说明
请求参数验证是Endpoint层的重要职责,确保传入的数据符合业务规则。可以使用validator库进行声明式验证,在结构体标签中定义验证规则。参数验证应该在调用Service方法之前进行,避免无效数据进入业务逻辑层。验证失败应该返回清晰的错误信息,帮助客户端快速定位问题。
b.代码示例
---
// 参数验证实现
package endpoint
import (
"context"
"fmt"
"github.com/go-playground/validator/v10"
"github.com/go-kit/kit/endpoint"
)
var validate = validator.New()
// ValidationMiddleware 参数验证中间件
func ValidationMiddleware() endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if err := validate.Struct(request); err != nil {
return nil, fmt.Errorf("validation failed: %w", err)
}
return next(ctx, request)
}
}
}
// MakeCreateUserEndpoint 带验证的Endpoint
func MakeCreateUserEndpoint(svc UserService) endpoint.Endpoint {
e := func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
// 自定义验证逻辑
if req.Username == "" {
return CreateUserResponse{Err: "username is required"}, nil
}
if len(req.Username) < 3 {
return CreateUserResponse{Err: "username must be at least 3 characters"}, nil
}
if req.Email == "" {
return CreateUserResponse{Err: "email is required"}, nil
}
user, err := svc.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err.Error()}, nil
}
return CreateUserResponse{User: user}, nil
}
// 应用验证中间件
return ValidationMiddleware()(e)
}
// ValidateRequest 通用验证函数
func ValidateRequest(request interface{}) error {
if err := validate.Struct(request); err != nil {
validationErrors := err.(validator.ValidationErrors)
for _, e := range validationErrors {
return fmt.Errorf("field %s failed validation: %s", e.Field(), e.Tag())
}
}
return nil
}
// 使用示例
func ExampleValidation() {
req := CreateUserRequest{
Username: "jo",
Email: "invalid-email",
}
if err := ValidateRequest(req); err != nil {
fmt.Println("Validation error:", err)
// Output: Validation error: field Username failed validation: min
}
}
---
03.端点组合
a.端点集合
a.功能说明
将服务的所有Endpoint组织为一个结构体,便于统一管理和传递。Endpoint集合作为服务的完整接口定义,包含所有业务操作对应的Endpoint。这种组织方式使得服务接口清晰明确,便于Transport层绑定和客户端调用。Endpoint集合还可以实现Service接口,使得Endpoint可以像Service一样使用,便于测试和Mock。
b.代码示例
---
// Endpoint集合实现
package endpoint
import (
"context"
"errors"
"github.com/go-kit/kit/endpoint"
)
// Endpoints 端点集合
type Endpoints struct {
CreateUserEndpoint endpoint.Endpoint
GetUserEndpoint endpoint.Endpoint
UpdateUserEndpoint endpoint.Endpoint
DeleteUserEndpoint endpoint.Endpoint
ListUsersEndpoint endpoint.Endpoint
}
// MakeEndpoints 创建端点集合
func MakeEndpoints(svc UserService) Endpoints {
return Endpoints{
CreateUserEndpoint: MakeCreateUserEndpoint(svc),
GetUserEndpoint: MakeGetUserEndpoint(svc),
UpdateUserEndpoint: MakeUpdateUserEndpoint(svc),
DeleteUserEndpoint: MakeDeleteUserEndpoint(svc),
ListUsersEndpoint: MakeListUsersEndpoint(svc),
}
}
// Endpoints实现UserService接口
func (e Endpoints) CreateUser(ctx context.Context, username, email string) (User, error) {
request := CreateUserRequest{
Username: username,
Email: email,
}
response, err := e.CreateUserEndpoint(ctx, request)
if err != nil {
return User{}, err
}
resp := response.(CreateUserResponse)
if resp.Err != "" {
return User{}, errors.New(resp.Err)
}
return resp.User, nil
}
func (e Endpoints) GetUser(ctx context.Context, id string) (User, error) {
request := GetUserRequest{ID: id}
response, err := e.GetUserEndpoint(ctx, request)
if err != nil {
return User{}, err
}
resp := response.(GetUserResponse)
if resp.Err != "" {
return User{}, errors.New(resp.Err)
}
return resp.User, nil
}
func (e Endpoints) UpdateUser(ctx context.Context, id string, updates UserUpdates) error {
request := UpdateUserRequest{
ID: id,
Username: updates.Username,
Email: updates.Email,
}
response, err := e.UpdateUserEndpoint(ctx, request)
if err != nil {
return err
}
resp := response.(UpdateUserResponse)
if resp.Err != "" {
return errors.New(resp.Err)
}
return nil
}
func (e Endpoints) DeleteUser(ctx context.Context, id string) error {
request := DeleteUserRequest{ID: id}
response, err := e.DeleteUserEndpoint(ctx, request)
if err != nil {
return err
}
resp := response.(DeleteUserResponse)
if resp.Err != "" {
return errors.New(resp.Err)
}
return nil
}
func (e Endpoints) ListUsers(ctx context.Context, offset, limit int) ([]User, error) {
request := ListUsersRequest{
Offset: offset,
Limit: limit,
}
response, err := e.ListUsersEndpoint(ctx, request)
if err != nil {
return nil, err
}
resp := response.(ListUsersResponse)
if resp.Err != "" {
return nil, errors.New(resp.Err)
}
return resp.Users, nil
}
---
b.端点复用
a.功能说明
通过Endpoint集合实现Service接口,可以将Endpoint当作Service使用,实现端点的复用。这种方式使得客户端可以像调用本地Service一样调用远程服务,隐藏了网络通信的细节。端点复用也便于构建服务代理和API网关,可以将多个服务的Endpoint组合为统一的接口。这种设计提高了代码的灵活性和可组合性。
b.代码示例
---
// 端点复用示例
package endpoint
import (
"context"
"io"
"time"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/sd"
"github.com/go-kit/kit/sd/lb"
)
// NewClientEndpoints 创建客户端端点集合
func NewClientEndpoints(instancer sd.Instancer) Endpoints {
// 端点工厂
factory := func(instance string) (endpoint.Endpoint, io.Closer, error) {
return makeRemoteEndpoint(instance), nil, nil
}
// 创建端点管理器
endpointer := sd.NewEndpointer(instancer, factory, logger)
// 负载均衡器
balancer := lb.NewRoundRobin(endpointer)
// 重试机制
retry := lb.Retry(3, 500*time.Millisecond, balancer)
return Endpoints{
CreateUserEndpoint: retry,
GetUserEndpoint: retry,
UpdateUserEndpoint: retry,
DeleteUserEndpoint: retry,
ListUsersEndpoint: retry,
}
}
// ProxyService 服务代理
type ProxyService struct {
createUserEndpoint endpoint.Endpoint
getUserEndpoint endpoint.Endpoint
}
func NewProxyService(endpoints Endpoints) UserService {
return &ProxyService{
createUserEndpoint: endpoints.CreateUserEndpoint,
getUserEndpoint: endpoints.GetUserEndpoint,
}
}
func (p *ProxyService) CreateUser(ctx context.Context, username, email string) (User, error) {
request := CreateUserRequest{Username: username, Email: email}
response, err := p.createUserEndpoint(ctx, request)
if err != nil {
return User{}, err
}
resp := response.(CreateUserResponse)
if resp.Err != "" {
return User{}, errors.New(resp.Err)
}
return resp.User, nil
}
func (p *ProxyService) GetUser(ctx context.Context, id string) (User, error) {
request := GetUserRequest{ID: id}
response, err := p.getUserEndpoint(ctx, request)
if err != nil {
return User{}, err
}
resp := response.(GetUserResponse)
if resp.Err != "" {
return User{}, errors.New(resp.Err)
}
return resp.User, nil
}
// 使用示例
func ExampleProxyService() {
// 服务发现
instancer := consul.NewInstancer(client, logger, "user-service", []string{}, true)
// 创建客户端端点
endpoints := NewClientEndpoints(instancer)
// 创建代理服务
proxySvc := NewProxyService(endpoints)
// 像调用本地服务一样使用
user, err := proxySvc.CreateUser(context.Background(), "john", "[email protected]")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created user: %+v\n", user)
}
---
3.2 中间件模式
01.中间件定义
a.函数签名
a.功能说明
Endpoint中间件是一个接收Endpoint并返回新Endpoint的函数,类型定义为Middleware func(Endpoint) Endpoint。中间件可以在原始Endpoint执行前后添加额外逻辑,如日志记录、性能监控、参数验证、错误处理等。中间件采用装饰器模式,可以层层包装Endpoint,形成处理链。每个中间件都是独立的、可复用的功能单元,可以灵活组合应用到不同的Endpoint上。中间件的执行顺序由包装顺序决定,最外层的中间件最先执行。
b.代码示例
---
// 中间件基础定义
package middleware
import (
"context"
"time"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
)
// Middleware 中间件类型定义
type Middleware func(endpoint.Endpoint) endpoint.Endpoint
// LoggingMiddleware 日志中间件
func LoggingMiddleware(logger log.Logger) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
logger.Log("msg", "calling endpoint", "request", request)
defer func(begin time.Time) {
logger.Log("msg", "called endpoint", "took", time.Since(begin))
}(time.Now())
return next(ctx, request)
}
}
}
// InstrumentingMiddleware 性能监控中间件
func InstrumentingMiddleware(duration metrics.Histogram) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
duration.With("success", "true").Observe(time.Since(begin).Seconds())
}(time.Now())
return next(ctx, request)
}
}
}
// AuthMiddleware 认证中间件
func AuthMiddleware(validator TokenValidator) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
token, ok := ctx.Value("token").(string)
if !ok || token == "" {
return nil, ErrUnauthorized
}
if err := validator.Validate(token); err != nil {
return nil, ErrInvalidToken
}
return next(ctx, request)
}
}
}
---
b.链式组合
a.功能说明
多个中间件可以链式组合,形成中间件链。中间件链按照添加顺序依次执行,每个中间件都可以决定是否继续调用下一个中间件。可以使用辅助函数简化中间件链的构建,如Chain函数接收多个中间件并返回组合后的中间件。中间件链的顺序很重要,通常将认证、限流等前置中间件放在最外层,日志、监控等后置中间件放在内层。合理的中间件顺序可以提高性能和安全性。
b.代码示例
---
// 中间件链组合
package middleware
import "github.com/go-kit/kit/endpoint"
// Chain 链式组合多个中间件
func Chain(outer Middleware, others ...Middleware) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
for i := len(others) - 1; i >= 0; i-- {
next = others[i](next)
}
return outer(next)
}
}
// ChainEndpoint 直接对Endpoint应用中间件链
func ChainEndpoint(e endpoint.Endpoint, middlewares ...Middleware) endpoint.Endpoint {
for i := len(middlewares) - 1; i >= 0; i-- {
e = middlewares[i](e)
}
return e
}
// 使用示例
func BuildEndpoint(svc UserService) endpoint.Endpoint {
var e endpoint.Endpoint
e = MakeCreateUserEndpoint(svc)
// 方式1:逐个应用中间件
e = LoggingMiddleware(logger)(e)
e = InstrumentingMiddleware(histogram)(e)
e = AuthMiddleware(validator)(e)
// 方式2:使用Chain函数
middleware := Chain(
AuthMiddleware(validator),
LoggingMiddleware(logger),
InstrumentingMiddleware(histogram),
)
e = middleware(e)
// 方式3:使用ChainEndpoint函数
e = ChainEndpoint(e,
AuthMiddleware(validator),
LoggingMiddleware(logger),
InstrumentingMiddleware(histogram),
)
return e
}
---
02.常用中间件
a.日志中间件
a.功能说明
日志中间件记录Endpoint的调用信息,包括请求参数、响应结果、执行时间、错误信息等。可以使用go-kit的log包实现结构化日志,支持多种日志格式和输出目标。日志中间件应该记录足够的信息用于问题排查,但避免记录敏感信息如密码、token等。可以根据日志级别控制日志详细程度,如DEBUG级别记录完整请求响应,INFO级别只记录关键信息。日志中间件通常放在中间件链的外层,确保所有请求都被记录。
b.代码示例
---
// 日志中间件实现
package middleware
import (
"context"
"fmt"
"time"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
)
// LoggingMiddleware 基础日志中间件
func LoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
defer func(begin time.Time) {
level.Info(logger).Log(
"request", fmt.Sprintf("%+v", request),
"response", fmt.Sprintf("%+v", response),
"err", err,
"took", time.Since(begin),
)
}(time.Now())
return next(ctx, request)
}
}
}
// DetailedLoggingMiddleware 详细日志中间件
func DetailedLoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
requestID := ctx.Value("request_id")
userID := ctx.Value("user_id")
level.Debug(logger).Log(
"msg", "endpoint called",
"request_id", requestID,
"user_id", userID,
"request", request,
)
defer func(begin time.Time) {
if err != nil {
level.Error(logger).Log(
"msg", "endpoint failed",
"request_id", requestID,
"err", err,
"took", time.Since(begin),
)
} else {
level.Info(logger).Log(
"msg", "endpoint succeeded",
"request_id", requestID,
"took", time.Since(begin),
)
}
}(time.Now())
return next(ctx, request)
}
}
}
---
b.监控中间件
a.功能说明
监控中间件收集Endpoint的性能指标,如请求数量、响应时间、错误率等。可以使用go-kit的metrics包集成Prometheus、StatsD等监控系统。监控中间件应该记录关键业务指标,如QPS、延迟分布、成功率等,用于性能分析和容量规划。可以为不同的Endpoint设置不同的监控标签,便于区分和聚合。监控数据应该定期导出到监控系统,避免内存占用过高。
b.代码示例
---
// 监控中间件实现
package middleware
import (
"context"
"time"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/metrics"
"github.com/prometheus/client_golang/prometheus"
)
// InstrumentingMiddleware 基础监控中间件
func InstrumentingMiddleware(duration metrics.Histogram, counter metrics.Counter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
duration.Observe(time.Since(begin).Seconds())
counter.Add(1)
}(time.Now())
return next(ctx, request)
}
}
}
// MetricsMiddleware 完整监控中间件
func MetricsMiddleware(
requestCount metrics.Counter,
requestDuration metrics.Histogram,
errorCount metrics.Counter,
) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
lvs := []string{"method", "endpoint"}
requestCount.With(lvs...).Add(1)
requestDuration.With(lvs...).Observe(time.Since(begin).Seconds())
}(time.Now())
response, err := next(ctx, request)
if err != nil {
errorCount.With("method", "endpoint").Add(1)
}
return response, err
}
}
}
// PrometheusMiddleware Prometheus监控中间件
func PrometheusMiddleware(namespace, subsystem, name string) endpoint.Middleware {
requestCount := prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: name + "_request_count",
Help: "Total request count",
},
[]string{"method", "status"},
)
requestDuration := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: name + "_request_duration_seconds",
Help: "Request duration in seconds",
},
[]string{"method"},
)
prometheus.MustRegister(requestCount, requestDuration)
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
requestDuration.WithLabelValues(name).Observe(time.Since(begin).Seconds())
}(time.Now())
response, err := next(ctx, request)
status := "success"
if err != nil {
status = "error"
}
requestCount.WithLabelValues(name, status).Inc()
return response, err
}
}
}
---
03.高级中间件
a.限流中间件
a.功能说明
限流中间件控制Endpoint的请求速率,防止系统过载。可以使用令牌桶、漏桶等算法实现限流,go-kit推荐使用golang.org/x/time/rate包。限流可以基于全局速率、用户速率、IP速率等不同维度。限流中间件应该返回明确的错误信息,告知客户端请求被限流。可以配置限流参数如每秒请求数、突发容量等,根据系统容量动态调整。限流中间件通常放在认证之后、业务逻辑之前。
b.代码示例
---
// 限流中间件实现
package middleware
import (
"context"
"errors"
"golang.org/x/time/rate"
"github.com/go-kit/kit/endpoint"
)
var ErrRateLimitExceeded = errors.New("rate limit exceeded")
// RateLimitMiddleware 基础限流中间件
func RateLimitMiddleware(limiter *rate.Limiter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if !limiter.Allow() {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
// TokenBucketMiddleware 令牌桶限流中间件
func TokenBucketMiddleware(qps int, burst int) endpoint.Middleware {
limiter := rate.NewLimiter(rate.Limit(qps), burst)
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if err := limiter.Wait(ctx); err != nil {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
// UserRateLimitMiddleware 用户级限流中间件
func UserRateLimitMiddleware(limiters map[string]*rate.Limiter, defaultQPS int) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
userID, ok := ctx.Value("user_id").(string)
if !ok {
return nil, errors.New("user_id not found")
}
limiter, exists := limiters[userID]
if !exists {
limiter = rate.NewLimiter(rate.Limit(defaultQPS), defaultQPS*2)
limiters[userID] = limiter
}
if !limiter.Allow() {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
---
b.熔断中间件
a.功能说明
熔断中间件在服务异常时自动切断请求,防止故障扩散。可以使用go-kit的circuitbreaker包集成hystrix、gobreaker等熔断器。熔断器有三种状态:关闭(正常)、开启(熔断)、半开(尝试恢复)。当错误率超过阈值时熔断器开启,拒绝所有请求;经过一段时间后进入半开状态,允许少量请求通过;如果请求成功则关闭熔断器,否则继续开启。熔断中间件应该配置合理的阈值和超时时间,避免误判和过度保护。
b.代码示例
---
// 熔断中间件实现
package middleware
import (
"context"
"time"
"github.com/go-kit/kit/endpoint"
"github.com/sony/gobreaker"
)
// CircuitBreakerMiddleware 熔断中间件
func CircuitBreakerMiddleware(cb *gobreaker.CircuitBreaker) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
response, err := cb.Execute(func() (interface{}, error) {
return next(ctx, request)
})
return response, err
}
}
}
// NewCircuitBreaker 创建熔断器
func NewCircuitBreaker(name string, maxRequests uint32, interval time.Duration, timeout time.Duration) *gobreaker.CircuitBreaker {
settings := gobreaker.Settings{
Name: name,
MaxRequests: maxRequests,
Interval: interval,
Timeout: timeout,
ReadyToTrip: func(counts gobreaker.Counts) bool {
failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
return counts.Requests >= 3 && failureRatio >= 0.6
},
OnStateChange: func(name string, from gobreaker.State, to gobreaker.State) {
log.Printf("CircuitBreaker %s: %s -> %s", name, from, to)
},
}
return gobreaker.NewCircuitBreaker(settings)
}
// 使用示例
func BuildEndpointWithCircuitBreaker(svc UserService) endpoint.Endpoint {
var e endpoint.Endpoint
e = MakeCreateUserEndpoint(svc)
cb := NewCircuitBreaker("CreateUser", 10, 10*time.Second, 60*time.Second)
e = CircuitBreakerMiddleware(cb)(e)
e = LoggingMiddleware(logger)(e)
return e
}
---
3.3 请求处理
01.请求解码
a.解码器设计
a.功能说明
请求解码器负责将传输层的原始数据转换为Endpoint可以处理的请求对象。每个Endpoint都需要一个对应的解码器,解码器的签名为DecodeRequestFunc func(context.Context, interface{}) (interface{}, error)。解码器接收传输层的原始请求(如*http.Request),返回业务请求对象。解码器应该处理数据格式转换、参数提取、基础验证等工作。不同的传输协议需要不同的解码器实现,如HTTP解码器从Body或URL中提取参数,gRPC解码器从Protobuf消息中提取。
b.代码示例
---
// HTTP请求解码器
package transport
import (
"context"
"encoding/json"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
// decodeCreateUserRequest HTTP POST请求解码
func decodeCreateUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req endpoint.CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
// decodeGetUserRequest 从URL路径提取参数
func decodeGetUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
vars := mux.Vars(r)
id := vars["id"]
if id == "" {
return nil, errors.New("missing user id")
}
return endpoint.GetUserRequest{ID: id}, nil
}
// decodeListUsersRequest 从查询参数提取
func decodeListUsersRequest(_ context.Context, r *http.Request) (interface{}, error) {
query := r.URL.Query()
offset, err := strconv.Atoi(query.Get("offset"))
if err != nil {
offset = 0
}
limit, err := strconv.Atoi(query.Get("limit"))
if err != nil {
limit = 10
}
return endpoint.ListUsersRequest{
Offset: offset,
Limit: limit,
Filter: query.Get("filter"),
}, nil
}
// decodeUpdateUserRequest 组合路径和Body参数
func decodeUpdateUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
vars := mux.Vars(r)
id := vars["id"]
var updates endpoint.UserUpdates
if err := json.NewDecoder(r.Body).Decode(&updates); err != nil {
return nil, err
}
return endpoint.UpdateUserRequest{
ID: id,
Updates: updates,
}, nil
}
---
b.错误处理
a.功能说明
解码器应该妥善处理各种错误情况,如JSON格式错误、参数缺失、类型转换失败等。错误应该返回清晰的错误信息,帮助客户端快速定位问题。可以定义自定义错误类型,携带错误码和详细信息。解码器的错误会被Transport层捕获,转换为协议特定的错误响应。对于可选参数,应该提供合理的默认值而不是返回错误。
b.代码示例
---
// 解码器错误处理
package transport
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
)
var (
ErrInvalidJSON = errors.New("invalid JSON format")
ErrMissingParam = errors.New("missing required parameter")
ErrInvalidParam = errors.New("invalid parameter value")
)
// DecodeError 解码错误类型
type DecodeError struct {
Field string
Message string
Err error
}
func (e DecodeError) Error() string {
return fmt.Sprintf("decode error in field %s: %s", e.Field, e.Message)
}
// decodeCreateUserRequestWithValidation 带验证的解码器
func decodeCreateUserRequestWithValidation(_ context.Context, r *http.Request) (interface{}, error) {
var req endpoint.CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, DecodeError{
Field: "body",
Message: "failed to parse JSON",
Err: err,
}
}
if req.Username == "" {
return nil, DecodeError{
Field: "username",
Message: "username is required",
Err: ErrMissingParam,
}
}
if len(req.Username) < 3 {
return nil, DecodeError{
Field: "username",
Message: "username must be at least 3 characters",
Err: ErrInvalidParam,
}
}
if req.Email == "" {
return nil, DecodeError{
Field: "email",
Message: "email is required",
Err: ErrMissingParam,
}
}
return req, nil
}
// SafeDecode 安全解码辅助函数
func SafeDecode(r *http.Request, v interface{}) error {
decoder := json.NewDecoder(r.Body)
decoder.DisallowUnknownFields()
if err := decoder.Decode(v); err != nil {
return fmt.Errorf("decode failed: %w", err)
}
return nil
}
---
02.响应编码
a.编码器设计
a.功能说明
响应编码器负责将Endpoint返回的响应对象转换为传输层的格式。编码器的签名为EncodeResponseFunc func(context.Context, http.ResponseWriter, interface{}) error。编码器接收响应对象,将其序列化为协议格式并写入输出流。编码器应该设置正确的Content-Type、状态码等HTTP头。对于不同的响应类型,可以实现不同的编码逻辑,如JSON、XML、Protobuf等。编码器也负责处理响应中的错误信息,将其转换为合适的HTTP状态码。
b.代码示例
---
// HTTP响应编码器
package transport
import (
"context"
"encoding/json"
"net/http"
)
// encodeResponse 标准JSON响应编码器
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
// encodeResponseWithStatus 带状态码的响应编码器
func encodeResponseWithStatus(status int) httptransport.EncodeResponseFunc {
return func(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(status)
return json.NewEncoder(w).Encode(response)
}
}
// encodeCreateUserResponse 创建用户响应编码器
func encodeCreateUserResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
resp := response.(endpoint.CreateUserResponse)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
if resp.Err != "" {
w.WriteHeader(http.StatusBadRequest)
} else {
w.WriteHeader(http.StatusCreated)
w.Header().Set("Location", "/users/"+resp.User.ID)
}
return json.NewEncoder(w).Encode(resp)
}
// encodeNoContent 无内容响应编码器
func encodeNoContent(_ context.Context, w http.ResponseWriter, _ interface{}) error {
w.WriteHeader(http.StatusNoContent)
return nil
}
// encodePrettyJSON 格式化JSON响应编码器
func encodePrettyJSON(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
return encoder.Encode(response)
}
---
b.错误编码
a.功能说明
错误编码器专门处理Endpoint返回的错误,将其转换为HTTP错误响应。错误编码器的签名为ErrorEncoder func(context.Context, error, http.ResponseWriter)。应该根据错误类型返回合适的HTTP状态码,如404 Not Found、400 Bad Request、500 Internal Server Error等。错误响应应该包含清晰的错误信息和错误码,便于客户端处理。可以定义错误映射表,将业务错误映射到HTTP状态码。敏感的错误信息不应该暴露给客户端。
b.代码示例
---
// 错误编码器实现
package transport
import (
"context"
"encoding/json"
"net/http"
)
// ErrorResponse 错误响应结构
type ErrorResponse struct {
Error string `json:"error"`
Code string `json:"code,omitempty"`
}
// encodeError 标准错误编码器
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
code := http.StatusInternalServerError
message := err.Error()
switch err {
case endpoint.ErrUserNotFound:
code = http.StatusNotFound
case endpoint.ErrInvalidInput:
code = http.StatusBadRequest
case endpoint.ErrUnauthorized:
code = http.StatusUnauthorized
case endpoint.ErrForbidden:
code = http.StatusForbidden
}
w.WriteHeader(code)
json.NewEncoder(w).Encode(ErrorResponse{
Error: message,
})
}
// encodeDetailedError 详细错误编码器
func encodeDetailedError(_ context.Context, err error, w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
var code int
var errCode string
if e, ok := err.(endpoint.ServiceError); ok {
errCode = e.Code
switch e.Code {
case "USER_NOT_FOUND":
code = http.StatusNotFound
case "INVALID_INPUT":
code = http.StatusBadRequest
case "UNAUTHORIZED":
code = http.StatusUnauthorized
default:
code = http.StatusInternalServerError
}
} else {
code = http.StatusInternalServerError
errCode = "INTERNAL_ERROR"
}
w.WriteHeader(code)
json.NewEncoder(w).Encode(ErrorResponse{
Error: err.Error(),
Code: errCode,
})
}
---
03.上下文处理
a.请求上下文
a.功能说明
Transport层负责从协议中提取元数据并注入Context,如HTTP Header、gRPC Metadata等。可以使用ServerBefore选项在请求处理前修改Context,添加请求ID、认证信息、追踪ID等。Context在整个请求处理链中传递,Endpoint和Service层都可以访问。应该使用类型安全的Context Key,避免键冲突。Context只应该存储请求范围的数据,不应该存储业务逻辑需要的参数。
b.代码示例
---
// 上下文处理实现
package transport
import (
"context"
"net/http"
"github.com/google/uuid"
)
type contextKey string
const (
ContextKeyRequestID contextKey = "request_id"
ContextKeyUserID contextKey = "user_id"
ContextKeyToken contextKey = "token"
)
// extractRequestID 提取或生成请求ID
func extractRequestID(ctx context.Context, r *http.Request) context.Context {
requestID := r.Header.Get("X-Request-ID")
if requestID == "" {
requestID = uuid.New().String()
}
return context.WithValue(ctx, ContextKeyRequestID, requestID)
}
// extractAuthToken 提取认证Token
func extractAuthToken(ctx context.Context, r *http.Request) context.Context {
token := r.Header.Get("Authorization")
if token != "" {
return context.WithValue(ctx, ContextKeyToken, token)
}
return ctx
}
// extractUserInfo 提取用户信息
func extractUserInfo(ctx context.Context, r *http.Request) context.Context {
token, ok := ctx.Value(ContextKeyToken).(string)
if !ok {
return ctx
}
userID := parseUserIDFromToken(token)
if userID != "" {
ctx = context.WithValue(ctx, ContextKeyUserID, userID)
}
return ctx
}
// MakeHTTPHandler 创建HTTP处理器
func MakeHTTPHandler(e endpoint.Endpoint) http.Handler {
return httptransport.NewServer(
e,
decodeRequest,
encodeResponse,
httptransport.ServerBefore(
extractRequestID,
extractAuthToken,
extractUserInfo,
),
httptransport.ServerAfter(setResponseHeaders),
httptransport.ServerErrorEncoder(encodeError),
)
}
---
b.响应上下文
a.功能说明
ServerAfter选项允许在响应发送前修改Context或设置响应头。可以用于添加追踪信息、设置CORS头、记录响应时间等。ServerAfter函数接收Context和ResponseWriter,可以修改响应头但不能修改响应体。多个ServerAfter函数按顺序执行,每个函数都可以修改Context并传递给下一个。响应上下文处理应该快速完成,避免阻塞响应发送。
b.代码示例
---
// 响应上下文处理
package transport
import (
"context"
"net/http"
"time"
)
// setResponseHeaders 设置响应头
func setResponseHeaders(ctx context.Context, w http.ResponseWriter) context.Context {
if requestID, ok := ctx.Value(ContextKeyRequestID).(string); ok {
w.Header().Set("X-Request-ID", requestID)
}
w.Header().Set("X-Service-Version", "1.0.0")
w.Header().Set("X-Response-Time", time.Now().Format(time.RFC3339))
return ctx
}
// setCORSHeaders 设置CORS头
func setCORSHeaders(ctx context.Context, w http.ResponseWriter) context.Context {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
return ctx
}
// setSecurityHeaders 设置安全头
func setSecurityHeaders(ctx context.Context, w http.ResponseWriter) context.Context {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("X-XSS-Protection", "1; mode=block")
return ctx
}
// logResponseTime 记录响应时间
func logResponseTime(logger log.Logger) httptransport.ServerResponseFunc {
return func(ctx context.Context, w http.ResponseWriter) context.Context {
if startTime, ok := ctx.Value("start_time").(time.Time); ok {
duration := time.Since(startTime)
logger.Log("response_time", duration.Milliseconds())
}
return ctx
}
}
---
3.4 客户端实现
01.客户端端点
a.端点创建
a.功能说明
客户端端点用于调用远程服务,将本地方法调用转换为网络请求。go-kit提供了httptransport.NewClient和grpctransport.NewClient用于创建客户端端点。客户端端点的创建需要指定服务地址、HTTP方法、请求编码器、响应解码器等参数。客户端端点返回标准的Endpoint类型,可以像本地Endpoint一样使用,也可以应用中间件。客户端端点封装了网络通信的细节,使得远程调用像本地调用一样简单。
b.代码示例
---
// HTTP客户端端点创建
package client
import (
"context"
"net/url"
"github.com/go-kit/kit/endpoint"
httptransport "github.com/go-kit/kit/transport/http"
)
// NewHTTPClient 创建HTTP客户端
func NewHTTPClient(instance string) (endpoint.Endpoints, error) {
if !strings.HasPrefix(instance, "http") {
instance = "http://" + instance
}
u, err := url.Parse(instance)
if err != nil {
return endpoint.Endpoints{}, err
}
// 创建用户端点
createUserEndpoint := httptransport.NewClient(
"POST",
copyURL(u, "/users"),
encodeCreateUserRequest,
decodeCreateUserResponse,
).Endpoint()
getUserEndpoint := httptransport.NewClient(
"GET",
copyURL(u, "/users"),
encodeGetUserRequest,
decodeGetUserResponse,
).Endpoint()
updateUserEndpoint := httptransport.NewClient(
"PUT",
copyURL(u, "/users"),
encodeUpdateUserRequest,
decodeUpdateUserResponse,
).Endpoint()
deleteUserEndpoint := httptransport.NewClient(
"DELETE",
copyURL(u, "/users"),
encodeDeleteUserRequest,
decodeDeleteUserResponse,
).Endpoint()
return endpoint.Endpoints{
CreateUserEndpoint: createUserEndpoint,
GetUserEndpoint: getUserEndpoint,
UpdateUserEndpoint: updateUserEndpoint,
DeleteUserEndpoint: deleteUserEndpoint,
}, nil
}
func copyURL(base *url.URL, path string) *url.URL {
next := *base
next.Path = path
return &next
}
---
b.请求编码
a.功能说明
客户端请求编码器将Endpoint的请求对象转换为HTTP请求。编码器的签名为EncodeRequestFunc func(context.Context, *http.Request, interface{}) error。编码器接收请求对象,将其序列化并写入HTTP请求的Body或URL。对于GET请求,通常将参数编码到URL路径或查询字符串中。对于POST/PUT请求,通常将参数编码为JSON写入Body。编码器还可以设置HTTP头,如Content-Type、Authorization等。
b.代码示例
---
// 客户端请求编码器
package client
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
)
// encodeCreateUserRequest POST请求编码
func encodeCreateUserRequest(_ context.Context, r *http.Request, request interface{}) error {
req := request.(endpoint.CreateUserRequest)
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(req); err != nil {
return err
}
r.Body = io.NopCloser(&buf)
r.Header.Set("Content-Type", "application/json")
return nil
}
// encodeGetUserRequest GET请求编码
func encodeGetUserRequest(_ context.Context, r *http.Request, request interface{}) error {
req := request.(endpoint.GetUserRequest)
r.URL.Path = r.URL.Path + "/" + req.ID
return nil
}
// encodeUpdateUserRequest PUT请求编码
func encodeUpdateUserRequest(_ context.Context, r *http.Request, request interface{}) error {
req := request.(endpoint.UpdateUserRequest)
r.URL.Path = r.URL.Path + "/" + req.ID
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(req.Updates); err != nil {
return err
}
r.Body = io.NopCloser(&buf)
r.Header.Set("Content-Type", "application/json")
return nil
}
// encodeDeleteUserRequest DELETE请求编码
func encodeDeleteUserRequest(_ context.Context, r *http.Request, request interface{}) error {
req := request.(endpoint.DeleteUserRequest)
r.URL.Path = r.URL.Path + "/" + req.ID
return nil
}
// encodeJSONRequest 通用JSON请求编码器
func encodeJSONRequest(_ context.Context, r *http.Request, request interface{}) error {
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(request); err != nil {
return err
}
r.Body = io.NopCloser(&buf)
r.Header.Set("Content-Type", "application/json")
return nil
}
---
02.响应处理
a.响应解码
a.功能说明
客户端响应解码器将HTTP响应转换为Endpoint的响应对象。解码器的签名为DecodeResponseFunc func(context.Context, *http.Response) (interface{}, error)。解码器接收HTTP响应,从Body中读取数据并反序列化为响应对象。解码器应该检查HTTP状态码,对于非2xx状态码应该返回错误。解码器还可以从响应头中提取元数据,如追踪ID、速率限制信息等。响应解码后应该关闭响应Body,避免资源泄漏。
b.代码示例
---
// 客户端响应解码器
package client
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
)
// decodeCreateUserResponse 创建用户响应解码
func decodeCreateUserResponse(_ context.Context, r *http.Response) (interface{}, error) {
if r.StatusCode != http.StatusCreated && r.StatusCode != http.StatusOK {
return nil, decodeError(r)
}
var resp endpoint.CreateUserResponse
if err := json.NewDecoder(r.Body).Decode(&resp); err != nil {
return nil, err
}
return resp, nil
}
// decodeGetUserResponse 获取用户响应解码
func decodeGetUserResponse(_ context.Context, r *http.Response) (interface{}, error) {
if r.StatusCode != http.StatusOK {
return nil, decodeError(r)
}
var resp endpoint.GetUserResponse
if err := json.NewDecoder(r.Body).Decode(&resp); err != nil {
return nil, err
}
return resp, nil
}
// decodeUpdateUserResponse 更新用户响应解码
func decodeUpdateUserResponse(_ context.Context, r *http.Response) (interface{}, error) {
if r.StatusCode != http.StatusOK && r.StatusCode != http.StatusNoContent {
return nil, decodeError(r)
}
var resp endpoint.UpdateUserResponse
if r.StatusCode == http.StatusNoContent {
return resp, nil
}
if err := json.NewDecoder(r.Body).Decode(&resp); err != nil {
return nil, err
}
return resp, nil
}
// decodeError 错误响应解码
func decodeError(r *http.Response) error {
defer r.Body.Close()
body, err := io.ReadAll(r.Body)
if err != nil {
return fmt.Errorf("HTTP %d: failed to read body", r.StatusCode)
}
var errResp ErrorResponse
if err := json.Unmarshal(body, &errResp); err != nil {
return fmt.Errorf("HTTP %d: %s", r.StatusCode, string(body))
}
return fmt.Errorf("HTTP %d: %s", r.StatusCode, errResp.Error)
}
// decodeJSONResponse 通用JSON响应解码器
func decodeJSONResponse(v interface{}) httptransport.DecodeResponseFunc {
return func(_ context.Context, r *http.Response) (interface{}, error) {
if r.StatusCode < 200 || r.StatusCode >= 300 {
return nil, decodeError(r)
}
if err := json.NewDecoder(r.Body).Decode(v); err != nil {
return nil, err
}
return v, nil
}
}
---
b.错误处理
a.功能说明
客户端应该妥善处理各种错误情况,包括网络错误、超时错误、服务端错误等。可以定义自定义错误类型,区分不同类型的错误。对于临时性错误,可以实现重试机制。对于不可恢复的错误,应该快速失败并返回明确的错误信息。客户端错误处理应该考虑熔断、降级等容错机制,避免故障扩散。错误信息应该包含足够的上下文,便于问题排查。
b.代码示例
---
// 客户端错误处理
package client
import (
"errors"
"fmt"
"net/http"
)
var (
ErrNetworkError = errors.New("network error")
ErrTimeout = errors.New("request timeout")
ErrServiceError = errors.New("service error")
ErrInvalidResponse = errors.New("invalid response")
)
// ClientError 客户端错误类型
type ClientError struct {
StatusCode int
Message string
Err error
}
func (e ClientError) Error() string {
return fmt.Sprintf("client error (status %d): %s", e.StatusCode, e.Message)
}
func (e ClientError) Unwrap() error {
return e.Err
}
// handleHTTPError 处理HTTP错误
func handleHTTPError(r *http.Response) error {
switch r.StatusCode {
case http.StatusBadRequest:
return ClientError{
StatusCode: r.StatusCode,
Message: "bad request",
Err: ErrInvalidResponse,
}
case http.StatusUnauthorized:
return ClientError{
StatusCode: r.StatusCode,
Message: "unauthorized",
Err: ErrServiceError,
}
case http.StatusNotFound:
return ClientError{
StatusCode: r.StatusCode,
Message: "not found",
Err: ErrServiceError,
}
case http.StatusInternalServerError:
return ClientError{
StatusCode: r.StatusCode,
Message: "internal server error",
Err: ErrServiceError,
}
default:
return ClientError{
StatusCode: r.StatusCode,
Message: fmt.Sprintf("unexpected status code: %d", r.StatusCode),
Err: ErrServiceError,
}
}
}
// IsRetryable 判断错误是否可重试
func IsRetryable(err error) bool {
var clientErr ClientError
if errors.As(err, &clientErr) {
return clientErr.StatusCode >= 500
}
if errors.Is(err, ErrTimeout) || errors.Is(err, ErrNetworkError) {
return true
}
return false
}
---
03.客户端配置
a.HTTP客户端
a.功能说明
可以配置HTTP客户端的各种参数,如超时时间、连接池大小、重试策略等。go-kit使用标准库的http.Client,可以自定义Transport配置连接池、TLS、代理等。合理的超时配置可以避免请求长时间阻塞,提高系统的响应性。连接池配置影响并发性能和资源占用,应该根据实际负载调整。可以使用ClientBefore和ClientAfter选项添加请求前后的处理逻辑。
b.代码示例
---
// HTTP客户端配置
package client
import (
"crypto/tls"
"net"
"net/http"
"time"
)
// NewHTTPClient 创建配置好的HTTP客户端
func NewHTTPClient(config ClientConfig) *http.Client {
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
if config.TLSConfig != nil {
transport.TLSClientConfig = config.TLSConfig
}
return &http.Client{
Transport: transport,
Timeout: config.Timeout,
}
}
// ClientConfig 客户端配置
type ClientConfig struct {
Timeout time.Duration
TLSConfig *tls.Config
}
// DefaultClientConfig 默认客户端配置
func DefaultClientConfig() ClientConfig {
return ClientConfig{
Timeout: 10 * time.Second,
}
}
// 使用示例
func ExampleHTTPClient() {
config := DefaultClientConfig()
httpClient := NewHTTPClient(config)
createUserEndpoint := httptransport.NewClient(
"POST",
targetURL,
encodeRequest,
decodeResponse,
httptransport.SetClient(httpClient),
httptransport.ClientBefore(setAuthHeader),
).Endpoint()
resp, err := createUserEndpoint(context.Background(), request)
if err != nil {
log.Fatal(err)
}
}
// setAuthHeader 设置认证头
func setAuthHeader(token string) httptransport.RequestFunc {
return func(ctx context.Context, r *http.Request) context.Context {
r.Header.Set("Authorization", "Bearer "+token)
return ctx
}
}
---
b.中间件应用
a.功能说明
客户端端点也可以应用中间件,实现重试、熔断、追踪、日志等功能。客户端中间件的使用方式与服务端相同,都是装饰器模式。常用的客户端中间件包括重试中间件、超时中间件、熔断中间件、追踪中间件等。中间件的顺序很重要,通常将重试放在最外层,熔断放在重试内层,追踪和日志放在最内层。合理使用客户端中间件可以提高系统的可靠性和可观测性。
b.代码示例
---
// 客户端中间件应用
package client
import (
"context"
"time"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/sd"
"github.com/go-kit/kit/sd/lb"
)
// BuildClientEndpoint 构建客户端端点
func BuildClientEndpoint(instance string) endpoint.Endpoint {
var e endpoint.Endpoint
// 创建基础端点
e = makeHTTPEndpoint(instance)
// 应用中间件
e = RetryMiddleware(3, 100*time.Millisecond)(e)
e = CircuitBreakerMiddleware(circuitBreaker)(e)
e = TimeoutMiddleware(5 * time.Second)(e)
e = LoggingMiddleware(logger)(e)
return e
}
// RetryMiddleware 重试中间件
func RetryMiddleware(max int, delay time.Duration) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
var resp interface{}
var err error
for i := 0; i <= max; i++ {
resp, err = next(ctx, request)
if err == nil {
return resp, nil
}
if !IsRetryable(err) {
return nil, err
}
if i < max {
time.Sleep(delay * time.Duration(i+1))
}
}
return nil, err
}
}
}
// TimeoutMiddleware 超时中间件
func TimeoutMiddleware(timeout time.Duration) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
type response struct {
resp interface{}
err error
}
ch := make(chan response, 1)
go func() {
resp, err := next(ctx, request)
ch <- response{resp, err}
}()
select {
case r := <-ch:
return r.resp, r.err
case <-ctx.Done():
return nil, ctx.Err()
}
}
}
}
---
3.5 测试实践
01.端点测试
a.单元测试
a.功能说明
Endpoint的单元测试应该独立于Transport层,直接测试Endpoint的输入输出。可以Mock Service层的依赖,专注于测试Endpoint的请求响应转换逻辑。单元测试应该覆盖正常情况和各种异常情况,如参数错误、Service错误等。使用表驱动测试可以提高测试覆盖率和可维护性。Endpoint测试不需要启动HTTP服务器,执行速度快,适合频繁运行。
b.代码示例
---
// Endpoint单元测试
package endpoint_test
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
// MockUserService Mock服务
type MockUserService struct {
mock.Mock
}
func (m *MockUserService) CreateUser(ctx context.Context, username, email string) (User, error) {
args := m.Called(ctx, username, email)
return args.Get(0).(User), args.Error(1)
}
func (m *MockUserService) GetUser(ctx context.Context, id string) (User, error) {
args := m.Called(ctx, id)
return args.Get(0).(User), args.Error(1)
}
// TestCreateUserEndpoint 测试创建用户端点
func TestCreateUserEndpoint(t *testing.T) {
tests := []struct {
name string
request CreateUserRequest
mockUser User
mockError error
expectedErr string
expectedUser User
}{
{
name: "success",
request: CreateUserRequest{
Username: "john",
Email: "[email protected]",
},
mockUser: User{
ID: "123",
Username: "john",
Email: "[email protected]",
},
mockError: nil,
expectedErr: "",
expectedUser: User{ID: "123", Username: "john"},
},
{
name: "service error",
request: CreateUserRequest{
Username: "john",
Email: "[email protected]",
},
mockUser: User{},
mockError: errors.New("database error"),
expectedErr: "database error",
expectedUser: User{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockSvc := new(MockUserService)
mockSvc.On("CreateUser", mock.Anything, tt.request.Username, tt.request.Email).
Return(tt.mockUser, tt.mockError)
endpoint := MakeCreateUserEndpoint(mockSvc)
resp, err := endpoint(context.Background(), tt.request)
if tt.expectedErr != "" {
assert.Nil(t, err)
response := resp.(CreateUserResponse)
assert.Equal(t, tt.expectedErr, response.Err)
} else {
assert.NoError(t, err)
response := resp.(CreateUserResponse)
assert.Equal(t, tt.expectedUser.ID, response.User.ID)
}
mockSvc.AssertExpectations(t)
})
}
}
---
b.集成测试
a.功能说明
集成测试验证Endpoint与Transport层的集成,测试完整的HTTP请求响应流程。需要启动测试HTTP服务器,使用httptest包创建测试服务器。集成测试应该测试请求解码、Endpoint调用、响应编码的完整链路。可以测试HTTP头、状态码、响应格式等Transport层的细节。集成测试比单元测试慢,但能发现层间集成的问题。
b.代码示例
---
// Endpoint集成测试
package transport_test
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCreateUserHTTP(t *testing.T) {
svc := NewUserService(db, cache)
endpoints := MakeEndpoints(svc)
handler := MakeHTTPHandler(endpoints)
tests := []struct {
name string
method string
path string
body interface{}
expectedStatus int
expectedBody map[string]interface{}
}{
{
name: "create user success",
method: "POST",
path: "/users",
body: map[string]string{
"username": "john",
"email": "[email protected]",
},
expectedStatus: http.StatusCreated,
expectedBody: map[string]interface{}{
"user": map[string]interface{}{
"username": "john",
"email": "[email protected]",
},
},
},
{
name: "create user invalid input",
method: "POST",
path: "/users",
body: map[string]string{
"username": "",
"email": "invalid",
},
expectedStatus: http.StatusBadRequest,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
body, _ := json.Marshal(tt.body)
req := httptest.NewRequest(tt.method, tt.path, bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
assert.Equal(t, tt.expectedStatus, w.Code)
if tt.expectedBody != nil {
var resp map[string]interface{}
json.NewDecoder(w.Body).Decode(&resp)
assert.Equal(t, tt.expectedBody["user"], resp["user"])
}
})
}
}
---
02.中间件测试
a.功能测试
a.功能说明
中间件测试验证中间件的功能是否正确,如日志是否记录、指标是否收集、限流是否生效等。可以Mock Endpoint,专注于测试中间件的逻辑。中间件测试应该验证中间件对请求响应的影响,如是否修改了Context、是否返回了错误等。使用Spy模式可以验证中间件是否调用了依赖的组件,如Logger、Metrics等。中间件测试应该覆盖各种边界情况。
b.代码示例
---
// 中间件功能测试
package middleware_test
import (
"context"
"errors"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestLoggingMiddleware(t *testing.T) {
var loggedMessages []string
logger := &MockLogger{
LogFunc: func(keyvals ...interface{}) error {
loggedMessages = append(loggedMessages, keyvals[1].(string))
return nil
},
}
endpoint := func(ctx context.Context, request interface{}) (interface{}, error) {
return "response", nil
}
middleware := LoggingMiddleware(logger)
wrappedEndpoint := middleware(endpoint)
resp, err := wrappedEndpoint(context.Background(), "request")
assert.NoError(t, err)
assert.Equal(t, "response", resp)
assert.Contains(t, loggedMessages, "calling endpoint")
assert.Contains(t, loggedMessages, "called endpoint")
}
func TestRateLimitMiddleware(t *testing.T) {
limiter := rate.NewLimiter(rate.Limit(1), 1)
endpoint := func(ctx context.Context, request interface{}) (interface{}, error) {
return "response", nil
}
middleware := RateLimitMiddleware(limiter)
wrappedEndpoint := middleware(endpoint)
// 第一次请求应该成功
resp, err := wrappedEndpoint(context.Background(), "request")
assert.NoError(t, err)
assert.Equal(t, "response", resp)
// 第二次请求应该被限流
resp, err = wrappedEndpoint(context.Background(), "request")
assert.Error(t, err)
assert.Equal(t, ErrRateLimitExceeded, err)
}
func TestCircuitBreakerMiddleware(t *testing.T) {
cb := NewCircuitBreaker("test", 1, time.Second, time.Second)
failingEndpoint := func(ctx context.Context, request interface{}) (interface{}, error) {
return nil, errors.New("service error")
}
middleware := CircuitBreakerMiddleware(cb)
wrappedEndpoint := middleware(failingEndpoint)
// 触发熔断
for i := 0; i < 5; i++ {
wrappedEndpoint(context.Background(), "request")
}
// 熔断器应该开启
_, err := wrappedEndpoint(context.Background(), "request")
assert.Error(t, err)
assert.Contains(t, err.Error(), "circuit breaker is open")
}
---
b.性能测试
a.功能说明
性能测试评估中间件对系统性能的影响,测量中间件的开销。使用Go的benchmark测试框架进行性能测试,测量每次操作的耗时和内存分配。性能测试应该比较有无中间件的性能差异,评估中间件的成本。对于性能敏感的系统,应该优化中间件的实现,减少不必要的开销。性能测试结果可以指导中间件的选择和配置。
b.代码示例
---
// 中间件性能测试
package middleware_test
import (
"context"
"testing"
)
func BenchmarkEndpointWithoutMiddleware(b *testing.B) {
endpoint := func(ctx context.Context, request interface{}) (interface{}, error) {
return "response", nil
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
endpoint(context.Background(), "request")
}
}
func BenchmarkEndpointWithLogging(b *testing.B) {
endpoint := func(ctx context.Context, request interface{}) (interface{}, error) {
return "response", nil
}
logger := NewNopLogger()
middleware := LoggingMiddleware(logger)
wrappedEndpoint := middleware(endpoint)
b.ResetTimer()
for i := 0; i < b.N; i++ {
wrappedEndpoint(context.Background(), "request")
}
}
func BenchmarkEndpointWithMultipleMiddlewares(b *testing.B) {
endpoint := func(ctx context.Context, request interface{}) (interface{}, error) {
return "response", nil
}
logger := NewNopLogger()
metrics := NewNopMetrics()
limiter := rate.NewLimiter(rate.Inf, 1)
wrappedEndpoint := endpoint
wrappedEndpoint = LoggingMiddleware(logger)(wrappedEndpoint)
wrappedEndpoint = InstrumentingMiddleware(metrics)(wrappedEndpoint)
wrappedEndpoint = RateLimitMiddleware(limiter)(wrappedEndpoint)
b.ResetTimer()
for i := 0; i < b.N; i++ {
wrappedEndpoint(context.Background(), "request")
}
}
func BenchmarkMiddlewareAllocation(b *testing.B) {
endpoint := func(ctx context.Context, request interface{}) (interface{}, error) {
return "response", nil
}
logger := NewNopLogger()
middleware := LoggingMiddleware(logger)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
wrappedEndpoint := middleware(endpoint)
wrappedEndpoint(context.Background(), "request")
}
}
---
03.Mock和Stub
a.Service Mock
a.功能说明
Mock Service用于隔离Endpoint测试,避免依赖真实的Service实现。可以使用testify/mock或gomock等Mock框架生成Mock对象。Mock对象可以预设返回值和行为,验证方法调用次数和参数。使用Mock可以测试各种边界情况和错误场景,无需准备复杂的测试数据。Mock应该只模拟必要的方法,保持测试的简洁性。
b.代码示例
---
// Service Mock实现
package endpoint_test
import (
"context"
"github.com/stretchr/testify/mock"
)
// MockUserService Mock用户服务
type MockUserService struct {
mock.Mock
}
func (m *MockUserService) CreateUser(ctx context.Context, username, email string) (User, error) {
args := m.Called(ctx, username, email)
if args.Get(0) == nil {
return User{}, args.Error(1)
}
return args.Get(0).(User), args.Error(1)
}
func (m *MockUserService) GetUser(ctx context.Context, id string) (User, error) {
args := m.Called(ctx, id)
return args.Get(0).(User), args.Error(1)
}
func (m *MockUserService) UpdateUser(ctx context.Context, id string, updates UserUpdates) error {
args := m.Called(ctx, id, updates)
return args.Error(0)
}
func (m *MockUserService) DeleteUser(ctx context.Context, id string) error {
args := m.Called(ctx, id)
return args.Error(0)
}
// 使用示例
func TestEndpointWithMock(t *testing.T) {
mockSvc := new(MockUserService)
// 设置期望
mockSvc.On("CreateUser", mock.Anything, "john", "[email protected]").
Return(User{ID: "123", Username: "john"}, nil)
endpoint := MakeCreateUserEndpoint(mockSvc)
resp, err := endpoint(context.Background(), CreateUserRequest{
Username: "john",
Email: "[email protected]",
})
assert.NoError(t, err)
response := resp.(CreateUserResponse)
assert.Equal(t, "123", response.User.ID)
// 验证调用
mockSvc.AssertExpectations(t)
mockSvc.AssertCalled(t, "CreateUser", mock.Anything, "john", "[email protected]")
}
---
b.Transport Stub
a.功能说明
Transport Stub用于测试客户端Endpoint,模拟HTTP服务器的响应。使用httptest包创建测试服务器,返回预设的响应。Stub可以模拟各种HTTP状态码、响应头、响应体,测试客户端的错误处理。使用Stub可以测试网络错误、超时等异常情况,无需依赖真实的服务器。Stub应该尽可能简单,只返回测试所需的最小响应。
b.代码示例
---
// Transport Stub实现
package client_test
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
)
// StubServer 创建测试服务器
func StubServer(t *testing.T, statusCode int, response interface{}) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(statusCode)
json.NewEncoder(w).Encode(response)
}))
}
// TestClientEndpoint 测试客户端端点
func TestClientEndpoint(t *testing.T) {
// 创建Stub服务器
server := StubServer(t, http.StatusOK, CreateUserResponse{
User: User{ID: "123", Username: "john"},
})
defer server.Close()
// 创建客户端
client, err := NewHTTPClient(server.URL)
assert.NoError(t, err)
// 调用端点
resp, err := client.CreateUserEndpoint(context.Background(), CreateUserRequest{
Username: "john",
Email: "[email protected]",
})
assert.NoError(t, err)
response := resp.(CreateUserResponse)
assert.Equal(t, "123", response.User.ID)
}
// TestClientError 测试客户端错误处理
func TestClientError(t *testing.T) {
server := StubServer(t, http.StatusInternalServerError, ErrorResponse{
Error: "internal server error",
})
defer server.Close()
client, err := NewHTTPClient(server.URL)
assert.NoError(t, err)
resp, err := client.CreateUserEndpoint(context.Background(), CreateUserRequest{
Username: "john",
Email: "[email protected]",
})
assert.Error(t, err)
assert.Contains(t, err.Error(), "500")
}
---
3.6 最佳实践
01.设计原则
a.单一职责
a.功能说明
Endpoint层应该只负责请求响应的转换,不包含业务逻辑。业务逻辑应该放在Service层,Endpoint只是Service的薄包装。这种分离使得业务逻辑可以独立测试,不依赖于传输协议。Endpoint应该保持简洁,避免复杂的条件判断和数据处理。如果Endpoint变得复杂,应该考虑将逻辑下沉到Service层或上移到中间件。单一职责原则使得代码更易于理解和维护。
b.代码示例
---
// 单一职责示例
package endpoint
import "context"
// 错误示例:Endpoint包含业务逻辑
func MakeCreateUserEndpointBad(db Database) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
// 不应该在Endpoint中直接操作数据库
if exists, _ := db.UserExists(ctx, req.Username); exists {
return CreateUserResponse{Err: "user exists"}, nil
}
user := User{
ID: generateID(),
Username: req.Username,
Email: req.Email,
}
if err := db.SaveUser(ctx, user); err != nil {
return CreateUserResponse{Err: err.Error()}, nil
}
return CreateUserResponse{User: user}, nil
}
}
// 正确示例:Endpoint只做转换
func MakeCreateUserEndpointGood(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
// 只调用Service,不包含业务逻辑
user, err := svc.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err.Error()}, nil
}
return CreateUserResponse{User: user}, nil
}
}
// Service层包含业务逻辑
func (s *userService) CreateUser(ctx context.Context, username, email string) (User, error) {
if exists, _ := s.db.UserExists(ctx, username); exists {
return User{}, errors.New("user exists")
}
user := User{
ID: generateID(),
Username: username,
Email: email,
}
if err := s.db.SaveUser(ctx, user); err != nil {
return User{}, err
}
return user, nil
}
---
b.接口隔离
a.功能说明
Endpoint应该依赖于Service接口而非具体实现,遵循依赖倒置原则。Service接口应该定义清晰的方法签名,每个方法对应一个业务操作。接口应该小而专注,避免定义过大的接口。使用接口可以方便地Mock Service进行测试,也便于替换不同的Service实现。接口隔离使得Endpoint与Service的耦合度降低,提高了代码的灵活性。
b.代码示例
---
// 接口隔离示例
package endpoint
import "context"
// 错误示例:依赖具体实现
type ConcreteUserService struct {
db *sql.DB
cache *redis.Client
}
func MakeEndpointBad(svc *ConcreteUserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
// 依赖具体实现,难以测试和替换
return svc.CreateUser(ctx, request)
}
}
// 正确示例:依赖接口
type UserService interface {
CreateUser(ctx context.Context, username, email string) (User, error)
GetUser(ctx context.Context, id string) (User, error)
UpdateUser(ctx context.Context, id string, updates UserUpdates) error
DeleteUser(ctx context.Context, id string) error
}
func MakeEndpointGood(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := svc.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err.Error()}, nil
}
return CreateUserResponse{User: user}, nil
}
}
// 可以轻松Mock进行测试
type MockUserService struct {
CreateUserFunc func(ctx context.Context, username, email string) (User, error)
}
func (m *MockUserService) CreateUser(ctx context.Context, username, email string) (User, error) {
return m.CreateUserFunc(ctx, username, email)
}
---
02.性能优化
a.减少分配
a.功能说明
Endpoint处理请求时应该尽量减少内存分配,避免频繁的GC。可以复用对象,使用对象池减少分配。避免在热路径上创建不必要的临时对象,如字符串拼接、切片扩容等。使用指针传递大对象,避免值拷贝。性能优化应该基于性能分析的结果,不要过早优化。使用pprof等工具分析内存分配热点,针对性地优化。
b.代码示例
---
// 性能优化示例
package endpoint
import (
"context"
"sync"
)
// 使用对象池减少分配
var requestPool = sync.Pool{
New: func() interface{} {
return &CreateUserRequest{}
},
}
var responsePool = sync.Pool{
New: func() interface{} {
return &CreateUserResponse{}
},
}
// 优化的Endpoint实现
func MakeOptimizedEndpoint(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := svc.CreateUser(ctx, req.Username, req.Email)
// 从对象池获取响应对象
resp := responsePool.Get().(*CreateUserResponse)
defer responsePool.Put(resp)
if err != nil {
resp.Err = err.Error()
resp.User = User{}
} else {
resp.User = user
resp.Err = ""
}
return *resp, nil
}
}
// 避免不必要的字符串拼接
func BuildErrorMessageBad(field, message string) string {
return "field " + field + ": " + message // 多次分配
}
func BuildErrorMessageGood(field, message string) string {
var buf strings.Builder
buf.Grow(len("field ") + len(field) + len(": ") + len(message))
buf.WriteString("field ")
buf.WriteString(field)
buf.WriteString(": ")
buf.WriteString(message)
return buf.String() // 一次分配
}
// 使用指针传递大对象
func ProcessLargeRequestBad(req LargeRequest) Response {
// 值拷贝,如果LargeRequest很大会影响性能
return processRequest(req)
}
func ProcessLargeRequestGood(req *LargeRequest) Response {
// 指针传递,避免拷贝
return processRequest(req)
}
---
b.并发控制
a.功能说明
Endpoint应该是并发安全的,可以被多个goroutine同时调用。避免在Endpoint中使用共享状态,如全局变量、包级变量等。如果必须使用共享状态,应该使用互斥锁或其他同步机制保护。使用Context传递请求范围的数据,避免使用全局状态。并发控制应该在中间件层实现,如限流、熔断等,Endpoint本身应该保持无状态。
b.代码示例
---
// 并发控制示例
package endpoint
import (
"context"
"sync"
"sync/atomic"
)
// 错误示例:使用共享状态
var requestCount int // 不安全
func MakeEndpointBad(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
requestCount++ // 并发不安全
return svc.CreateUser(ctx, request)
}
}
// 正确示例:使用原子操作
var requestCountAtomic int64
func MakeEndpointGood(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
atomic.AddInt64(&requestCountAtomic, 1) // 并发安全
return svc.CreateUser(ctx, request)
}
}
// 使用互斥锁保护共享状态
type EndpointMetrics struct {
mu sync.RWMutex
requestCount map[string]int
}
func (m *EndpointMetrics) Increment(endpoint string) {
m.mu.Lock()
defer m.mu.Unlock()
m.requestCount[endpoint]++
}
func (m *EndpointMetrics) Get(endpoint string) int {
m.mu.RLock()
defer m.mu.RUnlock()
return m.requestCount[endpoint]
}
// 使用Context传递请求数据
func MakeEndpointWithContext(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
// 从Context获取请求ID,而不是使用全局变量
requestID := ctx.Value("request_id").(string)
req := request.(CreateUserRequest)
user, err := svc.CreateUser(ctx, req.Username, req.Email)
logger.Log("request_id", requestID, "user", user.ID)
if err != nil {
return CreateUserResponse{Err: err.Error()}, nil
}
return CreateUserResponse{User: user}, nil
}
}
---
03.错误处理
a.错误分类
a.功能说明
应该区分不同类型的错误,如业务错误、系统错误、网络错误等。业务错误应该返回给客户端,系统错误应该记录日志但不暴露细节。可以定义自定义错误类型,携���错误码、错误消息、堆栈信息等。使用errors.Is和errors.As判断错误类型,而不是字符串比较。错误处理应该一致,避免在不同地方使用不同的错误处理方式。
b.代码示例
---
// 错误分类示例
package endpoint
import (
"errors"
"fmt"
)
// 定义错误类型
type ErrorType int
const (
ErrorTypeUnknown ErrorType = iota
ErrorTypeBusiness
ErrorTypeSystem
ErrorTypeNetwork
)
// 自定义错误
type ServiceError struct {
Type ErrorType
Code string
Message string
Err error
}
func (e *ServiceError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%s: %v", e.Message, e.Err)
}
return e.Message
}
func (e *ServiceError) Unwrap() error {
return e.Err
}
// 预定义错误
var (
ErrUserNotFound = &ServiceError{
Type: ErrorTypeBusiness,
Code: "USER_NOT_FOUND",
Message: "user not found",
}
ErrInvalidInput = &ServiceError{
Type: ErrorTypeBusiness,
Code: "INVALID_INPUT",
Message: "invalid input",
}
ErrDatabaseError = &ServiceError{
Type: ErrorTypeSystem,
Code: "DATABASE_ERROR",
Message: "database error",
}
)
// Endpoint错误处理
func MakeEndpointWithErrorHandling(svc UserService) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := svc.CreateUser(ctx, req.Username, req.Email)
if err != nil {
// 判断错误类型
var svcErr *ServiceError
if errors.As(err, &svcErr) {
switch svcErr.Type {
case ErrorTypeBusiness:
// 业务错误返回给客户端
return CreateUserResponse{Err: svcErr.Message}, nil
case ErrorTypeSystem:
// 系统错误记录日志,返回通用错误
logger.Log("error", err)
return CreateUserResponse{Err: "internal server error"}, nil
}
}
// 未知错误
logger.Log("error", err)
return CreateUserResponse{Err: "unknown error"}, nil
}
return CreateUserResponse{User: user}, nil
}
}
---
b.错误恢复
a.功能说明
Endpoint应该能够从错误中恢复,避免单个请求的错误影响整个服务。可以使用recover捕获panic,记录日志并返回错误响应。错误恢复应该在中间件层实现,Endpoint本身应该避免panic。对于可恢复的错误,应该尝试重试或降级处理。错误恢复机制应该记录详细的错误信息,便于问题排查。
b.代码示例
---
// 错误恢复示例
package middleware
import (
"context"
"fmt"
"runtime/debug"
)
// RecoveryMiddleware 错误恢复中间件
func RecoveryMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
defer func() {
if r := recover(); r != nil {
// 记录panic信息和堆栈
logger.Log(
"panic", r,
"stack", string(debug.Stack()),
)
// 返回错误响应
err = fmt.Errorf("panic recovered: %v", r)
response = nil
}
}()
return next(ctx, request)
}
}
}
// RetryMiddleware 重试中间件
func RetryMiddleware(maxRetries int, shouldRetry func(error) bool) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
var lastErr error
for i := 0; i <= maxRetries; i++ {
resp, err := next(ctx, request)
if err == nil {
return resp, nil
}
lastErr = err
// 判断是否应该重试
if !shouldRetry(err) {
return nil, err
}
// 最后一次不等待
if i < maxRetries {
select {
case <-time.After(time.Duration(i+1) * 100 * time.Millisecond):
case <-ctx.Done():
return nil, ctx.Err()
}
}
}
return nil, fmt.Errorf("max retries exceeded: %w", lastErr)
}
}
}
// FallbackMiddleware 降级中间件
func FallbackMiddleware(fallback endpoint.Endpoint) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
resp, err := next(ctx, request)
if err != nil {
// 主端点失败,使用降级端点
logger.Log("msg", "using fallback", "error", err)
return fallback(ctx, request)
}
return resp, nil
}
}
}
---
4 传输层
4.1 HTTP传输
01.HTTP服务器
a.路由配置
a.功能说明
HTTP传输层使用httptransport.NewServer创建HTTP处理器,配置路由将URL映射到Endpoint。支持RESTful风格的路由设计,使用HTTP方法区分操作类型。可以使用gorilla/mux等路由库实现复杂的路由规则,支持路径参数、查询参数、正则匹配等。路由配置应该清晰明确,遵循REST规范,便于API管理和文档生成。合理的路由设计可以提高API的可读性和可维护性。
b.代码示例
---
// HTTP路由配置
package transport
import (
"net/http"
"github.com/gorilla/mux"
httptransport "github.com/go-kit/kit/transport/http"
)
// MakeHTTPHandler 创建HTTP处理器
func MakeHTTPHandler(endpoints Endpoints, logger log.Logger) http.Handler {
r := mux.NewRouter()
options := []httptransport.ServerOption{
httptransport.ServerErrorEncoder(encodeError),
httptransport.ServerBefore(extractRequestID),
}
// 用户相关路由
r.Methods("POST").Path("/users").Handler(httptransport.NewServer(
endpoints.CreateUser,
decodeCreateUserRequest,
encodeResponse,
options...,
))
r.Methods("GET").Path("/users/{id}").Handler(httptransport.NewServer(
endpoints.GetUser,
decodeGetUserRequest,
encodeResponse,
options...,
))
r.Methods("PUT").Path("/users/{id}").Handler(httptransport.NewServer(
endpoints.UpdateUser,
decodeUpdateUserRequest,
encodeResponse,
options...,
))
r.Methods("DELETE").Path("/users/{id}").Handler(httptransport.NewServer(
endpoints.DeleteUser,
decodeDeleteUserRequest,
encodeNoContent,
options...,
))
r.Methods("GET").Path("/users").Handler(httptransport.NewServer(
endpoints.ListUsers,
decodeListUsersRequest,
encodeResponse,
options...,
))
// 健康检查
r.Methods("GET").Path("/health").HandlerFunc(healthCheckHandler)
return r
}
func healthCheckHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
}
---
b.编解码器
a.功能说明
编解码器负责HTTP请求响应的序列化和反序列化,支持JSON、XML、Protobuf等多种格式。解码器从HTTP请求中提取数据转换为请求对象,包括Body、URL参数、Header等。编码器将响应对象转换为HTTP响应,设置正确的Content-Type和状态码。可以自定义编解码器实现特殊格式支持。编解码器与业务逻辑分离,便于格式切换和测试。编解码器应该处理各种异常情况,返回清晰的错误信息。
b.代码示例
---
// HTTP编解码器
package transport
import (
"context"
"encoding/json"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
// decodeCreateUserRequest 解码创建用户请求
func decodeCreateUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req endpoint.CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
// decodeGetUserRequest 解码获取用户请求
func decodeGetUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
vars := mux.Vars(r)
id, ok := vars["id"]
if !ok {
return nil, errors.New("missing user id")
}
return endpoint.GetUserRequest{ID: id}, nil
}
// decodeListUsersRequest 解码列表请求
func decodeListUsersRequest(_ context.Context, r *http.Request) (interface{}, error) {
query := r.URL.Query()
offset, _ := strconv.Atoi(query.Get("offset"))
limit, _ := strconv.Atoi(query.Get("limit"))
if limit == 0 {
limit = 10
}
return endpoint.ListUsersRequest{
Offset: offset,
Limit: limit,
Filter: query.Get("filter"),
}, nil
}
// encodeResponse 标准JSON响应编码
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
// encodeNoContent 无内容响应编码
func encodeNoContent(_ context.Context, w http.ResponseWriter, _ interface{}) error {
w.WriteHeader(http.StatusNoContent)
return nil
}
// encodeError 错误响应编码
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
code := http.StatusInternalServerError
switch err {
case endpoint.ErrNotFound:
code = http.StatusNotFound
case endpoint.ErrInvalidInput:
code = http.StatusBadRequest
}
w.WriteHeader(code)
json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
}
---
02.服务器选项
a.ServerBefore
a.功能说明
ServerBefore在请求处理前执行,用于从HTTP请求中提取元数据并注入Context。可以提取请求ID、认证信息、追踪ID等,传递给Endpoint和Service层。多个ServerBefore函数按顺序执行,每个函数都可以修改Context。ServerBefore是实现横切关注点的重要机制,如认证、日志、追踪等。应该保持ServerBefore函数的轻量级,避免执行耗时操作。
b.代码示例
---
// ServerBefore选项
package transport
import (
"context"
"net/http"
"github.com/google/uuid"
)
type contextKey string
const (
ContextKeyRequestID contextKey = "request_id"
ContextKeyUserID contextKey = "user_id"
ContextKeyToken contextKey = "token"
)
// extractRequestID 提取或生成请求ID
func extractRequestID(ctx context.Context, r *http.Request) context.Context {
requestID := r.Header.Get("X-Request-ID")
if requestID == "" {
requestID = uuid.New().String()
}
return context.WithValue(ctx, ContextKeyRequestID, requestID)
}
// extractAuthToken 提取认证Token
func extractAuthToken(ctx context.Context, r *http.Request) context.Context {
auth := r.Header.Get("Authorization")
if auth != "" {
// 移除Bearer前缀
if len(auth) > 7 && auth[:7] == "Bearer " {
token := auth[7:]
return context.WithValue(ctx, ContextKeyToken, token)
}
}
return ctx
}
// extractUserID 从Token提取用户ID
func extractUserID(validator TokenValidator) httptransport.RequestFunc {
return func(ctx context.Context, r *http.Request) context.Context {
token, ok := ctx.Value(ContextKeyToken).(string)
if !ok || token == "" {
return ctx
}
claims, err := validator.ValidateToken(token)
if err != nil {
return ctx
}
if userID, ok := claims["user_id"].(string); ok {
return context.WithValue(ctx, ContextKeyUserID, userID)
}
return ctx
}
}
// extractClientIP 提取客户端IP
func extractClientIP(ctx context.Context, r *http.Request) context.Context {
ip := r.Header.Get("X-Forwarded-For")
if ip == "" {
ip = r.Header.Get("X-Real-IP")
}
if ip == "" {
ip = r.RemoteAddr
}
return context.WithValue(ctx, "client_ip", ip)
}
---
b.ServerAfter
a.功能说明
ServerAfter在响应发送前执行,用于设置响应头、记录日志、追踪等。可以从Context中提取信息并写入响应头,如请求ID、服务版本等。多个ServerAfter函数按顺序执行,每个函数都可以修改响应头。ServerAfter不能修改响应体,只能修改响应头。应该在ServerAfter中完成响应的最后处理,如CORS头、安全头等。
b.代码示例
---
// ServerAfter选��
package transport
import (
"context"
"net/http"
"time"
)
// setResponseHeaders 设置响应头
func setResponseHeaders(ctx context.Context, w http.ResponseWriter) context.Context {
if requestID, ok := ctx.Value(ContextKeyRequestID).(string); ok {
w.Header().Set("X-Request-ID", requestID)
}
w.Header().Set("X-Service-Name", "user-service")
w.Header().Set("X-Service-Version", "1.0.0")
return ctx
}
// setCORSHeaders 设置CORS头
func setCORSHeaders(ctx context.Context, w http.ResponseWriter) context.Context {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Request-ID")
w.Header().Set("Access-Control-Max-Age", "3600")
return ctx
}
// setSecurityHeaders 设置安全头
func setSecurityHeaders(ctx context.Context, w http.ResponseWriter) context.Context {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("X-XSS-Protection", "1; mode=block")
w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
return ctx
}
// logResponse 记录响应日志
func logResponse(logger log.Logger) httptransport.ServerResponseFunc {
return func(ctx context.Context, w http.ResponseWriter) context.Context {
requestID, _ := ctx.Value(ContextKeyRequestID).(string)
startTime, _ := ctx.Value("start_time").(time.Time)
duration := time.Since(startTime)
logger.Log(
"request_id", requestID,
"duration_ms", duration.Milliseconds(),
)
return ctx
}
}
// MakeHTTPHandlerWithOptions 创建带选项的处理器
func MakeHTTPHandlerWithOptions(endpoints Endpoints, logger log.Logger) http.Handler {
options := []httptransport.ServerOption{
httptransport.ServerBefore(
extractRequestID,
extractAuthToken,
recordStartTime,
),
httptransport.ServerAfter(
setResponseHeaders,
setCORSHeaders,
setSecurityHeaders,
logResponse(logger),
),
httptransport.ServerErrorEncoder(encodeError),
}
r := mux.NewRouter()
r.Methods("POST").Path("/users").Handler(httptransport.NewServer(
endpoints.CreateUser,
decodeCreateUserRequest,
encodeResponse,
options...,
))
return r
}
func recordStartTime(ctx context.Context, r *http.Request) context.Context {
return context.WithValue(ctx, "start_time", time.Now())
}
---
03.HTTP客户端
a.客户端创建
a.功能说明
HTTP客户端使用httptransport.NewClient创建,封装HTTP请求的发送和响应的接收。客户端返回标准的Endpoint类型,可以像本地Endpoint一样使用。需要指定目标URL、HTTP方法、请求编码器、响应解码器等参数。客户端可以配置超时、重试、熔断等策略。使用客户端可以简化远程服务调用,隐藏HTTP通信的细节。
b.代码示例
---
// HTTP客户端创建
package client
import (
"net/url"
"strings"
"time"
"github.com/go-kit/kit/endpoint"
httptransport "github.com/go-kit/kit/transport/http"
)
// NewHTTPClient 创建HTTP客户端
func NewHTTPClient(instance string, logger log.Logger) (Service, error) {
if !strings.HasPrefix(instance, "http") {
instance = "http://" + instance
}
u, err := url.Parse(instance)
if err != nil {
return nil, err
}
// 配置HTTP客户端
httpClient := &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
},
}
options := []httptransport.ClientOption{
httptransport.SetClient(httpClient),
httptransport.ClientBefore(setRequestHeaders),
}
// 创建用户端点
createUserEndpoint := httptransport.NewClient(
"POST",
copyURL(u, "/users"),
encodeJSONRequest,
decodeCreateUserResponse,
options...,
).Endpoint()
getUserEndpoint := httptransport.NewClient(
"GET",
copyURL(u, "/users"),
encodeGetUserRequest,
decodeGetUserResponse,
options...,
).Endpoint()
updateUserEndpoint := httptransport.NewClient(
"PUT",
copyURL(u, "/users"),
encodeUpdateUserRequest,
decodeUpdateUserResponse,
options...,
).Endpoint()
deleteUserEndpoint := httptransport.NewClient(
"DELETE",
copyURL(u, "/users"),
encodeDeleteUserRequest,
decodeDeleteUserResponse,
options...,
).Endpoint()
return Endpoints{
CreateUserEndpoint: createUserEndpoint,
GetUserEndpoint: getUserEndpoint,
UpdateUserEndpoint: updateUserEndpoint,
DeleteUserEndpoint: deleteUserEndpoint,
}, nil
}
func copyURL(base *url.URL, path string) *url.URL {
next := *base
next.Path = path
return &next
}
func setRequestHeaders(ctx context.Context, r *http.Request) context.Context {
r.Header.Set("Content-Type", "application/json")
r.Header.Set("User-Agent", "go-kit-client/1.0")
return ctx
}
---
b.客户端选项
a.功能说明
客户端选项用于配置HTTP客户端的行为,如设置请求头、超时时间、重试策略等。ClientBefore在请求发送前执行,可以修改HTTP请求。ClientAfter在响应接收后执行,可以从响应中提取元数据。可以使用SetClient选项自定义http.Client,配置连接池、TLS等。合理配置客户端选项可以提高客户端的可靠性和性能。
b.代码示例
---
// HTTP客户端选项
package client
import (
"context"
"crypto/tls"
"net/http"
"time"
)
// ClientConfig 客户端配置
type ClientConfig struct {
Timeout time.Duration
MaxIdleConns int
TLSConfig *tls.Config
RequestTimeout time.Duration
RetryMax int
RetryWaitMin time.Duration
RetryWaitMax time.Duration
}
// DefaultClientConfig 默认配置
func DefaultClientConfig() ClientConfig {
return ClientConfig{
Timeout: 30 * time.Second,
MaxIdleConns: 100,
RequestTimeout: 10 * time.Second,
RetryMax: 3,
RetryWaitMin: 100 * time.Millisecond,
RetryWaitMax: 1 * time.Second,
}
}
// NewHTTPClientWithConfig 使用配置创建客户端
func NewHTTPClientWithConfig(instance string, config ClientConfig) *http.Client {
transport := &http.Transport{
MaxIdleConns: config.MaxIdleConns,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
}
if config.TLSConfig != nil {
transport.TLSClientConfig = config.TLSConfig
}
return &http.Client{
Transport: transport,
Timeout: config.Timeout,
}
}
// setAuthHeader 设置认证头
func setAuthHeader(token string) httptransport.RequestFunc {
return func(ctx context.Context, r *http.Request) context.Context {
r.Header.Set("Authorization", "Bearer "+token)
return ctx
}
}
// setRequestID 设置请求ID
func setRequestID(ctx context.Context, r *http.Request) context.Context {
if requestID, ok := ctx.Value("request_id").(string); ok {
r.Header.Set("X-Request-ID", requestID)
}
return ctx
}
// extractResponseHeaders 提取响应头
func extractResponseHeaders(ctx context.Context, r *http.Response) context.Context {
if requestID := r.Header.Get("X-Request-ID"); requestID != "" {
ctx = context.WithValue(ctx, "response_request_id", requestID)
}
return ctx
}
---
4.2 gRPC传输
01.gRPC服务器
a.服务定义
a.功能说明
gRPC使用Protocol Buffers定义服务接口,生成服务端和客户端代码。go-kit的grpctransport包提供了gRPC与Endpoint的集成。需要定义.proto文件描述服务接口,使用protoc编译生成Go代码。gRPC服务器实现生成的接口,将gRPC请求转换为Endpoint调用。gRPC支持流式RPC,适合高性能场景。
b.代码示例
---
// gRPC服务定义
// user.proto
syntax = "proto3";
package pb;
option go_package = "./pb";
service UserService {
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc ListUsers(ListUsersRequest) returns (stream User);
}
message CreateUserRequest {
string username = 1;
string email = 2;
}
message CreateUserResponse {
User user = 1;
string error = 2;
}
message GetUserRequest {
string id = 1;
}
message GetUserResponse {
User user = 1;
string error = 2;
}
message User {
string id = 1;
string username = 2;
string email = 3;
}
message ListUsersRequest {
int32 offset = 1;
int32 limit = 2;
}
---
b.服务实现
a.功能说明
gRPC服务器实现生成的接口,使用grpctransport.NewServer创建处理器。每个RPC方法对应一个Endpoint,通过解码器和编码器转换Protobuf消息。gRPC服务器支持拦截器,可以实现认证、日志、追踪等功能。服务实现应该保持简洁,只做协议转换,业务逻辑在Service层。
b.代码示例
---
// gRPC服务实现
package transport
import (
"context"
grpctransport "github.com/go-kit/kit/transport/grpc"
"google.golang.org/grpc"
)
type grpcServer struct {
createUser grpctransport.Handler
getUser grpctransport.Handler
pb.UnimplementedUserServiceServer
}
// NewGRPCServer 创建gRPC服务器
func NewGRPCServer(endpoints Endpoints) pb.UserServiceServer {
return &grpcServer{
createUser: grpctransport.NewServer(
endpoints.CreateUser,
decodeGRPCCreateUserRequest,
encodeGRPCCreateUserResponse,
),
getUser: grpctransport.NewServer(
endpoints.GetUser,
decodeGRPCGetUserRequest,
encodeGRPCGetUserResponse,
),
}
}
func (s *grpcServer) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.CreateUserResponse, error) {
_, resp, err := s.createUser.ServeGRPC(ctx, req)
if err != nil {
return nil, err
}
return resp.(*pb.CreateUserResponse), nil
}
func (s *grpcServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
_, resp, err := s.getUser.ServeGRPC(ctx, req)
if err != nil {
return nil, err
}
return resp.(*pb.GetUserResponse), nil
}
// 启动gRPC服务器
func RunGRPCServer(endpoints Endpoints, port string) error {
listener, err := net.Listen("tcp", port)
if err != nil {
return err
}
grpcServer := grpc.NewServer()
pb.RegisterUserServiceServer(grpcServer, NewGRPCServer(endpoints))
return grpcServer.Serve(listener)
}
---
02.gRPC编解码
a.请求解码
a.功能说明
gRPC请求解码器将Protobuf消息转换为Endpoint请求对象。解码器接收proto生成的消息类型,提取字段构造请求对象。解码过程应该处理字段验证,确保数据完整性。对于复杂消息,可以使用辅助函数简化转换逻辑。解码器应该保持简单,只做数据转换,不包含业务逻辑。
b.代码示例
---
// gRPC请求解码
package transport
import (
"context"
)
// decodeGRPCCreateUserRequest 解码创建用户请求
func decodeGRPCCreateUserRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(*pb.CreateUserRequest)
return endpoint.CreateUserRequest{
Username: req.Username,
Email: req.Email,
}, nil
}
// decodeGRPCGetUserRequest 解码获取用户请求
func decodeGRPCGetUserRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(*pb.GetUserRequest)
return endpoint.GetUserRequest{
ID: req.Id,
}, nil
}
// decodeGRPCListUsersRequest 解码列表请求
func decodeGRPCListUsersRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(*pb.ListUsersRequest)
return endpoint.ListUsersRequest{
Offset: int(req.Offset),
Limit: int(req.Limit),
}, nil
}
// decodeGRPCUpdateUserRequest 解码更新请求
func decodeGRPCUpdateUserRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(*pb.UpdateUserRequest)
return endpoint.UpdateUserRequest{
ID: req.Id,
Updates: endpoint.UserUpdates{
Username: req.Username,
Email: req.Email,
},
}, nil
}
---
b.响应编码
a.功能说明
gRPC响应编码器将Endpoint响应对象转换为Protobuf消息。编码器构造proto消息,填充字段并返回。应该处理错误情况,将错误信息编码到响应中。对于流式RPC,需要特殊处理,逐个发送消息。编码器应该确保消息格式正确,符合proto定义。
b.代码示例
---
// gRPC响应编码
package transport
import (
"context"
)
// encodeGRPCCreateUserResponse 编码创建用户响应
func encodeGRPCCreateUserResponse(_ context.Context, response interface{}) (interface{}, error) {
resp := response.(endpoint.CreateUserResponse)
return &pb.CreateUserResponse{
User: &pb.User{
Id: resp.User.ID,
Username: resp.User.Username,
Email: resp.User.Email,
},
Error: resp.Err,
}, nil
}
// encodeGRPCGetUserResponse 编码获取用户响应
func encodeGRPCGetUserResponse(_ context.Context, response interface{}) (interface{}, error) {
resp := response.(endpoint.GetUserResponse)
if resp.Err != "" {
return &pb.GetUserResponse{
Error: resp.Err,
}, nil
}
return &pb.GetUserResponse{
User: &pb.User{
Id: resp.User.ID,
Username: resp.User.Username,
Email: resp.User.Email,
},
}, nil
}
// encodeGRPCListUsersResponse 编码列表响应
func encodeGRPCListUsersResponse(_ context.Context, response interface{}) (interface{}, error) {
resp := response.(endpoint.ListUsersResponse)
users := make([]*pb.User, len(resp.Users))
for i, u := range resp.Users {
users[i] = &pb.User{
Id: u.ID,
Username: u.Username,
Email: u.Email,
}
}
return &pb.ListUsersResponse{
Users: users,
Total: int32(resp.Total),
}, nil
}
---
03.gRPC客户端
a.客户端创建
a.功能说明
gRPC客户端使用grpctransport.NewClient创建,封装gRPC调用。客户端返回Endpoint类型,可以应用中间件。需要建立gRPC连接,配置连接选项如超时、重试等。客户端支持负载均衡和服务发现,可以连接多个服务实例。使用连接池可以提高性能,减少连接开销。
b.代码示例
---
// gRPC客户端创建
package client
import (
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
grpctransport "github.com/go-kit/kit/transport/grpc"
)
// NewGRPCClient 创建gRPC客户端
func NewGRPCClient(conn *grpc.ClientConn) Service {
options := []grpctransport.ClientOption{}
createUserEndpoint := grpctransport.NewClient(
conn,
"pb.UserService",
"CreateUser",
encodeGRPCCreateUserRequest,
decodeGRPCCreateUserResponse,
&pb.CreateUserResponse{},
options...,
).Endpoint()
getUserEndpoint := grpctransport.NewClient(
conn,
"pb.UserService",
"GetUser",
encodeGRPCGetUserRequest,
decodeGRPCGetUserResponse,
&pb.GetUserResponse{},
options...,
).Endpoint()
return Endpoints{
CreateUserEndpoint: createUserEndpoint,
GetUserEndpoint: getUserEndpoint,
}
}
// NewGRPCConn 创建gRPC连接
func NewGRPCConn(target string) (*grpc.ClientConn, error) {
return grpc.Dial(
target,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithTimeout(10*time.Second),
grpc.WithBlock(),
)
}
// 使用示例
func ExampleGRPCClient() {
conn, err := NewGRPCConn("localhost:8080")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := NewGRPCClient(conn)
resp, err := client.CreateUser(context.Background(), "john", "[email protected]")
if err != nil {
log.Fatal(err)
}
fmt.Println(resp)
}
---
b.拦截器
a.功能说明
gRPC拦截器类似于HTTP中间件,在RPC调用前后执行。支持一元拦截器和流式拦截器,分别处理一元RPC和流式RPC。拦截器可以实现认证、日志、追踪、重试等功能。客户端和服务端都可以使用拦截器。拦截器链按顺序执行,可以组合多个拦截器。合理使用拦截器可以简化横切关注点的实现。
b.代码示例
---
// gRPC拦截器
package interceptor
import (
"context"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
// LoggingInterceptor 日志拦截器
func LoggingInterceptor(logger log.Logger) grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
start := time.Now()
err := invoker(ctx, method, req, reply, cc, opts...)
logger.Log(
"method", method,
"duration", time.Since(start),
"error", err,
)
return err
}
}
// AuthInterceptor 认证拦截器
func AuthInterceptor(token string) grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
md := metadata.New(map[string]string{
"authorization": "Bearer " + token,
})
ctx = metadata.NewOutgoingContext(ctx, md)
return invoker(ctx, method, req, reply, cc, opts...)
}
}
// RetryInterceptor 重试拦截器
func RetryInterceptor(maxRetries int) grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
var err error
for i := 0; i <= maxRetries; i++ {
err = invoker(ctx, method, req, reply, cc, opts...)
if err == nil {
return nil
}
if i < maxRetries {
time.Sleep(time.Duration(i+1) * 100 * time.Millisecond)
}
}
return err
}
}
// 使用拦截器
func NewGRPCConnWithInterceptors(target string, logger log.Logger, token string) (*grpc.ClientConn, error) {
return grpc.Dial(
target,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithChainUnaryInterceptor(
LoggingInterceptor(logger),
AuthInterceptor(token),
RetryInterceptor(3),
),
)
}
---
4.3 Thrift传输
01.Thrift集成
a.服务定义
a.功能说明
Thrift是Facebook开源的跨语言RPC框架,使用IDL定义服务接口。go-kit支持Thrift传输,通过thrifttransport包集成。需要定义.thrift文件描述服务,使用thrift编译器生成Go代码。Thrift支持多种传输协议(TBinaryProtocol、TCompactProtocol)和传输方式(TSocket、TFramedTransport)。Thrift适合需要跨语言通信的场景。
b.代码示例
---
// Thrift服务定义
// user.thrift
namespace go user
struct User {
1: string id
2: string username
3: string email
}
struct CreateUserRequest {
1: string username
2: string email
}
struct CreateUserResponse {
1: User user
2: string error
}
service UserService {
CreateUserResponse CreateUser(1: CreateUserRequest req)
User GetUser(1: string id)
list<User> ListUsers(1: i32 offset, 2: i32 limit)
}
---
b.服务实现
a.功能说明
Thrift服务器实现生成的接口,使用thrifttransport.NewServer创建处理器。每个Thrift方法对应一个Endpoint,通过编解码器转换Thrift结构体。Thrift服务器配置传输协议和传输方式,支持多种组合。服务实现应该保持简洁,只做协议转换。Thrift的二进制协议比JSON更高效,适合性能敏感场景。
b.代码示例
---
// Thrift服务实现
package transport
import (
"context"
"github.com/apache/thrift/lib/go/thrift"
thrifttransport "github.com/go-kit/kit/transport/thrift"
)
type thriftServer struct {
createUser thrifttransport.Handler
getUser thrifttransport.Handler
}
func NewThriftServer(endpoints Endpoints) *thriftServer {
return &thriftServer{
createUser: thrifttransport.NewServer(
endpoints.CreateUser,
decodeThriftCreateUserRequest,
encodeThriftCreateUserResponse,
),
getUser: thrifttransport.NewServer(
endpoints.GetUser,
decodeThriftGetUserRequest,
encodeThriftGetUserResponse,
),
}
}
func (s *thriftServer) CreateUser(ctx context.Context, req *user.CreateUserRequest) (*user.CreateUserResponse, error) {
_, resp, err := s.createUser.ServeThrift(ctx, req)
if err != nil {
return nil, err
}
return resp.(*user.CreateUserResponse), nil
}
func (s *thriftServer) GetUser(ctx context.Context, id string) (*user.User, error) {
_, resp, err := s.getUser.ServeThrift(ctx, id)
if err != nil {
return nil, err
}
return resp.(*user.User), nil
}
// 启动Thrift服务器
func RunThriftServer(endpoints Endpoints, port string) error {
processor := user.NewUserServiceProcessor(NewThriftServer(endpoints))
transport, err := thrift.NewTServerSocket(port)
if err != nil {
return err
}
server := thrift.NewTSimpleServer4(
processor,
transport,
thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory()),
thrift.NewTBinaryProtocolFactoryDefault(),
)
return server.Serve()
}
---
02.编解码器
a.请求解码
a.功能说明
Thrift请求解码器将Thrift结构体转换为Endpoint请求对象。解码器接收thrift生成的结构体,提取字段构造请求对象。Thrift的强类型特性使得解码过程更安全,减少类型错误。解码器应该处理必填字段验证,确保数据完整性。对于可选字段,应该提供合理的默认值。
b.代码示例
---
// Thrift请求解码
package transport
import "context"
func decodeThriftCreateUserRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(*user.CreateUserRequest)
return endpoint.CreateUserRequest{
Username: req.Username,
Email: req.Email,
}, nil
}
func decodeThriftGetUserRequest(_ context.Context, request interface{}) (interface{}, error) {
id := request.(string)
return endpoint.GetUserRequest{
ID: id,
}, nil
}
func decodeThriftListUsersRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(*user.ListUsersRequest)
return endpoint.ListUsersRequest{
Offset: int(req.Offset),
Limit: int(req.Limit),
}, nil
}
func decodeThriftUpdateUserRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(*user.UpdateUserRequest)
return endpoint.UpdateUserRequest{
ID: req.Id,
Updates: endpoint.UserUpdates{
Username: req.Username,
Email: req.Email,
},
}, nil
}
---
b.响应编码
a.功能说明
Thrift响应编码器将Endpoint���应对象转换为Thrift结构体。编码器构造thrift结构体,填充字段并返回。应该处理错误情况,将错误信息编码到响应中。Thrift的结构化错误处理比HTTP更规范,可以定义专门的异常类型。编码器应该确保所有必填字段都被正确设置。
b.代码示例
---
// Thrift响应编码
package transport
import "context"
func encodeThriftCreateUserResponse(_ context.Context, response interface{}) (interface{}, error) {
resp := response.(endpoint.CreateUserResponse)
return &user.CreateUserResponse{
User: &user.User{
Id: resp.User.ID,
Username: resp.User.Username,
Email: resp.User.Email,
},
Error: resp.Err,
}, nil
}
func encodeThriftGetUserResponse(_ context.Context, response interface{}) (interface{}, error) {
resp := response.(endpoint.GetUserResponse)
if resp.Err != "" {
return nil, errors.New(resp.Err)
}
return &user.User{
Id: resp.User.ID,
Username: resp.User.Username,
Email: resp.User.Email,
}, nil
}
func encodeThriftListUsersResponse(_ context.Context, response interface{}) (interface{}, error) {
resp := response.(endpoint.ListUsersResponse)
users := make([]*user.User, len(resp.Users))
for i, u := range resp.Users {
users[i] = &user.User{
Id: u.ID,
Username: u.Username,
Email: u.Email,
}
}
return users, nil
}
---
03.Thrift客户端
a.客户端创建
a.功能说明
Thrift客户端使用thrifttransport.NewClient创建,封装Thrift调用。客户端返回Endpoint类型,可以应用中间件。需要建立Thrift连接,配置传输协议和传输方式。Thrift客户端支持连接池,可以复用连接提高性能。客户端应该处理连接错误和超时,实现重试机制。
b.代码示例
---
// Thrift客户端创建
package client
import (
"github.com/apache/thrift/lib/go/thrift"
thrifttransport "github.com/go-kit/kit/transport/thrift"
)
func NewThriftClient(addr string) (Service, error) {
transport, err := thrift.NewTSocket(addr)
if err != nil {
return nil, err
}
transport = thrift.NewTFramedTransport(transport)
protocol := thrift.NewTBinaryProtocolTransport(transport)
if err := transport.Open(); err != nil {
return nil, err
}
client := user.NewUserServiceClientProtocol(transport, protocol, protocol)
createUserEndpoint := thrifttransport.NewClient(
client,
"CreateUser",
encodeThriftCreateUserRequest,
decodeThriftCreateUserResponse,
).Endpoint()
getUserEndpoint := thrifttransport.NewClient(
client,
"GetUser",
encodeThriftGetUserRequest,
decodeThriftGetUserResponse,
).Endpoint()
return Endpoints{
CreateUserEndpoint: createUserEndpoint,
GetUserEndpoint: getUserEndpoint,
}, nil
}
func encodeThriftCreateUserRequest(_ context.Context, request interface{}) (interface{}, error) {
req := request.(endpoint.CreateUserRequest)
return &user.CreateUserRequest{
Username: req.Username,
Email: req.Email,
}, nil
}
func decodeThriftCreateUserResponse(_ context.Context, response interface{}) (interface{}, error) {
resp := response.(*user.CreateUserResponse)
return endpoint.CreateUserResponse{
User: endpoint.User{
ID: resp.User.Id,
Username: resp.User.Username,
Email: resp.User.Email,
},
Err: resp.Error,
}, nil
}
---
b.连接管理
a.功能说明
Thrift客户端需要管理连接生命周期,包括连接建立、保持、关闭。应该实现连接池,复用连接减少开销。连接池应该配置最大连接数、空闲超时等参数。需要处理连接断开和重连,实现自动恢复机制。连接管理应该是线程安全的,支持并发访问。合理的连接管理可以提高客户端的性能和可靠性。
b.代码示例
---
// Thrift连接管理
package client
import (
"sync"
"time"
)
type ThriftPool struct {
addr string
maxConns int
idleTimeout time.Duration
conns chan *thrift.TTransport
mu sync.Mutex
}
func NewThriftPool(addr string, maxConns int, idleTimeout time.Duration) *ThriftPool {
return &ThriftPool{
addr: addr,
maxConns: maxConns,
idleTimeout: idleTimeout,
conns: make(chan *thrift.TTransport, maxConns),
}
}
func (p *ThriftPool) Get() (*thrift.TTransport, error) {
select {
case conn := <-p.conns:
if conn.IsOpen() {
return conn, nil
}
conn.Close()
default:
}
transport, err := thrift.NewTSocket(p.addr)
if err != nil {
return nil, err
}
transport = thrift.NewTFramedTransport(transport)
if err := transport.Open(); err != nil {
return nil, err
}
return &transport, nil
}
func (p *ThriftPool) Put(conn *thrift.TTransport) {
if !conn.IsOpen() {
return
}
select {
case p.conns <- conn:
default:
conn.Close()
}
}
func (p *ThriftPool) Close() {
close(p.conns)
for conn := range p.conns {
conn.Close()
}
}
// 使用连接池
func NewThriftClientWithPool(pool *ThriftPool) Service {
createUserEndpoint := func(ctx context.Context, request interface{}) (interface{}, error) {
conn, err := pool.Get()
if err != nil {
return nil, err
}
defer pool.Put(conn)
// 执行RPC调用
return callCreateUser(conn, request)
}
return Endpoints{
CreateUserEndpoint: createUserEndpoint,
}
}
---
4.4 自定义传输
``` 01.传输抽象 a.接口定义 a.功能说明 go-kit的Transport层是高度抽象的,可以实现自定义传输协议。只需实现Server和Client接口,提供编解码器即可。自定义传输适合特殊场景,如WebSocket、MQTT、AMQP等协议。传输抽象使得Endpoint可以无缝切换不同协议,业务逻辑不受影响。实现自定义传输需要理解go-kit的Transport模型,遵循统一的接口规范。 b.代码示例 --- // 自定义传输接口 package transport
import "context"
type Server interface {
ServeHTTP(w http.ResponseWriter, r *http.Request)
}
type Handler interface {
ServeTransport(ctx context.Context, request interface{}) (context.Context, interface{}, error)
}
type DecodeRequestFunc func(context.Context, interface{}) (interface{}, error)
type EncodeResponseFunc func(context.Context, interface{}, interface{}) error
type CustomServer struct {
e endpoint.Endpoint
dec DecodeRequestFunc
enc EncodeResponseFunc
before []RequestFunc
after []ServerResponseFunc
errorEncoder ErrorEncoder
}
func NewCustomServer(
e endpoint.Endpoint,
dec DecodeRequestFunc,
enc EncodeResponseFunc,
options ...ServerOption,
) *CustomServer {
s := &CustomServer{
e: e,
dec: dec,
enc: enc,
errorEncoder: DefaultErrorEncoder,
}
for _, option := range options {
option(s)
}
return s
}
func (s *CustomServer) ServeTransport(ctx context.Context, req interface{}) (context.Context, interface{}, error) {
for _, f := range s.before {
ctx = f(ctx, req)
}
request, err := s.dec(ctx, req)
if err != nil {
return ctx, nil, err
}
response, err := s.e(ctx, request)
if err != nil {
return ctx, nil, err
}
for _, f := range s.after {
ctx = f(ctx, response)
}
return ctx, response, nil
}
---
b.实现示例
a.功能说明
实现自定义传输需要创建Server和Client,处理协议特定的细节。Server负责接收请求,调用Endpoint,返回响应。Client负责发送请求,接收响应,转换为Endpoint调用。需要实现编解码器,处理协议的序列化和反序列化。自定义传输应该支持中间件,实现横切关注点。实现时应该参考go-kit内置传输的代码,遵循相同的模式。
b.代码示例
---
// WebSocket传输实现
package transport
import (
"context"
"github.com/gorilla/websocket"
)
type WebSocketServer struct {
e endpoint.Endpoint
dec DecodeRequestFunc
enc EncodeResponseFunc
upgrader websocket.Upgrader
}
func NewWebSocketServer(
e endpoint.Endpoint,
dec DecodeRequestFunc,
enc EncodeResponseFunc,
) *WebSocketServer {
return &WebSocketServer{
e: e,
dec: dec,
enc: enc,
upgrader: websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
},
}
}
func (s *WebSocketServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
conn, err := s.upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
defer conn.Close()
for {
_, message, err := conn.ReadMessage()
if err != nil {
break
}
ctx := context.Background()
request, err := s.dec(ctx, message)
if err != nil {
continue
}
response, err := s.e(ctx, request)
if err != nil {
continue
}
responseData, err := s.enc(ctx, response)
if err != nil {
continue
}
conn.WriteMessage(websocket.TextMessage, responseData.([]byte))
}
}
// WebSocket客户端
type WebSocketClient struct {
conn *websocket.Conn
dec DecodeResponseFunc
enc EncodeRequestFunc
}
func NewWebSocketClient(url string, dec DecodeResponseFunc, enc EncodeRequestFunc) (*WebSocketClient, error) {
conn, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
return nil, err
}
return &WebSocketClient{
conn: conn,
dec: dec,
enc: enc,
}, nil
}
func (c *WebSocketClient) Endpoint() endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
requestData, err := c.enc(ctx, request)
if err != nil {
return nil, err
}
err = c.conn.WriteMessage(websocket.TextMessage, requestData.([]byte))
if err != nil {
return nil, err
}
_, message, err := c.conn.ReadMessage()
if err != nil {
return nil, err
}
return c.dec(ctx, message)
}
}
---
02.协议适配 a.协议转换 a.功能说明 协议适配器将不同协议的请求转换为统一的Endpoint调用。适配器处理协议特定的细节,如消息格式、连接管理、错误处理等。可以为同一个Endpoint提供多种协议支持,如同时支持HTTP和WebSocket。协议适配使得服务可以灵活地支持多种客户端,提高兼容性。��现协议适配需要深入理解各协议的特点和限制。 b.代码示例 --- // 协议适配器 package adapter
import "context"
type ProtocolAdapter interface {
Adapt(ctx context.Context, request interface{}) (interface{}, error)
}
type HTTPToGRPCAdapter struct {
grpcClient pb.UserServiceClient
}
func (a *HTTPToGRPCAdapter) Adapt(ctx context.Context, request interface{}) (interface{}, error) {
httpReq := request.(HTTPRequest)
grpcReq := &pb.CreateUserRequest{
Username: httpReq.Username,
Email: httpReq.Email,
}
grpcResp, err := a.grpcClient.CreateUser(ctx, grpcReq)
if err != nil {
return nil, err
}
return HTTPResponse{
User: User{
ID: grpcResp.User.Id,
Username: grpcResp.User.Username,
Email: grpcResp.User.Email,
},
}, nil
}
type MultiProtocolServer struct {
endpoint endpoint.Endpoint
adapters map[string]ProtocolAdapter
}
func NewMultiProtocolServer(e endpoint.Endpoint) *MultiProtocolServer {
return &MultiProtocolServer{
endpoint: e,
adapters: make(map[string]ProtocolAdapter),
}
}
func (s *MultiProtocolServer) RegisterAdapter(protocol string, adapter ProtocolAdapter) {
s.adapters[protocol] = adapter
}
func (s *MultiProtocolServer) ServeProtocol(ctx context.Context, protocol string, request interface{}) (interface{}, error) {
adapter, ok := s.adapters[protocol]
if !ok {
return s.endpoint(ctx, request)
}
adaptedRequest, err := adapter.Adapt(ctx, request)
if err != nil {
return nil, err
}
return s.endpoint(ctx, adaptedRequest)
}
---
b.中间件支持
a.功能说明
自定义传输应该支持中间件,实现日志、监控、认证等功能。中间件应该与协议无关,可以在不同传输间复用。需要定义中间件接口,支持链式组合。中间件应该在传输层和Endpoint层之间,处理传输相关的横切关注点。合理使用中间件可以简化自定义传输的实现,提高代码复用性。
b.代码示例
---
// 传输中间件
package transport
import "context"
type Middleware func(Handler) Handler
type Handler func(ctx context.Context, request interface{}) (interface{}, error)
func LoggingMiddleware(logger log.Logger) Middleware {
return func(next Handler) Handler {
return func(ctx context.Context, request interface{}) (interface{}, error) {
logger.Log("msg", "handling request", "request", request)
response, err := next(ctx, request)
logger.Log("msg", "handled request", "response", response, "err", err)
return response, err
}
}
}
func AuthMiddleware(validator TokenValidator) Middleware {
return func(next Handler) Handler {
return func(ctx context.Context, request interface{}) (interface{}, error) {
token, ok := ctx.Value("token").(string)
if !ok || token == "" {
return nil, ErrUnauthorized
}
if err := validator.Validate(token); err != nil {
return nil, ErrInvalidToken
}
return next(ctx, request)
}
}
}
func ChainMiddleware(middlewares ...Middleware) Middleware {
return func(next Handler) Handler {
for i := len(middlewares) - 1; i >= 0; i-- {
next = middlewares[i](next)
}
return next
}
}
// 使用中间件
func NewServerWithMiddleware(e endpoint.Endpoint, middlewares ...Middleware) Handler {
handler := func(ctx context.Context, request interface{}) (interface{}, error) {
return e(ctx, request)
}
for i := len(middlewares) - 1; i >= 0; i-- {
handler = middlewares[i](handler)
}
return handler
}
---
03.最佳实践 a.性能优化 a.功能说明 自定义传输应该注重性能优化,减少不必要的开销。使用连接池复用连接,减少建立连接的开销。使用缓冲区减少内存分配,提高吞吐量。合理设置超时时间,避免资源泄漏。使用异步处理提高并发能力。性能优化应该基于性能测试的结果,针对性地优化瓶颈。 b.代码示例 --- // 性能优化示例 package transport
import (
"sync"
"time"
)
type ConnectionPool struct {
factory func() (interface{}, error)
pool chan interface{}
maxConns int
timeout time.Duration
mu sync.Mutex
}
func NewConnectionPool(factory func() (interface{}, error), maxConns int, timeout time.Duration) *ConnectionPool {
return &ConnectionPool{
factory: factory,
pool: make(chan interface{}, maxConns),
maxConns: maxConns,
timeout: timeout,
}
}
func (p *ConnectionPool) Get() (interface{}, error) {
select {
case conn := <-p.pool:
return conn, nil
case <-time.After(p.timeout):
return nil, ErrTimeout
default:
return p.factory()
}
}
func (p *ConnectionPool) Put(conn interface{}) {
select {
case p.pool <- conn:
default:
// Pool is full, discard connection
}
}
// 缓冲区池
var bufferPool = sync.Pool{
New: func() interface{} {
return make([]byte, 4096)
},
}
func GetBuffer() []byte {
return bufferPool.Get().([]byte)
}
func PutBuffer(buf []byte) {
bufferPool.Put(buf)
}
// 异步处理
type AsyncHandler struct {
handler Handler
workers int
requests chan asyncRequest
}
type asyncRequest struct {
ctx context.Context
request interface{}
response chan asyncResponse
}
type asyncResponse struct {
response interface{}
err error
}
func NewAsyncHandler(handler Handler, workers int) *AsyncHandler {
h := &AsyncHandler{
handler: handler,
workers: workers,
requests: make(chan asyncRequest, workers*2),
}
for i := 0; i < workers; i++ {
go h.worker()
}
return h
}
func (h *AsyncHandler) worker() {
for req := range h.requests {
response, err := h.handler(req.ctx, req.request)
req.response <- asyncResponse{response, err}
}
}
func (h *AsyncHandler) Handle(ctx context.Context, request interface{}) (interface{}, error) {
respChan := make(chan asyncResponse, 1)
h.requests <- asyncRequest{ctx, request, respChan}
select {
case resp := <-respChan:
return resp.response, resp.err
case <-ctx.Done():
return nil, ctx.Err()
}
}
---
b.错误处理
a.功能说明
自定义传输应该妥善处理各种错误情况,包括网络错误、协议错误、超时错误等。应该定义清晰的错误类型,便于客户端处理。错误信息应该包含足够的上下文,便于问题排查。对于可恢复的错误,应该实现重试机制。对于不可恢复的错误,应该快速失败并返回明确的错误信息。错误处理应该考虑安全性,不暴露敏感信息。
b.代码示例
---
// 错误处理
package transport
import (
"errors"
"fmt"
)
type TransportError struct {
Code string
Message string
Err error
}
func (e *TransportError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%s: %s: %v", e.Code, e.Message, e.Err)
}
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
func (e *TransportError) Unwrap() error {
return e.Err
}
var (
ErrConnectionFailed = &TransportError{
Code: "CONNECTION_FAILED",
Message: "failed to establish connection",
}
ErrTimeout = &TransportError{
Code: "TIMEOUT",
Message: "request timeout",
}
ErrInvalidProtocol = &TransportError{
Code: "INVALID_PROTOCOL",
Message: "invalid protocol",
}
)
func HandleError(err error) error {
var transportErr *TransportError
if errors.As(err, &transportErr) {
switch transportErr.Code {
case "CONNECTION_FAILED", "TIMEOUT":
// Retryable errors
return fmt.Errorf("retryable error: %w", err)
default:
// Non-retryable errors
return fmt.Errorf("fatal error: %w", err)
}
}
return err
}
type ErrorHandler interface {
HandleError(ctx context.Context, err error) error
}
type DefaultErrorHandler struct {
logger log.Logger
}
func (h *DefaultErrorHandler) HandleError(ctx context.Context, err error) error {
h.logger.Log("error", err)
var transportErr *TransportError
if errors.As(err, &transportErr) {
return &TransportError{
Code: transportErr.Code,
Message: "internal error",
}
}
return &TransportError{
Code: "UNKNOWN_ERROR",
Message: "unknown error occurred",
}
}
---
```
4.5 编解码器
01.JSON编解码
a.JSON解码
a.功能说明
JSON是最常用的数据交换格式,go-kit默认支持JSON编解码。JSON解码器使用encoding/json包解析HTTP请求体,将JSON数据转换为Go结构体。解码器应该处理JSON格式错误,返回清晰的错误信息。可以使用json.Decoder的DisallowUnknownFields方法拒绝未知字段,提高安全性。对于大型JSON,应该使用流式解码减少内存占用。
b.代码示例
---
// JSON解码器
package transport
import (
"context"
"encoding/json"
"errors"
"io"
"net/http"
)
func decodeJSONRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req CreateUserRequest
decoder := json.NewDecoder(r.Body)
decoder.DisallowUnknownFields()
if err := decoder.Decode(&req); err != nil {
if err == io.EOF {
return nil, errors.New("empty request body")
}
return nil, fmt.Errorf("invalid JSON: %w", err)
}
return req, nil
}
func decodeJSONRequestWithValidation(_ context.Context, r *http.Request) (interface{}, error) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
if req.Username == "" {
return nil, errors.New("username is required")
}
if req.Email == "" {
return nil, errors.New("email is required")
}
return req, nil
}
func decodeJSONArray(_ context.Context, r *http.Request) (interface{}, error) {
var items []Item
if err := json.NewDecoder(r.Body).Decode(&items); err != nil {
return nil, err
}
return items, nil
}
---
b.JSON编码
a.功能说明
JSON编码器将Go结构体转换为JSON格式并写入HTTP响应。编码器使用encoding/json包序列化数据,设置Content-Type为application/json。可以使用json.Encoder的SetIndent方法格式化输出,便于调试。对于大型响应,应该使用流式编码提高性能。编码器应该处理循环引用和特殊类型,避免编码失败。
b.代码示例
---
// JSON编码器
package transport
import (
"context"
"encoding/json"
"net/http"
)
func encodeJSONResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
func encodeJSONResponseWithStatus(status int) EncodeResponseFunc {
return func(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(status)
return json.NewEncoder(w).Encode(response)
}
}
func encodePrettyJSON(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
encoder := json.NewEncoder(w)
encoder.SetIndent("", " ")
return encoder.Encode(response)
}
func encodeJSONError(_ context.Context, err error, w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(map[string]string{
"error": err.Error(),
})
}
---
02.Protobuf编解码
a.Protobuf解码
a.功能说明
Protobuf是高效的二进制序列化格式,适合性能敏感场景。Protobuf解码器使用proto.Unmarshal解析二进制数据,转换为proto生成的结构体。解码器应该验证消息完整性,确保所有必填字段都存在。Protobuf的强类型特性使得解码更安全,减少运行时错误。对于大型消息,Protobuf比JSON更高效,占用更少的带宽和内存。
b.代码示例
---
// Protobuf解码器
package transport
import (
"context"
"io/ioutil"
"net/http"
"google.golang.org/protobuf/proto"
)
func decodeProtobufRequest(_ context.Context, r *http.Request) (interface{}, error) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
var req pb.CreateUserRequest
if err := proto.Unmarshal(body, &req); err != nil {
return nil, err
}
return endpoint.CreateUserRequest{
Username: req.Username,
Email: req.Email,
}, nil
}
func decodeProtobufWithValidation(_ context.Context, r *http.Request) (interface{}, error) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
var req pb.CreateUserRequest
if err := proto.Unmarshal(body, &req); err != nil {
return nil, err
}
if req.Username == "" {
return nil, errors.New("username is required")
}
return endpoint.CreateUserRequest{
Username: req.Username,
Email: req.Email,
}, nil
}
---
b.Protobuf编码
a.功能说明
Protobuf编码器将Go结构体转换为二进制格式并写入响应。编码器使用proto.Marshal序列化数据,设置Content-Type为application/x-protobuf。Protobuf编码比JSON更快,生成的数据更小。编码器应该确保所有必填字段都被设置,避免编码失败。对于流式数据,可以使用Protobuf的流式API提高性能。
b.代码示例
---
// Protobuf编码器
package transport
import (
"context"
"net/http"
"google.golang.org/protobuf/proto"
)
func encodeProtobufResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
resp := response.(endpoint.CreateUserResponse)
pbResp := &pb.CreateUserResponse{
User: &pb.User{
Id: resp.User.ID,
Username: resp.User.Username,
Email: resp.User.Email,
},
Error: resp.Err,
}
data, err := proto.Marshal(pbResp)
if err != nil {
return err
}
w.Header().Set("Content-Type", "application/x-protobuf")
w.Write(data)
return nil
}
func encodeProtobufWithStatus(status int) EncodeResponseFunc {
return func(_ context.Context, w http.ResponseWriter, response interface{}) error {
data, err := proto.Marshal(response.(proto.Message))
if err != nil {
return err
}
w.Header().Set("Content-Type", "application/x-protobuf")
w.WriteHeader(status)
w.Write(data)
return nil
}
}
---
03.自定义编解码
a.编解码器接口
a.功能说明
可以实现自定��编解码器支持特殊格式,如XML、YAML、MessagePack等。编解码器需要实现DecodeRequestFunc和EncodeResponseFunc接口。自定义编解码器应该处理格式特定的细节,如命名空间、标签等。可以组合多个编解码器,根据Content-Type选择合适的编解码器。自定义编解码器使得go-kit可以支持任意数据格式。
b.代码示例
---
// 自定义编解码器
package transport
import (
"context"
"encoding/xml"
"net/http"
"gopkg.in/yaml.v2"
)
func decodeXMLRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req CreateUserRequest
if err := xml.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
func encodeXMLResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/xml; charset=utf-8")
return xml.NewEncoder(w).Encode(response)
}
func decodeYAMLRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req CreateUserRequest
if err := yaml.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
func encodeYAMLResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/x-yaml; charset=utf-8")
return yaml.NewEncoder(w).Encode(response)
}
type ContentTypeCodec struct {
decoders map[string]DecodeRequestFunc
encoders map[string]EncodeResponseFunc
}
func NewContentTypeCodec() *ContentTypeCodec {
return &ContentTypeCodec{
decoders: make(map[string]DecodeRequestFunc),
encoders: make(map[string]EncodeResponseFunc),
}
}
func (c *ContentTypeCodec) RegisterDecoder(contentType string, decoder DecodeRequestFunc) {
c.decoders[contentType] = decoder
}
func (c *ContentTypeCodec) RegisterEncoder(contentType string, encoder EncodeResponseFunc) {
c.encoders[contentType] = encoder
}
func (c *ContentTypeCodec) Decode(ctx context.Context, r *http.Request) (interface{}, error) {
contentType := r.Header.Get("Content-Type")
decoder, ok := c.decoders[contentType]
if !ok {
return nil, fmt.Errorf("unsupported content type: %s", contentType)
}
return decoder(ctx, r)
}
func (c *ContentTypeCodec) Encode(ctx context.Context, w http.ResponseWriter, response interface{}) error {
accept := w.Header().Get("Accept")
encoder, ok := c.encoders[accept]
if !ok {
encoder = c.encoders["application/json"]
}
return encoder(ctx, w, response)
}
---
b.编解码优化
a.功能说明
编解码器的性能直接影响服务的吞吐量和延迟。应该使用对象池减少内存分配,复用编解码器对象。对于大型数据,使用流式编解码避免一次性加载到内存。可以使用并行编解码提高多核利用率。编解码器应该避免不必要的数据拷贝,使用零拷贝技术。性能优化应该基于性能测试的结果,针对性地优化瓶颈。
b.代码示例
---
// 编解码优化
package transport
import (
"bytes"
"encoding/json"
"sync"
)
var bufferPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
func decodeJSONOptimized(_ context.Context, r *http.Request) (interface{}, error) {
buf := bufferPool.Get().(*bytes.Buffer)
defer func() {
buf.Reset()
bufferPool.Put(buf)
}()
if _, err := buf.ReadFrom(r.Body); err != nil {
return nil, err
}
var req CreateUserRequest
if err := json.Unmarshal(buf.Bytes(), &req); err != nil {
return nil, err
}
return req, nil
}
var encoderPool = sync.Pool{
New: func() interface{} {
return json.NewEncoder(nil)
},
}
func encodeJSONOptimized(_ context.Context, w http.ResponseWriter, response interface{}) error {
encoder := encoderPool.Get().(*json.Encoder)
defer encoderPool.Put(encoder)
encoder.Reset(w)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return encoder.Encode(response)
}
type StreamDecoder struct {
decoder *json.Decoder
}
func NewStreamDecoder(r io.Reader) *StreamDecoder {
return &StreamDecoder{
decoder: json.NewDecoder(r),
}
}
func (d *StreamDecoder) DecodeNext(v interface{}) error {
return d.decoder.Decode(v)
}
func (d *StreamDecoder) More() bool {
return d.decoder.More()
}
---
4.6 传输中间件
01.HTTP中间件
a.请求拦截
a.功能说明
HTTP传输中间件可以在请求处理前后执行额外逻辑,如认证、日志、限流等。中间件使用装饰器模式包装Handler,形成中间件链。每个中间件都可以访问和修改请求响应,或者提前终止请求。中间件应该保持单一职责,每个中间件只做一件事。可以组合多个中间件实现复杂功能。中间件的执行顺序很重要,应该合理安排。
b.代码示例
---
// HTTP请求拦截中间件
package middleware
import (
"context"
"net/http"
"time"
)
// AuthMiddleware 认证中间件
func AuthMiddleware(tokenValidator TokenValidator) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "missing authorization", http.StatusUnauthorized)
return
}
claims, err := tokenValidator.Validate(token)
if err != nil {
http.Error(w, "invalid token", http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), "user_id", claims.UserID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// RateLimitMiddleware 限流中间件
func RateLimitMiddleware(limiter *rate.Limiter) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !limiter.Allow() {
http.Error(w, "rate limit exceeded", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}
}
// TimeoutMiddleware 超时中间件
func TimeoutMiddleware(timeout time.Duration) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), timeout)
defer cancel()
done := make(chan struct{})
go func() {
next.ServeHTTP(w, r.WithContext(ctx))
close(done)
}()
select {
case <-done:
return
case <-ctx.Done():
http.Error(w, "request timeout", http.StatusRequestTimeout)
}
})
}
}
---
b.响应处理
a.功能说明
响应处理中间件在响应发送前执行,可以修改响应头、压缩响应体、添加CORS头等。响应中间件可以实现统一的错误处理,将不同类型的错误转换为合适的HTTP状态码。可以添加响应时间、请求ID等元数据到响应头。响应中间件应该注意不要多次写入响应,避免冲突。使用ResponseWriter包装器可以捕获响应状态码和大小。
b.代码示例
---
// HTTP响应处理中间件
package middleware
import (
"compress/gzip"
"io"
"net/http"
"strings"
)
// CORSMiddleware CORS中间件
func CORSMiddleware(allowOrigins []string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
for _, allowed := range allowOrigins {
if origin == allowed || allowed == "*" {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
break
}
}
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}
}
// GzipMiddleware 压缩中间件
type gzipResponseWriter struct {
io.Writer
http.ResponseWriter
}
func (w gzipResponseWriter) Write(b []byte) (int, error) {
return w.Writer.Write(b)
}
func GzipMiddleware() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
next.ServeHTTP(w, r)
return
}
w.Header().Set("Content-Encoding", "gzip")
gz := gzip.NewWriter(w)
defer gz.Close()
gzw := gzipResponseWriter{Writer: gz, ResponseWriter: w}
next.ServeHTTP(gzw, r)
})
}
}
// ResponseMetricsMiddleware 响应指标中间件
type responseWriter struct {
http.ResponseWriter
statusCode int
size int
}
func (rw *responseWriter) WriteHeader(code int) {
rw.statusCode = code
rw.ResponseWriter.WriteHeader(code)
}
func (rw *responseWriter) Write(b []byte) (int, error) {
size, err := rw.ResponseWriter.Write(b)
rw.size += size
return size, err
}
func ResponseMetricsMiddleware(logger log.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
start := time.Now()
next.ServeHTTP(rw, r)
logger.Log(
"method", r.Method,
"path", r.URL.Path,
"status", rw.statusCode,
"size", rw.size,
"duration", time.Since(start),
)
})
}
}
---
02.gRPC拦截器
a.一元拦截器
a.功能说明
gRPC一元拦截器用于拦截一元RPC调用,在调用前后执行额外逻辑。拦截器可以访问请求响应,修改Context,或者提前返回错误。支持客户端拦截器和服务端拦截器,分别在客户端和服务端执行。拦截器链按顺序执行,可以组合多个拦截器。一元拦截器适合实现认证、日志、追踪、重试等功能。拦截器应该保持轻量级,避免阻塞RPC调用。
b.代码示例
---
// gRPC一元拦截器
package interceptor
import (
"context"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// UnaryServerLoggingInterceptor 服务端日志拦截器
func UnaryServerLoggingInterceptor(logger log.Logger) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
start := time.Now()
resp, err := handler(ctx, req)
logger.Log(
"method", info.FullMethod,
"duration", time.Since(start),
"error", err,
)
return resp, err
}
}
// UnaryServerAuthInterceptor 服务端认证拦截器
func UnaryServerAuthInterceptor(validator TokenValidator) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Error(codes.Unauthenticated, "missing metadata")
}
tokens := md.Get("authorization")
if len(tokens) == 0 {
return nil, status.Error(codes.Unauthenticated, "missing token")
}
claims, err := validator.Validate(tokens[0])
if err != nil {
return nil, status.Error(codes.Unauthenticated, "invalid token")
}
ctx = context.WithValue(ctx, "user_id", claims.UserID)
return handler(ctx, req)
}
}
// UnaryClientRetryInterceptor 客户端重试拦截器
func UnaryClientRetryInterceptor(maxRetries int) grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
var lastErr error
for i := 0; i <= maxRetries; i++ {
err := invoker(ctx, method, req, reply, cc, opts...)
if err == nil {
return nil
}
lastErr = err
if st, ok := status.FromError(err); ok {
if st.Code() == codes.Unavailable || st.Code() == codes.DeadlineExceeded {
if i < maxRetries {
time.Sleep(time.Duration(i+1) * 100 * time.Millisecond)
continue
}
}
}
return err
}
return lastErr
}
}
---
b.流式拦截器
a.功能说明
gRPC流式拦截器用于拦截流式RPC调用,包括客户端流、服务端流、双向流。流式拦截器需要包装Stream对象,拦截消息的发送和接收。可以在消息发送前后执行逻辑,如验证、转换、记录等。流式拦截器比一元拦截器更复杂,需要正确处理流的生命周期。支持客户端和服务端流式拦截器。流式拦截器适合实现流量控制、消息过滤、压缩等功能。
b.代码示例
---
// gRPC流式拦截器
package interceptor
import (
"context"
"google.golang.org/grpc"
)
// StreamServerLoggingInterceptor 服务端流式日志拦截器
func StreamServerLoggingInterceptor(logger log.Logger) grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
start := time.Now()
wrapped := &loggingServerStream{
ServerStream: ss,
logger: logger,
method: info.FullMethod,
}
err := handler(srv, wrapped)
logger.Log(
"method", info.FullMethod,
"duration", time.Since(start),
"messages_sent", wrapped.sentCount,
"messages_received", wrapped.recvCount,
"error", err,
)
return err
}
}
type loggingServerStream struct {
grpc.ServerStream
logger log.Logger
method string
sentCount int
recvCount int
}
func (s *loggingServerStream) SendMsg(m interface{}) error {
err := s.ServerStream.SendMsg(m)
if err == nil {
s.sentCount++
}
return err
}
func (s *loggingServerStream) RecvMsg(m interface{}) error {
err := s.ServerStream.RecvMsg(m)
if err == nil {
s.recvCount++
}
return err
}
// StreamClientInterceptor 客户端流式拦截器
func StreamClientInterceptor(logger log.Logger) grpc.StreamClientInterceptor {
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
start := time.Now()
cs, err := streamer(ctx, desc, cc, method, opts...)
if err != nil {
return nil, err
}
wrapped := &loggingClientStream{
ClientStream: cs,
logger: logger,
method: method,
start: start,
}
return wrapped, nil
}
}
type loggingClientStream struct {
grpc.ClientStream
logger log.Logger
method string
start time.Time
sentCount int
recvCount int
}
func (s *loggingClientStream) SendMsg(m interface{}) error {
err := s.ClientStream.SendMsg(m)
if err == nil {
s.sentCount++
}
return err
}
func (s *loggingClientStream) RecvMsg(m interface{}) error {
err := s.ClientStream.RecvMsg(m)
if err == nil {
s.recvCount++
}
return err
}
func (s *loggingClientStream) CloseSend() error {
err := s.ClientStream.CloseSend()
s.logger.Log(
"method", s.method,
"duration", time.Since(s.start),
"messages_sent", s.sentCount,
"messages_received", s.recvCount,
)
return err
}
---
03.中间件链
a.链式组合
a.功能说明
中间件链将多个中间件按顺序组合,形成处理管道。每个中间件都可以决定是否继续执行下一个中间件。中间件的执行顺序很重要,应该合理安排。通常认证在最前面,日志在最外层,业务逻辑在最里层。可以使用辅助函数简化中间件链的构建。中间件链应该是可配置的,可以根据需要启用或禁用某些中间件。合理的中间件链可以提高代码的可维护性和可测试性。
b.代码示例
---
// 中间件链组合
package middleware
import (
"net/http"
"github.com/go-kit/kit/endpoint"
)
// Chain 中间件链构建器
type Chain struct {
middlewares []func(http.Handler) http.Handler
}
func NewChain(middlewares ...func(http.Handler) http.Handler) *Chain {
return &Chain{middlewares: middlewares}
}
func (c *Chain) Then(h http.Handler) http.Handler {
for i := len(c.middlewares) - 1; i >= 0; i-- {
h = c.middlewares[i](h)
}
return h
}
func (c *Chain) Append(middlewares ...func(http.Handler) http.Handler) *Chain {
newChain := &Chain{
middlewares: make([]func(http.Handler) http.Handler, 0, len(c.middlewares)+len(middlewares)),
}
newChain.middlewares = append(newChain.middlewares, c.middlewares...)
newChain.middlewares = append(newChain.middlewares, middlewares...)
return newChain
}
// 使用示例
func MakeHTTPHandler(endpoints Endpoints, logger log.Logger) http.Handler {
chain := NewChain(
LoggingMiddleware(logger),
RecoveryMiddleware(logger),
CORSMiddleware([]string{"*"}),
RateLimitMiddleware(rate.NewLimiter(100, 200)),
AuthMiddleware(tokenValidator),
)
mux := http.NewServeMux()
mux.Handle("/users", chain.Then(makeUserHandler(endpoints)))
mux.Handle("/health", chain.Then(makeHealthHandler()))
return mux
}
// EndpointMiddlewareChain Endpoint中间件链
func EndpointMiddlewareChain(mws ...endpoint.Middleware) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
for i := len(mws) - 1; i >= 0; i-- {
next = mws[i](next)
}
return next
}
}
// 使用示例
func MakeEndpoint(svc Service, logger log.Logger) endpoint.Endpoint {
var ep endpoint.Endpoint
ep = MakeCreateUserEndpoint(svc)
ep = EndpointMiddlewareChain(
LoggingMiddleware(logger),
InstrumentingMiddleware(duration),
RateLimitMiddleware(limiter),
CircuitBreakerMiddleware(breaker),
)(ep)
return ep
}
---
b.条件中间件
a.功能说明
条件中间件根据请求特征决定是否执行某些逻辑,实现灵活的处理策略。可以根据路径、方法、头部等条件选择性地应用中间件。条件中间件可以实现A/B测试、灰度发布、特性开关等功能。应该使用配置或规则引擎管理条件,避免硬编码。条件中间件可以提高系统的灵活性,但也增加了复杂度,应该谨慎使用。条件判断应该高效,避免影响性能。
b.代码示例
---
// 条件中间件
package middleware
import (
"context"
"net/http"
"strings"
)
// ConditionalMiddleware 条件中间件包装器
func ConditionalMiddleware(condition func(*http.Request) bool, middleware func(http.Handler) http.Handler) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
wrapped := middleware(next)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if condition(r) {
wrapped.ServeHTTP(w, r)
} else {
next.ServeHTTP(w, r)
}
})
}
}
// PathPrefixCondition 路径前缀条件
func PathPrefixCondition(prefix string) func(*http.Request) bool {
return func(r *http.Request) bool {
return strings.HasPrefix(r.URL.Path, prefix)
}
}
// MethodCondition HTTP方法条件
func MethodCondition(methods ...string) func(*http.Request) bool {
methodSet := make(map[string]bool)
for _, m := range methods {
methodSet[m] = true
}
return func(r *http.Request) bool {
return methodSet[r.Method]
}
}
// HeaderCondition 头部条件
func HeaderCondition(key, value string) func(*http.Request) bool {
return func(r *http.Request) bool {
return r.Header.Get(key) == value
}
}
// 使用示例
func MakeConditionalHandler(endpoints Endpoints, logger log.Logger) http.Handler {
mux := http.NewServeMux()
// 只对API路径应用认证
authMiddleware := ConditionalMiddleware(
PathPrefixCondition("/api/"),
AuthMiddleware(tokenValidator),
)
// 只对POST/PUT/DELETE应用CSRF保护
csrfMiddleware := ConditionalMiddleware(
MethodCondition("POST", "PUT", "DELETE"),
CSRFMiddleware(csrfToken),
)
// 只对特定客户端启用新特性
featureMiddleware := ConditionalMiddleware(
HeaderCondition("X-Feature-Flag", "enabled"),
NewFeatureMiddleware(),
)
chain := NewChain(
LoggingMiddleware(logger),
authMiddleware,
csrfMiddleware,
featureMiddleware,
)
mux.Handle("/", chain.Then(makeHandler(endpoints)))
return mux
}
// EndpointConditionalMiddleware Endpoint条件中间件
func EndpointConditionalMiddleware(condition func(context.Context, interface{}) bool, middleware endpoint.Middleware) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
wrapped := middleware(next)
return func(ctx context.Context, request interface{}) (interface{}, error) {
if condition(ctx, request) {
return wrapped(ctx, request)
}
return next(ctx, request)
}
}
}
---
5 中间件系统
5.1 中间件概念
01.中间件定义
a.基本概念
a.功能说明
中间件是一个接收Endpoint并返回增强后Endpoint的函数,采用装饰器模式实现。中间件可以在Endpoint执行前后添加额外逻辑,如日志记录、性能监控、参数验证等。多个中间件可以链式组合,形成处理管道。中间件机制实现了横切关注点与业务逻辑的分离,提高了代码的可维护性和可复用性。中间件是go-kit架构的核心特性,使得功能扩展变得简单灵活。
b.代码示例
---
// 中间件基本定义
package middleware
import (
"context"
"time"
"github.com/go-kit/kit/endpoint"
)
// Middleware 中间件类型定义
type Middleware func(endpoint.Endpoint) endpoint.Endpoint
// LoggingMiddleware 日志中间件
func LoggingMiddleware(logger log.Logger) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
logger.Log(
"transport_error", err,
"took", time.Since(begin),
)
}(time.Now())
return next(ctx, request)
}
}
}
// ApplyMiddleware 应用多个中间件
func ApplyMiddleware(e endpoint.Endpoint, mw ...Middleware) endpoint.Endpoint {
for i := len(mw) - 1; i >= 0; i-- {
e = mw[i](e)
}
return e
}
// ChainMiddleware 链式组合中间件
func ChainMiddleware(outer Middleware, others ...Middleware) Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
for i := len(others) - 1; i >= 0; i-- {
next = others[i](next)
}
return outer(next)
}
}
// 使用示例
func MakeEndpoint(svc Service, logger log.Logger) endpoint.Endpoint {
var ep endpoint.Endpoint
ep = MakeCreateUserEndpoint(svc)
ep = ApplyMiddleware(ep,
LoggingMiddleware(logger),
InstrumentingMiddleware(duration),
RateLimitMiddleware(limiter),
)
return ep
}
---
b.中间件类型
a.功能说明
go-kit支持Service层中间件和Endpoint层中间件两种类型。Service中间件包装Service接口,在业务方法执行前后添加逻辑。Endpoint中间件包装Endpoint函数,在请求处理前后添加逻辑。两种中间件适用于不同场景,Service中间件更接近业务逻辑,Endpoint中间件更接近传输层。Service中间件可以访问业务参数,Endpoint中间件处理通用的请求响应。合理选择中间件类型可以优化性能和功能。
b.代码示例
---
// Service层中间件
package middleware
import (
"context"
"time"
)
// ServiceMiddleware Service中间件类型
type ServiceMiddleware func(Service) Service
// ServiceLoggingMiddleware Service日志中间件
func ServiceLoggingMiddleware(logger log.Logger) ServiceMiddleware {
return func(next Service) Service {
return &loggingMiddleware{logger, next}
}
}
type loggingMiddleware struct {
logger log.Logger
next Service
}
func (mw loggingMiddleware) CreateUser(ctx context.Context, username, email string) (User, error) {
defer func(begin time.Time) {
mw.logger.Log(
"method", "CreateUser",
"username", username,
"took", time.Since(begin),
)
}(time.Now())
return mw.next.CreateUser(ctx, username, email)
}
func (mw loggingMiddleware) GetUser(ctx context.Context, id string) (User, error) {
defer func(begin time.Time) {
mw.logger.Log(
"method", "GetUser",
"id", id,
"took", time.Since(begin),
)
}(time.Now())
return mw.next.GetUser(ctx, id)
}
// Endpoint层中间件
func EndpointLoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
logger.Log(
"request", request,
"took", time.Since(begin),
)
}(time.Now())
return next(ctx, request)
}
}
}
// 组合使用
func NewService(repo Repository, logger log.Logger) Service {
var svc Service
svc = NewBasicService(repo)
svc = ServiceLoggingMiddleware(logger)(svc)
return svc
}
---
02.中间件模式
a.装饰器模式
a.功能说明
中间件使用装饰器模式,通过包装原始Endpoint添加新功能。装饰器模式允许动态地给对象添加职责,比继承更灵活。每个中间件都是一个装饰器,可以独立开发和测试。装饰器可以任意组合,形成不同的功能链。装饰器模式符合开闭原则,对扩展开放,对修改关闭。中间件的装饰器实现使得功能扩展变得简单,不需要修改原有代码。
b.代码示例
---
// 装饰器模式实现
package middleware
import (
"context"
"fmt"
"time"
)
// 基础Endpoint
func BasicEndpoint(ctx context.Context, request interface{}) (interface{}, error) {
return "response", nil
}
// 日志装饰器
func LoggingDecorator(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
logger.Log("msg", "calling endpoint")
response, err := next(ctx, request)
logger.Log("msg", "called endpoint")
return response, err
}
}
}
// 计时装饰器
func TimingDecorator() endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
start := time.Now()
response, err := next(ctx, request)
duration := time.Since(start)
fmt.Printf("Took: %v\n", duration)
return response, err
}
}
}
// 错误处理装饰器
func ErrorHandlingDecorator() endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic recovered: %v", r)
response = nil
}
}()
return next(ctx, request)
}
}
}
// 组合装饰器
func DecoratedEndpoint(logger log.Logger) endpoint.Endpoint {
ep := endpoint.Endpoint(BasicEndpoint)
ep = ErrorHandlingDecorator()(ep)
ep = TimingDecorator()(ep)
ep = LoggingDecorator(logger)(ep)
return ep
}
// 装饰器工厂
type DecoratorFactory struct {
decorators []endpoint.Middleware
}
func NewDecoratorFactory() *DecoratorFactory {
return &DecoratorFactory{
decorators: make([]endpoint.Middleware, 0),
}
}
func (f *DecoratorFactory) Add(decorator endpoint.Middleware) *DecoratorFactory {
f.decorators = append(f.decorators, decorator)
return f
}
func (f *DecoratorFactory) Decorate(ep endpoint.Endpoint) endpoint.Endpoint {
for i := len(f.decorators) - 1; i >= 0; i-- {
ep = f.decorators[i](ep)
}
return ep
}
---
b.责任链模式
a.功能说明
中间件链形成责任链模式,每个中间件处理请求的一部分,然后传递给下一个中间件。责任链模式将请求的发送者和接收者解耦,多个对象都有机会处理请求。每个中间件可以决定是否继续传递请求,或者提前返回。责任链的顺序很重要,不同的顺序会产生不同的效果。责任链模式使得中间件的添加和删除变得容易,不影响其他中间件。中间件链的责任链实现提供了灵活的请求处理机制。
b.代码示例
---
// 责任链模式实现
package middleware
import (
"context"
"errors"
)
// Handler 处理器接口
type Handler interface {
Handle(ctx context.Context, request interface{}) (interface{}, error)
SetNext(handler Handler)
}
// BaseHandler 基础处理器
type BaseHandler struct {
next Handler
}
func (h *BaseHandler) SetNext(handler Handler) {
h.next = handler
}
// AuthHandler 认证处理器
type AuthHandler struct {
BaseHandler
validator TokenValidator
}
func (h *AuthHandler) Handle(ctx context.Context, request interface{}) (interface{}, error) {
token, ok := ctx.Value("token").(string)
if !ok || token == "" {
return nil, errors.New("missing token")
}
if err := h.validator.Validate(token); err != nil {
return nil, errors.New("invalid token")
}
if h.next != nil {
return h.next.Handle(ctx, request)
}
return nil, nil
}
// ValidationHandler 验证处理器
type ValidationHandler struct {
BaseHandler
}
func (h *ValidationHandler) Handle(ctx context.Context, request interface{}) (interface{}, error) {
if err := validateRequest(request); err != nil {
return nil, err
}
if h.next != nil {
return h.next.Handle(ctx, request)
}
return nil, nil
}
// BusinessHandler 业务处理器
type BusinessHandler struct {
BaseHandler
service Service
}
func (h *BusinessHandler) Handle(ctx context.Context, request interface{}) (interface{}, error) {
return h.service.Process(ctx, request)
}
// 构建责任链
func BuildHandlerChain(validator TokenValidator, service Service) Handler {
business := &BusinessHandler{service: service}
validation := &ValidationHandler{}
auth := &AuthHandler{validator: validator}
auth.SetNext(validation)
validation.SetNext(business)
return auth
}
// 转换为Endpoint
func HandlerToEndpoint(handler Handler) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
return handler.Handle(ctx, request)
}
}
---
03.中间件组合
a.顺序组合
a.功能说明
中间件的执行顺序对最终效果有重要影响,需要合理安排。通常认证中间件在最前面,确保未认证请求被拒绝。日志中间件在最外层,记录完整的请求处理过程。限流和熔断中间件在业务逻辑之前,保护系统资源。指标收集中间件在外层,统计所有请求。错误处理中间件在最外层,捕获所有异常。合理的中间件顺序可以提高系统的性能和可靠性。
b.代码示例
---
// 中间件顺序组合
package middleware
import (
"context"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/ratelimit"
"golang.org/x/time/rate"
)
// MakeEndpointWithMiddleware 创建带中间件的Endpoint
func MakeEndpointWithMiddleware(
svc Service,
logger log.Logger,
duration metrics.Histogram,
limiter *rate.Limiter,
) endpoint.Endpoint {
var ep endpoint.Endpoint
ep = MakeCreateUserEndpoint(svc)
// 按照从内到外的顺序应用中间件
// 1. 业务逻辑 (最内层)
// 2. 参数验证
ep = ValidationMiddleware()(ep)
// 3. 限流保护
ep = ratelimit.NewErroringLimiter(limiter)(ep)
// 4. 熔断保护
ep = CircuitBreakerMiddleware()(ep)
// 5. 性能指标
ep = InstrumentingMiddleware(duration)(ep)
// 6. 日志记录
ep = LoggingMiddleware(logger)(ep)
// 7. 错误恢复 (最外层)
ep = RecoveryMiddleware(logger)(ep)
return ep
}
// MiddlewareChain 中间件链构建器
type MiddlewareChain struct {
middlewares []endpoint.Middleware
}
func NewMiddlewareChain() *MiddlewareChain {
return &MiddlewareChain{
middlewares: make([]endpoint.Middleware, 0),
}
}
func (c *MiddlewareChain) Append(mw endpoint.Middleware) *MiddlewareChain {
c.middlewares = append(c.middlewares, mw)
return c
}
func (c *MiddlewareChain) Then(ep endpoint.Endpoint) endpoint.Endpoint {
for i := len(c.middlewares) - 1; i >= 0; i-- {
ep = c.middlewares[i](ep)
}
return ep
}
// 使用链式构建器
func MakeEndpointWithChain(svc Service, logger log.Logger) endpoint.Endpoint {
chain := NewMiddlewareChain().
Append(RecoveryMiddleware(logger)).
Append(LoggingMiddleware(logger)).
Append(InstrumentingMiddleware(duration)).
Append(CircuitBreakerMiddleware()).
Append(RateLimitMiddleware(limiter)).
Append(ValidationMiddleware())
return chain.Then(MakeCreateUserEndpoint(svc))
}
// 预定义中间件组合
func StandardMiddleware(logger log.Logger) []endpoint.Middleware {
return []endpoint.Middleware{
RecoveryMiddleware(logger),
LoggingMiddleware(logger),
InstrumentingMiddleware(duration),
}
}
func SecurityMiddleware() []endpoint.Middleware {
return []endpoint.Middleware{
AuthenticationMiddleware(),
AuthorizationMiddleware(),
RateLimitMiddleware(limiter),
}
}
---
b.条件组合
a.功能说明
条件组合允许根据运行时条件动态选择中间件,实现灵活的处理策略。可以根据请求类型、用户角色、环境配置等条件启用或禁用中间件。条件组合支持A/B测试、灰度发布、特性开关等场景。应该使用配置中心管理条件规则,避免硬编码。条件判断应该高效,避免影响性能。条件组合增加了系统的灵活性,但也增加了复杂度,需要谨慎使用。
b.代码示例
---
// 条件中间件组合
package middleware
import (
"context"
)
// ConditionalMiddleware 条件中间件
func ConditionalMiddleware(
condition func(context.Context, interface{}) bool,
middleware endpoint.Middleware,
) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
wrapped := middleware(next)
return func(ctx context.Context, request interface{}) (interface{}, error) {
if condition(ctx, request) {
return wrapped(ctx, request)
}
return next(ctx, request)
}
}
}
// 条件函数示例
func IsAdminUser(ctx context.Context, request interface{}) bool {
role, ok := ctx.Value("user_role").(string)
return ok && role == "admin"
}
func IsProductionEnv(ctx context.Context, request interface{}) bool {
env := ctx.Value("environment").(string)
return env == "production"
}
func HasFeatureFlag(flag string) func(context.Context, interface{}) bool {
return func(ctx context.Context, request interface{}) bool {
flags, ok := ctx.Value("feature_flags").(map[string]bool)
return ok && flags[flag]
}
}
// 使用条件中间件
func MakeConditionalEndpoint(svc Service, logger log.Logger) endpoint.Endpoint {
ep := MakeCreateUserEndpoint(svc)
// 只对管理员启用详细日志
ep = ConditionalMiddleware(
IsAdminUser,
DetailedLoggingMiddleware(logger),
)(ep)
// 只在生产环境启用限流
ep = ConditionalMiddleware(
IsProductionEnv,
RateLimitMiddleware(limiter),
)(ep)
// 根据特性开关启用新功能
ep = ConditionalMiddleware(
HasFeatureFlag("new_validation"),
NewValidationMiddleware(),
)(ep)
return ep
}
// 动态中间件选择器
type MiddlewareSelector struct {
rules map[string]endpoint.Middleware
}
func NewMiddlewareSelector() *MiddlewareSelector {
return &MiddlewareSelector{
rules: make(map[string]endpoint.Middleware),
}
}
func (s *MiddlewareSelector) Register(name string, mw endpoint.Middleware) {
s.rules[name] = mw
}
func (s *MiddlewareSelector) Select(ctx context.Context) endpoint.Middleware {
strategy, ok := ctx.Value("middleware_strategy").(string)
if !ok {
strategy = "default"
}
if mw, exists := s.rules[strategy]; exists {
return mw
}
return func(next endpoint.Endpoint) endpoint.Endpoint {
return next
}
}
---
5.2 日志中间件
01.基础日志
a.请求日志
a.功能说明
请求日志中间件记录每个请求的基本信息,包括请求参数、响应结果、执行时间等。日志中间件应该在最外层,确保记录所有请求。可以配置日志级别,控制输出详细程度。日志应该包含请求ID,便于追踪。支持结构化日志,便于分析和检索。日志中间件不应该影响业务逻辑,即使日志失败也要继续执行。
b.代码示例
---
// 请求日志中间件
package middleware
import (
"context"
"time"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/log"
)
func LoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
defer func(begin time.Time) {
logger.Log(
"request", request,
"response", response,
"err", err,
"took", time.Since(begin),
)
}(time.Now())
return next(ctx, request)
}
}
}
// 带请求ID的日志中间件
func RequestIDLoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
requestID := ctx.Value("request_id")
logger := log.With(logger, "request_id", requestID)
begin := time.Now()
response, err := next(ctx, request)
logger.Log(
"method", ctx.Value("method"),
"took", time.Since(begin),
"err", err,
)
return response, err
}
}
}
// 分级日志中间件
func LeveledLoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
begin := time.Now()
response, err := next(ctx, request)
duration := time.Since(begin)
if err != nil {
logger.Log("level", "error", "err", err, "took", duration)
} else if duration > 1*time.Second {
logger.Log("level", "warn", "msg", "slow request", "took", duration)
} else {
logger.Log("level", "info", "took", duration)
}
return response, err
}
}
}
---
b.错误日志
a.功能说明
错误日志中间件专门记录错误信息,包括错误类型、错误堆栈、上下文信息等。错误日志应该包含足够的信息用于问题诊断。可以集成错误追踪系统,如Sentry。错误日志应该区分业务错误和系统错误。支持错误聚合和告警。错误日志不应该泄露敏感信息。
b.代码示例
---
// 错误日志中间件
package middleware
import (
"context"
"fmt"
"runtime/debug"
)
func ErrorLoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
response, err := next(ctx, request)
if err != nil {
logger.Log(
"level", "error",
"error", err,
"request", fmt.Sprintf("%+v", request),
"stack", string(debug.Stack()),
)
}
return response, err
}
}
}
// 错误分类日志中间件
func CategorizedErrorLoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
response, err := next(ctx, request)
if err != nil {
category := categorizeError(err)
logger.Log(
"level", "error",
"category", category,
"error", err,
"user_id", ctx.Value("user_id"),
)
}
return response, err
}
}
}
func categorizeError(err error) string {
switch err.(type) {
case *ValidationError:
return "validation"
case *AuthenticationError:
return "authentication"
case *AuthorizationError:
return "authorization"
case *DatabaseError:
return "database"
default:
return "unknown"
}
}
// Panic恢复日志中间件
func PanicLoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
defer func() {
if r := recover(); r != nil {
logger.Log(
"level", "critical",
"panic", r,
"stack", string(debug.Stack()),
"request", request,
)
err = fmt.Errorf("panic recovered: %v", r)
}
}()
return next(ctx, request)
}
}
}
---
02.结构化日志
a.字段日志
a.功能说明
结构化日志使用键值对格式,便于机器解析和分析。字段日志中间件添加标准字段,如时间戳、服务名、版本号等。支持自定义字段,记录业务相关信息。字段应该有统一的命名规范。结构化日志便于日志聚合和检索。可以与ELK、Splunk等日志系统集成。
b.代码示例
---
// 结构化日志中间件
package middleware
import (
"context"
"time"
)
func StructuredLoggingMiddleware(logger log.Logger, serviceName, version string) endpoint.Middleware {
logger = log.With(logger,
"service", serviceName,
"version", version,
)
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
begin := time.Now()
contextLogger := log.With(logger,
"timestamp", begin.Format(time.RFC3339),
"request_id", ctx.Value("request_id"),
"user_id", ctx.Value("user_id"),
"method", ctx.Value("method"),
)
response, err := next(ctx, request)
contextLogger.Log(
"duration_ms", time.Since(begin).Milliseconds(),
"success", err == nil,
"error", err,
)
return response, err
}
}
}
// 业务字段日志中间件
type LogFields map[string]interface{}
func BusinessLoggingMiddleware(logger log.Logger, extractor func(interface{}) LogFields) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
fields := extractor(request)
contextLogger := logger
for k, v := range fields {
contextLogger = log.With(contextLogger, k, v)
}
begin := time.Now()
response, err := next(ctx, request)
contextLogger.Log(
"took", time.Since(begin),
"err", err,
)
return response, err
}
}
}
// 使用示例
func extractUserFields(request interface{}) LogFields {
if req, ok := request.(CreateUserRequest); ok {
return LogFields{
"username": req.Username,
"email": req.Email,
"action": "create_user",
}
}
return LogFields{}
}
---
b.上下文日志
a.功能说明
上下文日志从Context中提取信息并记录,如用户ID、会话ID、追踪ID等。上下文日志便于关联多个请求,追踪用户行为。应该在请求入口处设置上下文信息。上下文信息应该在整个调用链中传递。支持分布式追踪,关联跨服务的请求。上下文日志不应该包含敏感信息。
b.代码示例
---
// 上下文日志中间件
package middleware
import (
"context"
)
type contextKey string
const (
RequestIDKey contextKey = "request_id"
UserIDKey contextKey = "user_id"
SessionIDKey contextKey = "session_id"
TraceIDKey contextKey = "trace_id"
SpanIDKey contextKey = "span_id"
)
func ContextLoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
contextLogger := enrichLoggerFromContext(logger, ctx)
begin := time.Now()
response, err := next(ctx, request)
contextLogger.Log(
"took", time.Since(begin),
"err", err,
)
return response, err
}
}
}
func enrichLoggerFromContext(logger log.Logger, ctx context.Context) log.Logger {
if requestID := ctx.Value(RequestIDKey); requestID != nil {
logger = log.With(logger, "request_id", requestID)
}
if userID := ctx.Value(UserIDKey); userID != nil {
logger = log.With(logger, "user_id", userID)
}
if sessionID := ctx.Value(SessionIDKey); sessionID != nil {
logger = log.With(logger, "session_id", sessionID)
}
if traceID := ctx.Value(TraceIDKey); traceID != nil {
logger = log.With(logger, "trace_id", traceID)
}
if spanID := ctx.Value(SpanIDKey); spanID != nil {
logger = log.With(logger, "span_id", spanID)
}
return logger
}
// 链路追踪日志中间件
func TracingLoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
traceID := ctx.Value(TraceIDKey)
spanID := generateSpanID()
ctx = context.WithValue(ctx, SpanIDKey, spanID)
logger := log.With(logger,
"trace_id", traceID,
"span_id", spanID,
)
logger.Log("event", "span_start")
response, err := next(ctx, request)
logger.Log("event", "span_end", "err", err)
return response, err
}
}
}
func generateSpanID() string {
return fmt.Sprintf("%d", time.Now().UnixNano())
}
---
03.日志采样
a.采样策略
a.功能说明
日志采样减少日志量,降低存储和处理成本。采样策略包括固定比例采样、自适应采样、错误全采样等。高频请求可以降低采样率,低频请求保持高采样率。错误请求应该全部记录。采样决策应该在请求入口处做出,保证一致性。采样信息应该在上下文中传递。
b.代码示例
---
// 日志采样中间件
package middleware
import (
"context"
"math/rand"
)
// 固定比例采样
func SamplingLoggingMiddleware(logger log.Logger, sampleRate float64) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
shouldSample := rand.Float64() < sampleRate
if !shouldSample {
return next(ctx, request)
}
begin := time.Now()
response, err := next(ctx, request)
logger.Log(
"sampled", true,
"took", time.Since(begin),
"err", err,
)
return response, err
}
}
}
// 自适应采样
type AdaptiveSampler struct {
baseRate float64
errorRate float64
slowRate float64
slowThreshold time.Duration
}
func NewAdaptiveSampler(baseRate, errorRate, slowRate float64, slowThreshold time.Duration) *AdaptiveSampler {
return &AdaptiveSampler{
baseRate: baseRate,
errorRate: errorRate,
slowRate: slowRate,
slowThreshold: slowThreshold,
}
}
func (s *AdaptiveSampler) ShouldSample(err error, duration time.Duration) bool {
if err != nil {
return rand.Float64() < s.errorRate
}
if duration > s.slowThreshold {
return rand.Float64() < s.slowRate
}
return rand.Float64() < s.baseRate
}
func AdaptiveSamplingMiddleware(logger log.Logger, sampler *AdaptiveSampler) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
begin := time.Now()
response, err := next(ctx, request)
duration := time.Since(begin)
if sampler.ShouldSample(err, duration) {
logger.Log(
"took", duration,
"err", err,
"sampled", true,
)
}
return response, err
}
}
}
// 基于负载的采样
type LoadBasedSampler struct {
minRate float64
maxRate float64
loadFunc func() float64
}
func (s *LoadBasedSampler) GetSampleRate() float64 {
load := s.loadFunc()
if load < 0.5 {
return s.maxRate
} else if load > 0.9 {
return s.minRate
}
return s.minRate + (s.maxRate-s.minRate)*(0.9-load)/0.4
}
func LoadBasedSamplingMiddleware(logger log.Logger, sampler *LoadBasedSampler) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
sampleRate := sampler.GetSampleRate()
shouldSample := rand.Float64() < sampleRate
begin := time.Now()
response, err := next(ctx, request)
if shouldSample {
logger.Log(
"sample_rate", sampleRate,
"took", time.Since(begin),
"err", err,
)
}
return response, err
}
}
}
---
b.采样配置
a.功能说明
采样配置支持动态调整采样率,适应不同的运行环境。可以为不同的端点配置不同的采样率。支持基于规则的采样,如特定用户、特定时间段等。采样配置应该从配置中心读取,支持热更新。采样决策应该高效,避免影响性能。采样配置应该有合理的默认值。
b.代码示例
---
// 采样配置中间件
package middleware
import (
"context"
"sync"
)
type SamplingConfig struct {
mu sync.RWMutex
defaultRate float64
endpointRates map[string]float64
userRates map[string]float64
}
func NewSamplingConfig(defaultRate float64) *SamplingConfig {
return &SamplingConfig{
defaultRate: defaultRate,
endpointRates: make(map[string]float64),
userRates: make(map[string]float64),
}
}
func (c *SamplingConfig) SetEndpointRate(endpoint string, rate float64) {
c.mu.Lock()
defer c.mu.Unlock()
c.endpointRates[endpoint] = rate
}
func (c *SamplingConfig) SetUserRate(userID string, rate float64) {
c.mu.Lock()
defer c.mu.Unlock()
c.userRates[userID] = rate
}
func (c *SamplingConfig) GetRate(ctx context.Context) float64 {
c.mu.RLock()
defer c.mu.RUnlock()
if userID, ok := ctx.Value("user_id").(string); ok {
if rate, exists := c.userRates[userID]; exists {
return rate
}
}
if endpoint, ok := ctx.Value("endpoint").(string); ok {
if rate, exists := c.endpointRates[endpoint]; exists {
return rate
}
}
return c.defaultRate
}
func ConfigurableSamplingMiddleware(logger log.Logger, config *SamplingConfig) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
sampleRate := config.GetRate(ctx)
shouldSample := rand.Float64() < sampleRate
begin := time.Now()
response, err := next(ctx, request)
if shouldSample {
logger.Log(
"sample_rate", sampleRate,
"took", time.Since(begin),
"err", err,
)
}
return response, err
}
}
}
// 规则采样中间件
type SamplingRule struct {
Condition func(context.Context, interface{}) bool
Rate float64
}
type RuleBasedSampler struct {
rules []SamplingRule
defaultRate float64
}
func (s *RuleBasedSampler) GetRate(ctx context.Context, request interface{}) float64 {
for _, rule := range s.rules {
if rule.Condition(ctx, request) {
return rule.Rate
}
}
return s.defaultRate
}
func RuleBasedSamplingMiddleware(logger log.Logger, sampler *RuleBasedSampler) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
sampleRate := sampler.GetRate(ctx, request)
shouldSample := rand.Float64() < sampleRate
begin := time.Now()
response, err := next(ctx, request)
if shouldSample {
logger.Log(
"took", time.Since(begin),
"err", err,
)
}
return response, err
}
}
}
---
5.3 监控中间件
01.性能指标
a.延迟监控
a.功能说明
延迟监控中间件记录请求的响应时间,包括最小值、最大值、平均值、百分位数等。延迟指标是衡量系统性能的关键指标。应该区分不同端点的延迟,便于定位性能瓶颈。支持直方图统计,分析延迟分布。可以设置延迟阈值告警。延迟监控应该轻量级,避免影响性能。
b.代码示例
---
// 延迟监控中间件
package middleware
import (
"context"
"time"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/kit/metrics"
)
func LatencyMiddleware(histogram metrics.Histogram) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
histogram.Observe(time.Since(begin).Seconds())
}(time.Now())
return next(ctx, request)
}
}
}
// 分层延迟监控
func LayeredLatencyMiddleware(histograms map[string]metrics.Histogram) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
endpoint := ctx.Value("endpoint").(string)
histogram := histograms[endpoint]
defer func(begin time.Time) {
duration := time.Since(begin)
histogram.Observe(duration.Seconds())
// 记录慢请求
if duration > 1*time.Second {
histogram.With("slow", "true").Observe(duration.Seconds())
}
}(time.Now())
return next(ctx, request)
}
}
}
// 百分位延迟监控
type PercentileTracker struct {
durations []time.Duration
mu sync.Mutex
}
func (t *PercentileTracker) Record(d time.Duration) {
t.mu.Lock()
defer t.mu.Unlock()
t.durations = append(t.durations, d)
if len(t.durations) > 1000 {
t.durations = t.durations[1:]
}
}
func (t *PercentileTracker) Percentile(p float64) time.Duration {
t.mu.Lock()
defer t.mu.Unlock()
if len(t.durations) == 0 {
return 0
}
sorted := make([]time.Duration, len(t.durations))
copy(sorted, t.durations)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i] < sorted[j]
})
idx := int(float64(len(sorted)) * p)
return sorted[idx]
}
func PercentileLatencyMiddleware(tracker *PercentileTracker) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
begin := time.Now()
response, err := next(ctx, request)
tracker.Record(time.Since(begin))
return response, err
}
}
}
---
b.吞吐量监控
a.功能说明
吞吐量监控记录单位时间内处理的请求数量,反映系统的处理能力。吞吐量指标包括QPS、TPS等。应该区分成功请求和失败请求的吞吐量。支持实时统计和历史趋势分析。可以设置吞吐量阈值告警。吞吐量监控帮助评估系统容量和扩展需求。
b.代码示例
---
// 吞吐量监控中间件
package middleware
import (
"context"
"sync/atomic"
"time"
)
type ThroughputCounter struct {
total uint64
success uint64
failure uint64
window time.Duration
}
func NewThroughputCounter(window time.Duration) *ThroughputCounter {
return &ThroughputCounter{window: window}
}
func (c *ThroughputCounter) Increment(success bool) {
atomic.AddUint64(&c.total, 1)
if success {
atomic.AddUint64(&c.success, 1)
} else {
atomic.AddUint64(&c.failure, 1)
}
}
func (c *ThroughputCounter) QPS() float64 {
total := atomic.LoadUint64(&c.total)
return float64(total) / c.window.Seconds()
}
func ThroughputMiddleware(counter *ThroughputCounter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
response, err := next(ctx, request)
counter.Increment(err == nil)
return response, err
}
}
}
// 滑动窗口吞吐量监控
type SlidingWindowCounter struct {
buckets []uint64
bucketSize time.Duration
numBuckets int
current int
mu sync.Mutex
}
func NewSlidingWindowCounter(window time.Duration, numBuckets int) *SlidingWindowCounter {
return &SlidingWindowCounter{
buckets: make([]uint64, numBuckets),
bucketSize: window / time.Duration(numBuckets),
numBuckets: numBuckets,
}
}
func (c *SlidingWindowCounter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.buckets[c.current]++
}
func (c *SlidingWindowCounter) Rotate() {
c.mu.Lock()
defer c.mu.Unlock()
c.current = (c.current + 1) % c.numBuckets
c.buckets[c.current] = 0
}
func (c *SlidingWindowCounter) Total() uint64 {
c.mu.Lock()
defer c.mu.Unlock()
var total uint64
for _, count := range c.buckets {
total += count
}
return total
}
func SlidingWindowThroughputMiddleware(counter *SlidingWindowCounter) endpoint.Middleware {
go func() {
ticker := time.NewTicker(counter.bucketSize)
for range ticker.C {
counter.Rotate()
}
}()
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
counter.Increment()
return next(ctx, request)
}
}
}
---
02.错误监控
a.错误率统计
a.功能说明
错误率监控统计请求失败的比例,反映系统的稳定性。错误率指标包括总错误率、各类错误的占比等。应该区分不同类型的错误,如业务错误、系统错误、网络错误等。支持错误趋势分析和异常检测。可以设置错误率阈值告警。错误率监控帮助及时发现和定位问题。
b.代码示例
---
// 错误率监控中间件
package middleware
import (
"context"
"sync/atomic"
)
type ErrorRateCounter struct {
total uint64
errors uint64
byType map[string]*uint64
mu sync.RWMutex
}
func NewErrorRateCounter() *ErrorRateCounter {
return &ErrorRateCounter{
byType: make(map[string]*uint64),
}
}
func (c *ErrorRateCounter) Increment(err error) {
atomic.AddUint64(&c.total, 1)
if err != nil {
atomic.AddUint64(&c.errors, 1)
errType := getErrorType(err)
c.mu.Lock()
if _, exists := c.byType[errType]; !exists {
var count uint64
c.byType[errType] = &count
}
c.mu.Unlock()
c.mu.RLock()
atomic.AddUint64(c.byType[errType], 1)
c.mu.RUnlock()
}
}
func (c *ErrorRateCounter) ErrorRate() float64 {
total := atomic.LoadUint64(&c.total)
if total == 0 {
return 0
}
errors := atomic.LoadUint64(&c.errors)
return float64(errors) / float64(total)
}
func getErrorType(err error) string {
switch err.(type) {
case *ValidationError:
return "validation"
case *AuthError:
return "auth"
case *DatabaseError:
return "database"
default:
return "unknown"
}
}
func ErrorRateMiddleware(counter *ErrorRateCounter, gauge metrics.Gauge) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
response, err := next(ctx, request)
counter.Increment(err)
gauge.Set(counter.ErrorRate())
return response, err
}
}
}
// 错误分类监控
func CategorizedErrorMiddleware(counters map[string]metrics.Counter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
response, err := next(ctx, request)
if err != nil {
category := getErrorType(err)
if counter, exists := counters[category]; exists {
counter.Add(1)
}
counters["total_errors"].Add(1)
}
counters["total_requests"].Add(1)
return response, err
}
}
}
---
b.错误追踪
a.功能说明
错误追踪记录错误的详细信息,��括错误堆栈、上下文、发生时间等。错误追踪帮助快速定位和修复问题。支持错误聚合,识别相同的错误。可以集成Sentry等错误追踪平台。应该记录足够的上下文信息,但不泄露敏感数据。错误追踪应该异步处理,避免影响请求性能。
b.代码示例
---
// 错误追踪中间件
package middleware
import (
"context"
"fmt"
"runtime/debug"
"time"
)
type ErrorTracker interface {
Track(ctx context.Context, err error, stack []byte)
}
type ErrorEvent struct {
Error error
Stack string
Timestamp time.Time
RequestID string
UserID string
Endpoint string
}
type InMemoryErrorTracker struct {
events []ErrorEvent
mu sync.Mutex
maxSize int
}
func NewInMemoryErrorTracker(maxSize int) *InMemoryErrorTracker {
return &InMemoryErrorTracker{
events: make([]ErrorEvent, 0, maxSize),
maxSize: maxSize,
}
}
func (t *InMemoryErrorTracker) Track(ctx context.Context, err error, stack []byte) {
t.mu.Lock()
defer t.mu.Unlock()
event := ErrorEvent{
Error: err,
Stack: string(stack),
Timestamp: time.Now(),
RequestID: getStringFromContext(ctx, "request_id"),
UserID: getStringFromContext(ctx, "user_id"),
Endpoint: getStringFromContext(ctx, "endpoint"),
}
t.events = append(t.events, event)
if len(t.events) > t.maxSize {
t.events = t.events[1:]
}
}
func (t *InMemoryErrorTracker) GetRecentErrors(n int) []ErrorEvent {
t.mu.Lock()
defer t.mu.Unlock()
if n > len(t.events) {
n = len(t.events)
}
return t.events[len(t.events)-n:]
}
func getStringFromContext(ctx context.Context, key string) string {
if val := ctx.Value(key); val != nil {
return fmt.Sprintf("%v", val)
}
return ""
}
func ErrorTrackingMiddleware(tracker ErrorTracker) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
response, err := next(ctx, request)
if err != nil {
go tracker.Track(ctx, err, debug.Stack())
}
return response, err
}
}
}
// Sentry集成
type SentryTracker struct {
client *sentry.Client
}
func (t *SentryTracker) Track(ctx context.Context, err error, stack []byte) {
t.client.CaptureException(err, &sentry.EventHint{
Context: ctx,
}, &sentry.Scope{
Extra: map[string]interface{}{
"stack": string(stack),
"request_id": ctx.Value("request_id"),
},
})
}
---
03.资源监控
a.并发监控
a.功能说明
并发监控跟踪当前正在处理的请求数量,反映系统的负载情况。并发指标帮助识别系统瓶颈和容量限制。可以设置最大并发数,保护系统资源。支持并发趋势分析,预测负载变化。并发监控应该轻量级,使用原子操作。可以结合限流中间件,控制并发数量。
b.代码示例
---
// 并发监控中间件
package middleware
import (
"context"
"sync/atomic"
)
type ConcurrencyMonitor struct {
current uint64
max uint64
gauge metrics.Gauge
}
func NewConcurrencyMonitor(gauge metrics.Gauge) *ConcurrencyMonitor {
return &ConcurrencyMonitor{gauge: gauge}
}
func (m *ConcurrencyMonitor) Increment() {
current := atomic.AddUint64(&m.current, 1)
m.gauge.Set(float64(current))
// 更新最大值
for {
max := atomic.LoadUint64(&m.max)
if current <= max {
break
}
if atomic.CompareAndSwapUint64(&m.max, max, current) {
break
}
}
}
func (m *ConcurrencyMonitor) Decrement() {
current := atomic.AddUint64(&m.current, ^uint64(0))
m.gauge.Set(float64(current))
}
func (m *ConcurrencyMonitor) Current() uint64 {
return atomic.LoadUint64(&m.current)
}
func (m *ConcurrencyMonitor) Max() uint64 {
return atomic.LoadUint64(&m.max)
}
func ConcurrencyMiddleware(monitor *ConcurrencyMonitor) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
monitor.Increment()
defer monitor.Decrement()
return next(ctx, request)
}
}
}
// 带限制的并发监控
type BoundedConcurrencyMonitor struct {
current uint64
max uint64
limit uint64
}
func NewBoundedConcurrencyMonitor(limit uint64) *BoundedConcurrencyMonitor {
return &BoundedConcurrencyMonitor{limit: limit}
}
func (m *BoundedConcurrencyMonitor) TryIncrement() bool {
for {
current := atomic.LoadUint64(&m.current)
if current >= m.limit {
return false
}
if atomic.CompareAndSwapUint64(&m.current, current, current+1) {
return true
}
}
}
func (m *BoundedConcurrencyMonitor) Decrement() {
atomic.AddUint64(&m.current, ^uint64(0))
}
func BoundedConcurrencyMiddleware(monitor *BoundedConcurrencyMonitor) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if !monitor.TryIncrement() {
return nil, errors.New("too many concurrent requests")
}
defer monitor.Decrement()
return next(ctx, request)
}
}
}
---
b.内存监控
a.功能说明
内存监控跟踪请求处理过程中的内存使用情况,帮助识别内存泄漏和优化内存使用。内存指标包括堆内存、栈内存、GC次数等。应该定期采样,避免频繁监控影响性能。可以设置内存阈值告警。内存监控结合性能分析工具,定位内存问题。支持内存趋势分析,预测内存需求。
b.代码示例
---
// 内存监控中间件
package middleware
import (
"context"
"runtime"
)
type MemoryMonitor struct {
heapGauge metrics.Gauge
gcGauge metrics.Gauge
goroutineGauge metrics.Gauge
}
func NewMemoryMonitor(heapGauge, gcGauge, goroutineGauge metrics.Gauge) *MemoryMonitor {
return &MemoryMonitor{
heapGauge: heapGauge,
gcGauge: gcGauge,
goroutineGauge: goroutineGauge,
}
}
func (m *MemoryMonitor) Update() {
var stats runtime.MemStats
runtime.ReadMemStats(&stats)
m.heapGauge.Set(float64(stats.HeapAlloc))
m.gcGauge.Set(float64(stats.NumGC))
m.goroutineGauge.Set(float64(runtime.NumGoroutine()))
}
func MemoryMonitoringMiddleware(monitor *MemoryMonitor, sampleRate float64) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if rand.Float64() < sampleRate {
monitor.Update()
}
return next(ctx, request)
}
}
}
// 请求级内存监控
func RequestMemoryMiddleware(histogram metrics.Histogram) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
var before runtime.MemStats
runtime.ReadMemStats(&before)
response, err := next(ctx, request)
var after runtime.MemStats
runtime.ReadMemStats(&after)
allocated := after.TotalAlloc - before.TotalAlloc
histogram.Observe(float64(allocated))
return response, err
}
}
}
// 内存泄漏检测
type MemoryLeakDetector struct {
baseline uint64
threshold float64
alertFunc func(string)
}
func NewMemoryLeakDetector(threshold float64, alertFunc func(string)) *MemoryLeakDetector {
var stats runtime.MemStats
runtime.ReadMemStats(&stats)
return &MemoryLeakDetector{
baseline: stats.HeapAlloc,
threshold: threshold,
alertFunc: alertFunc,
}
}
func (d *MemoryLeakDetector) Check() {
var stats runtime.MemStats
runtime.ReadMemStats(&stats)
growth := float64(stats.HeapAlloc-d.baseline) / float64(d.baseline)
if growth > d.threshold {
d.alertFunc(fmt.Sprintf("Memory leak detected: %.2f%% growth", growth*100))
}
}
func MemoryLeakDetectionMiddleware(detector *MemoryLeakDetector, checkInterval time.Duration) endpoint.Middleware {
go func() {
ticker := time.NewTicker(checkInterval)
for range ticker.C {
detector.Check()
}
}()
return func(next endpoint.Endpoint) endpoint.Endpoint {
return next
}
}
---
5.4 限流中间件
01.令牌桶算法
a.基本实现
a.功能说明
令牌桶算法通过固定速率向桶中添加令牌,请求需要获取令牌才能执行。令牌桶支持突发流量,桶中可以积累一定数量的令牌。当桶满时,新产生的令牌会被丢弃。令牌桶算法平滑流量,避免瞬时过载。go-kit提供了基于golang.org/x/time/rate的令牌桶实现。令牌桶适合需要平滑限流的场景。
b.代码示例
---
// 令牌桶限流中间件
package middleware
import (
"context"
"golang.org/x/time/rate"
"github.com/go-kit/kit/endpoint"
)
func TokenBucketMiddleware(limiter *rate.Limiter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if !limiter.Allow() {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
// 等待式令牌桶
func WaitingTokenBucketMiddleware(limiter *rate.Limiter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if err := limiter.Wait(ctx); err != nil {
return nil, err
}
return next(ctx, request)
}
}
}
// 自定义令牌桶
type CustomTokenBucket struct {
tokens float64
capacity float64
rate float64
lastTime time.Time
mu sync.Mutex
}
func NewCustomTokenBucket(capacity, rate float64) *CustomTokenBucket {
return &CustomTokenBucket{
tokens: capacity,
capacity: capacity,
rate: rate,
lastTime: time.Now(),
}
}
func (b *CustomTokenBucket) Allow() bool {
b.mu.Lock()
defer b.mu.Unlock()
now := time.Now()
elapsed := now.Sub(b.lastTime).Seconds()
b.tokens = math.Min(b.capacity, b.tokens+elapsed*b.rate)
b.lastTime = now
if b.tokens >= 1 {
b.tokens--
return true
}
return false
}
func CustomTokenBucketMiddleware(bucket *CustomTokenBucket) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if !bucket.Allow() {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
---
b.动态调整
a.功能说明
动态限流根据系统负载自动调整限流阈值,实现自适应流量控制。可以根据CPU使用率、内存使用率、响应时间等指标动态调整。动态限流避免固定阈值的局限性,更好地适应流量变化。支持平滑调整,避免突变影响服务。动态限流需要监控系统指标,增加了复杂度。应该设置合理的调整策略和边界条件。
b.代码示例
---
// 动态限流中间件
package middleware
import (
"context"
"sync/atomic"
"time"
)
type DynamicRateLimiter struct {
limiter *rate.Limiter
minRate float64
maxRate float64
currentRate uint64
loadFunc func() float64
adjustPeriod time.Duration
}
func NewDynamicRateLimiter(minRate, maxRate float64, loadFunc func() float64) *DynamicRateLimiter {
limiter := rate.NewLimiter(rate.Limit(maxRate), int(maxRate))
drl := &DynamicRateLimiter{
limiter: limiter,
minRate: minRate,
maxRate: maxRate,
loadFunc: loadFunc,
adjustPeriod: 10 * time.Second,
}
atomic.StoreUint64(&drl.currentRate, uint64(maxRate))
go drl.adjust()
return drl
}
func (d *DynamicRateLimiter) adjust() {
ticker := time.NewTicker(d.adjustPeriod)
for range ticker.C {
load := d.loadFunc()
var newRate float64
if load < 0.5 {
newRate = d.maxRate
} else if load > 0.9 {
newRate = d.minRate
} else {
newRate = d.minRate + (d.maxRate-d.minRate)*(0.9-load)/0.4
}
d.limiter.SetLimit(rate.Limit(newRate))
atomic.StoreUint64(&d.currentRate, uint64(newRate))
}
}
func (d *DynamicRateLimiter) Allow() bool {
return d.limiter.Allow()
}
func DynamicRateLimitMiddleware(limiter *DynamicRateLimiter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if !limiter.Allow() {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
// 基于响应时间的动态限流
type LatencyBasedLimiter struct {
limiter *rate.Limiter
targetLatency time.Duration
latencyTracker *LatencyTracker
adjustInterval time.Duration
}
func NewLatencyBasedLimiter(initialRate float64, targetLatency time.Duration) *LatencyBasedLimiter {
lbl := &LatencyBasedLimiter{
limiter: rate.NewLimiter(rate.Limit(initialRate), int(initialRate)),
targetLatency: targetLatency,
latencyTracker: NewLatencyTracker(),
adjustInterval: 5 * time.Second,
}
go lbl.adjust()
return lbl
}
func (l *LatencyBasedLimiter) adjust() {
ticker := time.NewTicker(l.adjustInterval)
for range ticker.C {
avgLatency := l.latencyTracker.Average()
currentLimit := l.limiter.Limit()
if avgLatency > l.targetLatency {
newLimit := float64(currentLimit) * 0.9
l.limiter.SetLimit(rate.Limit(newLimit))
} else if avgLatency < l.targetLatency/2 {
newLimit := float64(currentLimit) * 1.1
l.limiter.SetLimit(rate.Limit(newLimit))
}
}
}
---
02.滑动窗口算法
a.固定窗口
a.功能说明
固定窗口算法将时间划分为固定大小的窗口,统计每个窗口内的请求数。固定窗口实现简单,但存在边界问题,窗口切换时可能出现突发流量。固定窗口适合对精度要求不高的场景。可以使用计数器实现,性能开销小。窗口大小影响限流效果,需要根据业务特点选择。固定窗口无法平滑处理跨窗口的流量。
b.代码示例
---
// 固定窗口限流中间件
package middleware
import (
"context"
"sync"
"time"
)
type FixedWindowLimiter struct {
limit int
window time.Duration
counter int
windowStart time.Time
mu sync.Mutex
}
func NewFixedWindowLimiter(limit int, window time.Duration) *FixedWindowLimiter {
return &FixedWindowLimiter{
limit: limit,
window: window,
windowStart: time.Now(),
}
}
func (l *FixedWindowLimiter) Allow() bool {
l.mu.Lock()
defer l.mu.Unlock()
now := time.Now()
if now.Sub(l.windowStart) >= l.window {
l.counter = 0
l.windowStart = now
}
if l.counter < l.limit {
l.counter++
return true
}
return false
}
func FixedWindowMiddleware(limiter *FixedWindowLimiter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if !limiter.Allow() {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
// 多窗口限流
type MultiWindowLimiter struct {
limiters map[time.Duration]*FixedWindowLimiter
mu sync.RWMutex
}
func NewMultiWindowLimiter() *MultiWindowLimiter {
return &MultiWindowLimiter{
limiters: make(map[time.Duration]*FixedWindowLimiter),
}
}
func (m *MultiWindowLimiter) AddWindow(window time.Duration, limit int) {
m.mu.Lock()
defer m.mu.Unlock()
m.limiters[window] = NewFixedWindowLimiter(limit, window)
}
func (m *MultiWindowLimiter) Allow() bool {
m.mu.RLock()
defer m.mu.RUnlock()
for _, limiter := range m.limiters {
if !limiter.Allow() {
return false
}
}
return true
}
func MultiWindowMiddleware(limiter *MultiWindowLimiter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if !limiter.Allow() {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
---
b.滑动窗口
a.功能说明
滑动窗口算法将时间窗口细分为多个小窗口,统计滑动窗口内的请求总数。滑动窗口解决了固定窗口的边界问题,限流更加平滑。滑动窗口需要维护多个计数器,内存开销较大。窗口粒度影响精度和性能,需要权衡。滑动窗口适合对限流精度要求较高的场景。可以使用环形缓冲区优化内存使用。
b.代码示例
---
// 滑动窗口限流中间件
package middleware
import (
"context"
"sync"
"time"
)
type SlidingWindowLimiter struct {
limit int
window time.Duration
buckets []int
bucketSize time.Duration
numBuckets int
current int
lastRotate time.Time
mu sync.Mutex
}
func NewSlidingWindowLimiter(limit int, window time.Duration, numBuckets int) *SlidingWindowLimiter {
return &SlidingWindowLimiter{
limit: limit,
window: window,
buckets: make([]int, numBuckets),
bucketSize: window / time.Duration(numBuckets),
numBuckets: numBuckets,
lastRotate: time.Now(),
}
}
func (l *SlidingWindowLimiter) rotate() {
now := time.Now()
elapsed := now.Sub(l.lastRotate)
rotations := int(elapsed / l.bucketSize)
if rotations > 0 {
if rotations >= l.numBuckets {
for i := range l.buckets {
l.buckets[i] = 0
}
} else {
for i := 0; i < rotations; i++ {
l.current = (l.current + 1) % l.numBuckets
l.buckets[l.current] = 0
}
}
l.lastRotate = now
}
}
func (l *SlidingWindowLimiter) total() int {
sum := 0
for _, count := range l.buckets {
sum += count
}
return sum
}
func (l *SlidingWindowLimiter) Allow() bool {
l.mu.Lock()
defer l.mu.Unlock()
l.rotate()
if l.total() < l.limit {
l.buckets[l.current]++
return true
}
return false
}
func SlidingWindowMiddleware(limiter *SlidingWindowLimiter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if !limiter.Allow() {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
---
03.分布式限流
a.Redis限流
a.功能说明
Redis限流使用Redis存储限流计数器,实现分布式限流。Redis限流支持多实例共享限流配额,适合分布式系统。可以使用Redis的INCR、EXPIRE命令实现简单限流。使用Lua脚本保证原子性,避免竞���条件。Redis限流依赖Redis可用性,需要考虑故障处理。可以使用Redis集群提高可用性和性能。
b.代码示例
---
// Redis限流中间件
package middleware
import (
"context"
"fmt"
"time"
"github.com/go-redis/redis/v8"
)
type RedisRateLimiter struct {
client *redis.Client
limit int
window time.Duration
prefix string
}
func NewRedisRateLimiter(client *redis.Client, limit int, window time.Duration) *RedisRateLimiter {
return &RedisRateLimiter{
client: client,
limit: limit,
window: window,
prefix: "ratelimit:",
}
}
func (r *RedisRateLimiter) Allow(ctx context.Context, key string) (bool, error) {
redisKey := r.prefix + key
script := `
local current = redis.call('INCR', KEYS[1])
if current == 1 then
redis.call('EXPIRE', KEYS[1], ARGV[1])
end
return current
`
result, err := r.client.Eval(ctx, script, []string{redisKey}, int(r.window.Seconds())).Result()
if err != nil {
return false, err
}
count := result.(int64)
return count <= int64(r.limit), nil
}
func RedisRateLimitMiddleware(limiter *RedisRateLimiter, keyFunc func(context.Context, interface{}) string) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
key := keyFunc(ctx, request)
allowed, err := limiter.Allow(ctx, key)
if err != nil {
return nil, err
}
if !allowed {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
// Redis滑动窗口限流
type RedisSlidingWindowLimiter struct {
client *redis.Client
limit int
window time.Duration
prefix string
}
func (r *RedisSlidingWindowLimiter) Allow(ctx context.Context, key string) (bool, error) {
redisKey := r.prefix + key
now := time.Now().UnixNano()
windowStart := now - r.window.Nanoseconds()
script := `
redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, ARGV[1])
local count = redis.call('ZCARD', KEYS[1])
if count < tonumber(ARGV[3]) then
redis.call('ZADD', KEYS[1], ARGV[2], ARGV[2])
redis.call('EXPIRE', KEYS[1], ARGV[4])
return 1
end
return 0
`
result, err := r.client.Eval(ctx, script,
[]string{redisKey},
windowStart, now, r.limit, int(r.window.Seconds())).Result()
if err != nil {
return false, err
}
return result.(int64) == 1, nil
}
---
b.分布式协调
a.功能说明
分布式协调限流使用分布式锁或协调服务实现全局限流。可以使用etcd、Consul等协调服务存储限流状态。分布式协调保证多实例间的限流一致性。需要处理网络分区、节点故障等问题。可以使用租约机制实现故障恢复。分布式协调增加了系统复杂度,需要权衡收益。应该设置合理的超时和重试策略。
b.代码示例
---
// 分布式协调限流中间件
package middleware
import (
"context"
"fmt"
"time"
"go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
)
type EtcdRateLimiter struct {
client *clientv3.Client
limit int
window time.Duration
prefix string
}
func NewEtcdRateLimiter(client *clientv3.Client, limit int, window time.Duration) *EtcdRateLimiter {
return &EtcdRateLimiter{
client: client,
limit: limit,
window: window,
prefix: "/ratelimit/",
}
}
func (e *EtcdRateLimiter) Allow(ctx context.Context, key string) (bool, error) {
session, err := concurrency.NewSession(e.client)
if err != nil {
return false, err
}
defer session.Close()
mutex := concurrency.NewMutex(session, e.prefix+key+"/lock")
if err := mutex.Lock(ctx); err != nil {
return false, err
}
defer mutex.Unlock(ctx)
counterKey := e.prefix + key + "/counter"
resp, err := e.client.Get(ctx, counterKey)
if err != nil {
return false, err
}
var count int
if len(resp.Kvs) > 0 {
fmt.Sscanf(string(resp.Kvs[0].Value), "%d", &count)
}
if count >= e.limit {
return false, nil
}
count++
_, err = e.client.Put(ctx, counterKey, fmt.Sprintf("%d", count),
clientv3.WithLease(session.Lease()))
return err == nil, err
}
func EtcdRateLimitMiddleware(limiter *EtcdRateLimiter, keyFunc func(context.Context) string) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
key := keyFunc(ctx)
allowed, err := limiter.Allow(ctx, key)
if err != nil {
return nil, err
}
if !allowed {
return nil, ErrRateLimitExceeded
}
return next(ctx, request)
}
}
}
// 基于配额的分布式限流
type QuotaBasedLimiter struct {
client *redis.Client
totalQuota int
instances int
localQuota int
used int
mu sync.Mutex
}
func NewQuotaBasedLimiter(client *redis.Client, totalQuota, instances int) *QuotaBasedLimiter {
return &QuotaBasedLimiter{
client: client,
totalQuota: totalQuota,
instances: instances,
localQuota: totalQuota / instances,
}
}
func (q *QuotaBasedLimiter) Allow(ctx context.Context) bool {
q.mu.Lock()
defer q.mu.Unlock()
if q.used < q.localQuota {
q.used++
return true
}
// 尝试从全局配额获取
script := `
local quota = redis.call('GET', KEYS[1])
if not quota then
redis.call('SET', KEYS[1], ARGV[1])
quota = ARGV[1]
end
if tonumber(quota) > 0 then
redis.call('DECR', KEYS[1])
return 1
end
return 0
`
result, err := q.client.Eval(ctx, script, []string{"global_quota"}, q.totalQuota).Result()
if err != nil || result.(int64) == 0 {
return false
}
q.localQuota++
q.used++
return true
}
---
5.5 熔断中间件
01.熔断器模式
a.状态机实现
a.功能说明
熔断器通过状态机管理服务调用,包括关闭、打开、半开三种状态。关闭状态正常调用,打开状态直接返回错误,半开状态尝试恢复。熔断器根据错误率或连续失败次数触发状态转换。熔断器保护下游服务,避免级联故障。go-kit集成了sony/gobreaker等熔断器库。熔断器适合调用不稳定的外部服务。
b.代码示例
---
// 熔断器中间件
package middleware
import (
"context"
"errors"
"sync"
"time"
"github.com/go-kit/kit/endpoint"
)
type State int
const (
StateClosed State = iota
StateOpen
StateHalfOpen
)
type CircuitBreaker struct {
maxFailures int
timeout time.Duration
state State
failures int
lastFailTime time.Time
mu sync.Mutex
}
func NewCircuitBreaker(maxFailures int, timeout time.Duration) *CircuitBreaker {
return &CircuitBreaker{
maxFailures: maxFailures,
timeout: timeout,
state: StateClosed,
}
}
func (cb *CircuitBreaker) Call(fn func() error) error {
cb.mu.Lock()
defer cb.mu.Unlock()
if cb.state == StateOpen {
if time.Since(cb.lastFailTime) > cb.timeout {
cb.state = StateHalfOpen
} else {
return errors.New("circuit breaker is open")
}
}
err := fn()
if err != nil {
cb.failures++
cb.lastFailTime = time.Now()
if cb.failures >= cb.maxFailures {
cb.state = StateOpen
}
return err
}
if cb.state == StateHalfOpen {
cb.state = StateClosed
}
cb.failures = 0
return nil
}
func CircuitBreakerMiddleware(cb *CircuitBreaker) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
var response interface{}
var err error
cbErr := cb.Call(func() error {
response, err = next(ctx, request)
return err
})
if cbErr != nil {
return nil, cbErr
}
return response, err
}
}
}
---
b.gobreaker集成
a.功能说明
gobreaker是成熟的熔断器库,提供完整的熔断功能。支持自定义成功判断函数,灵活定义失败条件。提供状态变化回调,便于监控和告警。支持滑动窗口统计,更准确地计算错误率。gobreaker经过生产验证,稳定可靠。集成简单,配置灵活。
b.代码示例
---
// gobreaker集成中间件
package middleware
import (
"context"
"github.com/sony/gobreaker"
)
func GobreakerMiddleware(cb *gobreaker.CircuitBreaker) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
result, err := cb.Execute(func() (interface{}, error) {
return next(ctx, request)
})
return result, err
}
}
}
// 自定义配置
func NewCustomCircuitBreaker(name string) *gobreaker.CircuitBreaker {
settings := gobreaker.Settings{
Name: name,
MaxRequests: 3,
Interval: time.Minute,
Timeout: 30 * time.Second,
ReadyToTrip: func(counts gobreaker.Counts) bool {
failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
return counts.Requests >= 10 && failureRatio >= 0.5
},
OnStateChange: func(name string, from gobreaker.State, to gobreaker.State) {
log.Printf("Circuit breaker %s: %s -> %s", name, from, to)
},
}
return gobreaker.NewCircuitBreaker(settings)
}
// 多端点熔断器
type MultiCircuitBreaker struct {
breakers map[string]*gobreaker.CircuitBreaker
mu sync.RWMutex
}
func NewMultiCircuitBreaker() *MultiCircuitBreaker {
return &MultiCircuitBreaker{
breakers: make(map[string]*gobreaker.CircuitBreaker),
}
}
func (m *MultiCircuitBreaker) Get(name string) *gobreaker.CircuitBreaker {
m.mu.RLock()
cb, exists := m.breakers[name]
m.mu.RUnlock()
if !exists {
m.mu.Lock()
cb = NewCustomCircuitBreaker(name)
m.breakers[name] = cb
m.mu.Unlock()
}
return cb
}
func MultiCircuitBreakerMiddleware(mcb *MultiCircuitBreaker, nameFunc func(context.Context) string) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
name := nameFunc(ctx)
cb := mcb.Get(name)
result, err := cb.Execute(func() (interface{}, error) {
return next(ctx, request)
})
return result, err
}
}
}
---
02.降级策略
a.快速失败
a.功能说明
快速失败策略在熔断器打开时立即返回错误,不等待超时。快速失败减少资源占用,提高系统响应速度。适合对延迟敏感的场景。可以返回自定义错误信息,提示服务不可用。快速失败配合重试机制,提高成功率。应该记录失败日志,便于问题排查。
b.代码示例
---
// 快速失败中间件
package middleware
import (
"context"
"errors"
)
var ErrServiceUnavailable = errors.New("service unavailable")
func FailFastMiddleware(cb *CircuitBreaker) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if cb.IsOpen() {
return nil, ErrServiceUnavailable
}
return next(ctx, request)
}
}
}
// 带降级响应的快速失败
type FallbackFunc func(context.Context, interface{}) (interface{}, error)
func FailFastWithFallbackMiddleware(cb *CircuitBreaker, fallback FallbackFunc) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if cb.IsOpen() {
return fallback(ctx, request)
}
response, err := next(ctx, request)
if err != nil {
return fallback(ctx, request)
}
return response, nil
}
}
}
// 超时快速失败
func TimeoutFailFastMiddleware(timeout time.Duration) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
done := make(chan struct{})
var response interface{}
var err error
go func() {
response, err = next(ctx, request)
close(done)
}()
select {
case <-done:
return response, err
case <-ctx.Done():
return nil, errors.New("request timeout")
}
}
}
}
---
b.降级响应
a.功能说明
降级响应在服务不可用时返回备用数据,保证基本功能可用。降级数据可以来自缓存、默认值或备用服务。降级策略应该根据业务重要性设计。降级响应应该标记降级状态,便于监控。降级可以是部分降级或完全降级。降级策略需要定期测试,确保有效性。
b.代码示例
---
// 降级响应中间件
package middleware
import (
"context"
"time"
)
type FallbackStrategy interface {
Execute(ctx context.Context, request interface{}) (interface{}, error)
}
type CacheFallback struct {
cache Cache
ttl time.Duration
}
func (f *CacheFallback) Execute(ctx context.Context, request interface{}) (interface{}, error) {
key := generateCacheKey(request)
if value, found := f.cache.Get(key); found {
return value, nil
}
return nil, errors.New("no cached data available")
}
type DefaultValueFallback struct {
defaultValue interface{}
}
func (f *DefaultValueFallback) Execute(ctx context.Context, request interface{}) (interface{}, error) {
return f.defaultValue, nil
}
func FallbackMiddleware(strategy FallbackStrategy) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
response, err := next(ctx, request)
if err != nil {
return strategy.Execute(ctx, request)
}
return response, nil
}
}
}
// 多级降级
type TieredFallback struct {
strategies []FallbackStrategy
}
func (t *TieredFallback) Execute(ctx context.Context, request interface{}) (interface{}, error) {
for _, strategy := range t.strategies {
if response, err := strategy.Execute(ctx, request); err == nil {
return response, nil
}
}
return nil, errors.New("all fallback strategies failed")
}
func TieredFallbackMiddleware(strategies ...FallbackStrategy) endpoint.Middleware {
fallback := &TieredFallback{strategies: strategies}
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
response, err := next(ctx, request)
if err != nil {
return fallback.Execute(ctx, request)
}
return response, nil
}
}
}
---
03.自适应熔断
a.动��阈值
a.功能说明
动态阈值根据系统状态自动调整熔断参数,实现自适应保护。可以根据错误率、响应时间、系统负载等指标动态调整。动态阈值避免固定阈值的局限性,更好地适应流量变化。支持平滑调整,避免频繁触发熔断。动态阈值需要监控多个指标,增加了复杂度。应该设置合理的调整策略和边界条件。
b.代码示例
---
// 动态阈值熔断中间件
package middleware
import (
"context"
"sync/atomic"
"time"
)
type AdaptiveCircuitBreaker struct {
minThreshold int
maxThreshold int
threshold uint64
window time.Duration
metrics *MetricsCollector
}
func NewAdaptiveCircuitBreaker(minThreshold, maxThreshold int, window time.Duration) *AdaptiveCircuitBreaker {
acb := &AdaptiveCircuitBreaker{
minThreshold: minThreshold,
maxThreshold: maxThreshold,
window: window,
metrics: NewMetricsCollector(),
}
atomic.StoreUint64(&acb.threshold, uint64(maxThreshold))
go acb.adjust()
return acb
}
func (acb *AdaptiveCircuitBreaker) adjust() {
ticker := time.NewTicker(acb.window)
for range ticker.C {
errorRate := acb.metrics.ErrorRate()
avgLatency := acb.metrics.AverageLatency()
var newThreshold int
if errorRate < 0.01 && avgLatency < 100*time.Millisecond {
newThreshold = acb.maxThreshold
} else if errorRate > 0.1 || avgLatency > 1*time.Second {
newThreshold = acb.minThreshold
} else {
current := atomic.LoadUint64(&acb.threshold)
newThreshold = int(current)
}
atomic.StoreUint64(&acb.threshold, uint64(newThreshold))
}
}
func (acb *AdaptiveCircuitBreaker) ShouldTrip() bool {
failures := acb.metrics.Failures()
threshold := atomic.LoadUint64(&acb.threshold)
return failures >= threshold
}
func AdaptiveCircuitBreakerMiddleware(acb *AdaptiveCircuitBreaker) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if acb.ShouldTrip() {
return nil, errors.New("circuit breaker tripped")
}
begin := time.Now()
response, err := next(ctx, request)
duration := time.Since(begin)
acb.metrics.Record(err, duration)
return response, err
}
}
}
---
b.智能恢复
a.功能说明
智能恢复策略根据服务健康状况逐步恢复流量,避免雪崩。可以使用指数退避、线性增长等策略。智能恢复监控恢复期间的成功率,动态调整恢复速度。支持自动回退,恢复失败时重新熔断。智能恢复减少人工干预,提高系统自愈能力。应该记录恢复过程,便于分析和优化。
b.代码示例
---
// 智能恢复中间件
package middleware
import (
"context"
"math"
"sync/atomic"
)
type SmartRecovery struct {
state State
failures uint64
successes uint64
recoveryAttempt uint64
maxAttempts int
baseDelay time.Duration
lastAttempt time.Time
mu sync.Mutex
}
func NewSmartRecovery(maxAttempts int, baseDelay time.Duration) *SmartRecovery {
return &SmartRecovery{
state: StateClosed,
maxAttempts: maxAttempts,
baseDelay: baseDelay,
}
}
func (sr *SmartRecovery) CanAttempt() bool {
sr.mu.Lock()
defer sr.mu.Unlock()
if sr.state != StateOpen {
return true
}
attempt := atomic.LoadUint64(&sr.recoveryAttempt)
if attempt >= uint64(sr.maxAttempts) {
return false
}
delay := time.Duration(math.Pow(2, float64(attempt))) * sr.baseDelay
if time.Since(sr.lastAttempt) < delay {
return false
}
sr.lastAttempt = time.Now()
atomic.AddUint64(&sr.recoveryAttempt, 1)
return true
}
func (sr *SmartRecovery) RecordSuccess() {
atomic.AddUint64(&sr.successes, 1)
successes := atomic.LoadUint64(&sr.successes)
if successes >= 3 {
sr.mu.Lock()
sr.state = StateClosed
atomic.StoreUint64(&sr.failures, 0)
atomic.StoreUint64(&sr.successes, 0)
atomic.StoreUint64(&sr.recoveryAttempt, 0)
sr.mu.Unlock()
}
}
func (sr *SmartRecovery) RecordFailure() {
atomic.AddUint64(&sr.failures, 1)
atomic.StoreUint64(&sr.successes, 0)
failures := atomic.LoadUint64(&sr.failures)
if failures >= 3 {
sr.mu.Lock()
sr.state = StateOpen
sr.mu.Unlock()
}
}
func SmartRecoveryMiddleware(sr *SmartRecovery) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if !sr.CanAttempt() {
return nil, errors.New("circuit breaker open")
}
response, err := next(ctx, request)
if err != nil {
sr.RecordFailure()
} else {
sr.RecordSuccess()
}
return response, err
}
}
}
---
5.6 链路追踪
01.OpenTracing集成
a.Tracer初始化
a.功能说明
OpenTracing是分布式追踪的标准API,提供厂商中立的追踪接口。Tracer负责创建和管理Span,记录请求的执行路径。支持多种后端实现,如Jaeger、Zipkin等。OpenTracing定义了统一的数据模型和API。集成简单,对业务代码侵入小。支持跨进程、跨服务的追踪。
b.代码示例
---
// OpenTracing集成中间件
package middleware
import (
"context"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/go-kit/kit/endpoint"
)
func OpenTracingMiddleware(tracer opentracing.Tracer, operationName string) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
var span opentracing.Span
if parentSpan := opentracing.SpanFromContext(ctx); parentSpan != nil {
span = tracer.StartSpan(operationName, opentracing.ChildOf(parentSpan.Context()))
} else {
span = tracer.StartSpan(operationName)
}
defer span.Finish()
ctx = opentracing.ContextWithSpan(ctx, span)
response, err := next(ctx, request)
if err != nil {
ext.Error.Set(span, true)
span.SetTag("error.message", err.Error())
}
return response, err
}
}
}
// 带标签的追踪中间件
func TracingWithTagsMiddleware(tracer opentracing.Tracer, operationName string, tags map[string]interface{}) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
span, ctx := opentracing.StartSpanFromContext(ctx, operationName)
defer span.Finish()
for key, value := range tags {
span.SetTag(key, value)
}
ext.Component.Set(span, "go-kit")
response, err := next(ctx, request)
if err != nil {
ext.Error.Set(span, true)
span.LogKV("event", "error", "message", err.Error())
}
return response, err
}
}
}
---
b.Span管理
a.功能说明
Span表示一次操作的执行过程,包含操作名称、开始时间、结束时间等信息。Span可以嵌套,形成调用树。支持添加标签和日志,记录详细信息。Span应该在操作完成后及时关闭。可以通过Context传递Span,实现跨函数追踪。Span管理应该轻量级,避免影响性能。
b.代码示例
---
// Span管理中间件
package middleware
import (
"context"
"fmt"
)
func SpanManagementMiddleware(tracer opentracing.Tracer) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
span, ctx := opentracing.StartSpanFromContext(ctx, "endpoint.call")
defer span.Finish()
span.SetTag("request.type", fmt.Sprintf("%T", request))
span.LogKV("event", "request_received", "request", request)
response, err := next(ctx, request)
if err != nil {
span.SetTag("error", true)
span.LogKV("event", "error", "error.object", err)
} else {
span.LogKV("event", "request_completed", "response", response)
}
return response, err
}
}
}
// 自定义Span中间件
type SpanDecorator func(span opentracing.Span, ctx context.Context, request interface{})
func CustomSpanMiddleware(tracer opentracing.Tracer, operationName string, decorator SpanDecorator) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
span, ctx := opentracing.StartSpanFromContext(ctx, operationName)
defer span.Finish()
if decorator != nil {
decorator(span, ctx, request)
}
return next(ctx, request)
}
}
}
// 使用示例
func UserIDDecorator(span opentracing.Span, ctx context.Context, request interface{}) {
if userID := ctx.Value("user_id"); userID != nil {
span.SetTag("user.id", userID)
}
}
---
02.Jaeger集成
a.Jaeger配置
a.功能说明
Jaeger是开源的分布式追踪系统,兼容OpenTracing标准。支持采样策略,控制追踪数据量。提供UI界面,可视化追踪数据。支持多种存储后端,如Cassandra、Elasticsearch等。Jaeger配置包括Agent地址、采样率、服务名等。支持动态配置,运行时调整参数。
b.代码示例
---
// Jaeger集成
package tracing
import (
"io"
"github.com/uber/jaeger-client-go"
"github.com/uber/jaeger-client-go/config"
)
func InitJaeger(serviceName string) (opentracing.Tracer, io.Closer, error) {
cfg := &config.Configuration{
ServiceName: serviceName,
Sampler: &config.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &config.ReporterConfig{
LogSpans: true,
LocalAgentHostPort: "localhost:6831",
},
}
tracer, closer, err := cfg.NewTracer(
config.Logger(jaeger.StdLogger),
)
if err != nil {
return nil, nil, err
}
opentracing.SetGlobalTracer(tracer)
return tracer, closer, nil
}
// 自定义采样策略
func InitJaegerWithSampling(serviceName string, samplingRate float64) (opentracing.Tracer, io.Closer, error) {
cfg := &config.Configuration{
ServiceName: serviceName,
Sampler: &config.SamplerConfig{
Type: "probabilistic",
Param: samplingRate,
},
Reporter: &config.ReporterConfig{
LogSpans: false,
BufferFlushInterval: 1 * time.Second,
LocalAgentHostPort: "localhost:6831",
},
}
tracer, closer, err := cfg.NewTracer()
return tracer, closer, err
}
// 远程采样配置
func InitJaegerWithRemoteSampling(serviceName string) (opentracing.Tracer, io.Closer, error) {
cfg := &config.Configuration{
ServiceName: serviceName,
Sampler: &config.SamplerConfig{
Type: "remote",
Param: 1,
SamplingServerURL: "http://localhost:5778/sampling",
},
Reporter: &config.ReporterConfig{
LocalAgentHostPort: "localhost:6831",
},
}
return cfg.NewTracer()
}
---
b.追踪数据上报
a.功能说明
追踪数据上报将Span数据发送到Jaeger后端。支持批量上报,减少网络开销。可以配置缓冲区大小和刷新间隔。上报失败时支持重试机制。应该异步上报,避免阻塞业务逻辑。可以配置采样率,控制上报数据量。
b.代码示例
---
// 追踪数据上报配置
package tracing
import (
"github.com/uber/jaeger-client-go"
"github.com/uber/jaeger-client-go/config"
)
func InitJaegerReporter(serviceName string) (opentracing.Tracer, io.Closer, error) {
cfg := &config.Configuration{
ServiceName: serviceName,
Sampler: &config.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &config.ReporterConfig{
QueueSize: 1000,
BufferFlushInterval: 1 * time.Second,
LocalAgentHostPort: "localhost:6831",
CollectorEndpoint: "http://localhost:14268/api/traces",
},
}
tracer, closer, err := cfg.NewTracer(
config.Logger(jaeger.StdLogger),
config.Metrics(jaeger.NewMetrics(jaeger.NewStdMeter(), nil)),
)
return tracer, closer, err
}
// 自定义Reporter
type CustomReporter struct {
reporter jaeger.Reporter
logger log.Logger
}
func (r *CustomReporter) Report(span *jaeger.Span) {
r.logger.Log("span", span.OperationName(), "duration", span.Duration())
r.reporter.Report(span)
}
func (r *CustomReporter) Close() error {
return r.reporter.Close()
}
func InitWithCustomReporter(serviceName string, logger log.Logger) (opentracing.Tracer, io.Closer, error) {
transport, err := jaeger.NewUDPTransport("localhost:6831", 0)
if err != nil {
return nil, nil, err
}
reporter := jaeger.NewRemoteReporter(transport)
customReporter := &CustomReporter{
reporter: reporter,
logger: logger,
}
sampler := jaeger.NewConstSampler(true)
tracer, closer := jaeger.NewTracer(
serviceName,
sampler,
customReporter,
)
return tracer, closer, nil
}
---
03.分布式追踪
a.跨服务追踪
a.功能说明
跨服务追踪记录请求在多个服务间的传播路径。通过HTTP头或gRPC元数据传递追踪上下文。每个服务创建子Span,形成完整的调用链。支持异步调用的追踪,如消息队列。跨服务追踪帮助定位性能瓶颈和故障点。应该统一追踪标准,确保兼容性。
b.代码示例
---
// 跨服务追踪中间件
package middleware
import (
"context"
"net/http"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
func HTTPClientTracingMiddleware(tracer opentracing.Tracer) func(*http.Request) {
return func(req *http.Request) {
span := opentracing.SpanFromContext(req.Context())
if span == nil {
return
}
ext.SpanKindRPCClient.Set(span)
ext.HTTPUrl.Set(span, req.URL.String())
ext.HTTPMethod.Set(span, req.Method)
tracer.Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header),
)
}
}
func HTTPServerTracingMiddleware(tracer opentracing.Tracer, operationName string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
spanCtx, _ := tracer.Extract(
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(r.Header),
)
span := tracer.StartSpan(operationName, ext.RPCServerOption(spanCtx))
defer span.Finish()
ext.HTTPMethod.Set(span, r.Method)
ext.HTTPUrl.Set(span, r.URL.String())
ctx := opentracing.ContextWithSpan(r.Context(), span)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// gRPC追踪
func GRPCClientTracingInterceptor(tracer opentracing.Tracer) grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
span, ctx := opentracing.StartSpanFromContext(ctx, method)
defer span.Finish()
ext.SpanKindRPCClient.Set(span)
ext.Component.Set(span, "grpc")
md := metadata.New(nil)
if err := tracer.Inject(span.Context(), opentracing.TextMap, metadataTextMap(md)); err != nil {
span.LogKV("event", "inject_failed", "error", err)
}
ctx = metadata.NewOutgoingContext(ctx, md)
return invoker(ctx, method, req, reply, cc, opts...)
}
}
type metadataTextMap metadata.MD
func (m metadataTextMap) Set(key, val string) {
metadata.MD(m)[key] = append(metadata.MD(m)[key], val)
}
func (m metadataTextMap) ForeachKey(handler func(key, val string) error) error {
for k, vals := range metadata.MD(m) {
for _, v := range vals {
if err := handler(k, v); err != nil {
return err
}
}
}
return nil
}
---
b.追踪可视化
a.功能说明
追踪可视化将追踪数据以图形方式展示,便于分析和理解。Jaeger UI提供时间线视图,显示Span的执行顺序和耗时。支持依赖关系图,展示服务间的调用关系。可以搜索和过滤追踪数据,快速定位问题。支持对比分析,比较不同请求的性能差异。追踪可视化是分布式系统调试的重要工具。
b.代码示例
---
// 追踪数据增强
package tracing
import (
"context"
"fmt"
)
func EnhancedTracingMiddleware(tracer opentracing.Tracer, serviceName string) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
span, ctx := opentracing.StartSpanFromContext(ctx, "endpoint")
defer span.Finish()
span.SetTag("service.name", serviceName)
span.SetTag("service.version", "1.0.0")
span.SetTag("request.type", fmt.Sprintf("%T", request))
if userID := ctx.Value("user_id"); userID != nil {
span.SetTag("user.id", userID)
}
if requestID := ctx.Value("request_id"); requestID != nil {
span.SetTag("request.id", requestID)
}
span.LogKV("event", "request_start")
response, err := next(ctx, request)
span.LogKV("event", "request_end")
if err != nil {
span.SetTag("error", true)
span.SetTag("error.type", fmt.Sprintf("%T", err))
span.LogKV("event", "error", "message", err.Error())
}
return response, err
}
}
}
// 性能分析追踪
func PerformanceTracingMiddleware(tracer opentracing.Tracer) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
span, ctx := opentracing.StartSpanFromContext(ctx, "performance")
defer span.Finish()
var m runtime.MemStats
runtime.ReadMemStats(&m)
span.SetTag("mem.alloc.before", m.Alloc)
start := time.Now()
response, err := next(ctx, request)
duration := time.Since(start)
runtime.ReadMemStats(&m)
span.SetTag("mem.alloc.after", m.Alloc)
span.SetTag("duration.ms", duration.Milliseconds())
if duration > 1*time.Second {
span.SetTag("slow.request", true)
}
return response, err
}
}
}
---
5.7 自定义中间件
01.中间件开发
a.基本结构
a.功能说明
自定义中间件遵循endpoint.Middleware类型定义,接收Endpoint返回Endpoint。中间件应该保持单一职责,只做一件事。支持配置参数,提高灵活性。中间件应该是无状态的或线程安全的。可以组合多个中间件实现复杂功能。中间件开发应该考虑性能影响。
b.代码示例
---
package middleware
import ("context"; "time"; "github.com/go-kit/kit/endpoint")
func CustomMiddleware(config Config) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
start := time.Now()
response, err := next(ctx, request)
duration := time.Since(start)
if duration > config.Threshold {
config.Logger.Log("slow_request", duration)
}
return response, err
}
}
}
type Config struct {
Threshold time.Duration
Logger log.Logger
}
func NewConfig(threshold time.Duration, logger log.Logger) Config {
return Config{Threshold: threshold, Logger: logger}
}
---
b.参数注入
a.功能说明
参数注入通过闭包将配置传递给中间件,实现灵活配置。支持依赖注入,解耦中间件和具体实现。可以使用工厂函数创建中间件。参数应该在中间件创建时传入,避免运行时查找。支持可选参数,提供默认值。参数注入使中间件更易测试和复用。
b.代码示例
---
package middleware
import "context"
func InjectableMiddleware(logger log.Logger, metrics metrics.Counter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
metrics.Add(1)
logger.Log("request", request)
response, err := next(ctx, request)
if err != nil {
logger.Log("error", err)
}
return response, err
}
}
}
type MiddlewareFactory struct {
logger log.Logger
metrics metrics.Counter
}
func (f *MiddlewareFactory) Create() endpoint.Middleware {
return InjectableMiddleware(f.logger, f.metrics)
}
---
02.中间件测试
a.单元测试
a.功能说明
中间件单元测试验证中间件的功能正确性。使用mock Endpoint测试中间件行为。测试正常流程和异常流程。验证中间件对请求响应的影响。测试中间件的配置参数。单元测试应该覆盖所有分支。
b.代码示例
---
package middleware
import ("context"; "errors"; "testing")
func TestLoggingMiddleware(t *testing.T) {
logger := &mockLogger{}
mw := LoggingMiddleware(logger)
endpoint := mw(func(ctx context.Context, request interface{}) (interface{}, error) {
return "response", nil
})
response, err := endpoint(context.Background(), "request")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if response != "response" {
t.Errorf("unexpected response: %v", response)
}
if !logger.called {
t.Error("logger not called")
}
}
type mockLogger struct {
called bool
}
func (m *mockLogger) Log(keyvals ...interface{}) error {
m.called = true
return nil
}
---
b.集成测试
a.功能说明
集成测试验证中间件在实际环境中的行为。测试中间件与其他组件的交互。验证中间件链的执行顺序。测试中间件的性能影响。使用真实的依赖或测试替身。集成测试确保中间件在生产环境中正常工作。
b.代码示例
---
package middleware
import ("context"; "testing"; "time")
func TestMiddlewareChain(t *testing.T) {
logger := &mockLogger{}
counter := &mockCounter{}
endpoint := func(ctx context.Context, request interface{}) (interface{}, error) {
return "response", nil
}
endpoint = LoggingMiddleware(logger)(endpoint)
endpoint = MetricsMiddleware(counter)(endpoint)
endpoint = RateLimitMiddleware(rate.NewLimiter(10, 10))(endpoint)
response, err := endpoint(context.Background(), "request")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if !logger.called || !counter.called {
t.Error("middleware not executed")
}
}
type mockCounter struct {
called bool
}
func (m *mockCounter) Add(delta float64) {
m.called = true
}
---
03.中间件最佳实践
a.设计原则
a.功能说明
中间件应该遵循单一职责原则,每个中间件只做一件事。保持中间件的独立性,避免相互依赖。中间件应该是可组合的,支持灵活组合。使用配置参数而不是硬编码。中间件应该对性能影响最小。提供清晰的文档和使用示例。
b.代码示例
---
package middleware
import "context"
func WellDesignedMiddleware(config MiddlewareConfig) endpoint.Middleware {
if err := config.Validate(); err != nil {
panic(err)
}
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
if !config.Enabled {
return next(ctx, request)
}
if err := config.PreProcess(ctx, request); err != nil {
return nil, err
}
response, err := next(ctx, request)
config.PostProcess(ctx, response, err)
return response, err
}
}
}
type MiddlewareConfig struct {
Enabled bool
PreProcess func(context.Context, interface{}) error
PostProcess func(context.Context, interface{}, error)
}
func (c *MiddlewareConfig) Validate() error {
if c.PreProcess == nil || c.PostProcess == nil {
return errors.New("invalid config")
}
return nil
}
---
b.性能优化
a.功能说明
中间件性能优化减少对请求处理的影响。避免在中间件中执行耗时操作。使用异步处理非关键逻辑。合理使用缓存减少重复计算。避免过度日志记录。使用对象池减少内存分配。性能优化应该基于实际测量结果。
b.代码示例
---
package middleware
import ("context"; "sync")
var bufferPool = sync.Pool{
New: func() interface{} {
return make([]byte, 1024)
},
}
func OptimizedMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
buf := bufferPool.Get().([]byte)
defer bufferPool.Put(buf)
response, err := next(ctx, request)
go func() {
logger.Log("request", request, "response", response)
}()
return response, err
}
}
}
type CachedMiddleware struct {
cache map[string]interface{}
mu sync.RWMutex
}
func (c *CachedMiddleware) Middleware() endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
key := generateKey(request)
c.mu.RLock()
if cached, found := c.cache[key]; found {
c.mu.RUnlock()
return cached, nil
}
c.mu.RUnlock()
response, err := next(ctx, request)
if err == nil {
c.mu.Lock()
c.cache[key] = response
c.mu.Unlock()
}
return response, err
}
}
}
---
6 服务发现
6.1 服务发现概念
01.服务注册
a.基本实现
a.功能说明
服务注册是服务发现的核心功能,提供服务的注册和管理能力。支持服务元数据存储,包括地址、端口、版本等信息。提供服务健康检查,自动剔除不健康实例。支持服务分组和标签,实现灵活的服务管理。服务注册应该高可用,避免单点故障。支持动态更新,实时反映服务状态变化。
b.代码示例
---
package sd
import ("context"; "time")
type Registrar interface {
Register() error
Deregister() error
}
type ServiceInstance struct {
ID string
Name string
Address string
Port int
Metadata map[string]string
Tags []string
}
func NewServiceInstance(name, address string, port int) *ServiceInstance {
return &ServiceInstance{
ID: generateID(),
Name: name,
Address: address,
Port: port,
Metadata: make(map[string]string),
Tags: make([]string, 0),
}
}
func (s *ServiceInstance) WithMetadata(key, value string) *ServiceInstance {
s.Metadata[key] = value
return s
}
func (s *ServiceInstance) WithTags(tags ...string) *ServiceInstance {
s.Tags = append(s.Tags, tags...)
return s
}
---
b.配置管理
a.功能说明
配置管理提供服务发现的参数配置,包括注册中心地址、超时时间、重试策略等。支持配置热更新,运行时调整参数。配置应该支持多环境,开发、测试、生产使用不同配置。提供配置验证,确保参数合法性。配置管理应该集中化,便于维护。支持配置加密,保护敏感信息。
b.代码示例
---
package sd
import "time"
type Config struct {
Endpoints []string
Timeout time.Duration
RetryAttempts int
HealthCheckTTL time.Duration
DeregisterAfter time.Duration
}
func DefaultConfig() *Config {
return &Config{
Endpoints: []string{"localhost:8500"},
Timeout: 10 * time.Second,
RetryAttempts: 3,
HealthCheckTTL: 30 * time.Second,
DeregisterAfter: 1 * time.Minute,
}
}
func (c *Config) Validate() error {
if len(c.Endpoints) == 0 {
return errors.New("endpoints required")
}
if c.Timeout <= 0 {
return errors.New("invalid timeout")
}
return nil
}
func (c *Config) WithEndpoints(endpoints ...string) *Config {
c.Endpoints = endpoints
return c
}
---
02.服务查询
a.实现方��
a.功能说明
服务查询提供服务的具体实现方式,包括API调用、配置文件等。支持批量操作,提高效率。提供错误处理和重试机制。服务查询应该是幂等的,重复调用不产生副作用。支持异步操作,避免阻塞。提供操作日志,便于审计和排查。
b.代码示例
---
package sd
import "context"
type Client struct {
config *Config
conn Connection
}
func NewClient(config *Config) (*Client, error) {
if err := config.Validate(); err != nil {
return nil, err
}
conn, err := connect(config.Endpoints)
if err != nil {
return nil, err
}
return &Client{config: config, conn: conn}, nil
}
func (c *Client) Register(ctx context.Context, instance *ServiceInstance) error {
for i := 0; i < c.config.RetryAttempts; i++ {
if err := c.conn.Register(ctx, instance); err == nil {
return nil
}
time.Sleep(time.Second * time.Duration(i+1))
}
return errors.New("register failed after retries")
}
func (c *Client) Deregister(ctx context.Context, instanceID string) error {
return c.conn.Deregister(ctx, instanceID)
}
func (c *Client) Close() error {
return c.conn.Close()
}
---
b.错误处理
a.功能说明
错误处理确保服务发现操作的可靠性。区分临时错误和永久错误,采取不同策略。临时错误支持重试,永久错误直接返回。提供详细的错误信息,便于问题定位。错误处理应该记录日志。支持错误回调,通知上层应用。错误处理不应该影响系统稳定性。
b.代码示例
---
package sd
import "errors"
type ErrorType int
const (
ErrorTypeTemporary ErrorType = iota
ErrorTypePermanent
)
type SDError struct {
Type ErrorType
Message string
Cause error
}
func (e *SDError) Error() string {
return e.Message
}
func (e *SDError) Unwrap() error {
return e.Cause
}
func HandleError(err error, retryFunc func() error) error {
if err == nil {
return nil
}
sdErr, ok := err.(*SDError)
if !ok {
return err
}
if sdErr.Type == ErrorTypeTemporary {
return retryFunc()
}
return sdErr
}
---
03.健康检查
a.核心功能
a.功能说明
健康检查是服务发现的关键功能,提供服务的核心能力。支持实时更新,快速响应变化。提供高性能查询,满足高并发需求。健康检查应该是可扩展的,支持自定义扩展。提供监控指标,便于性能分析。健康检查应该容错,部分失败不影响整体。
b.代码示例
---
package sd
import ("context"; "sync")
type Manager struct {
instances map[string][]*ServiceInstance
mu sync.RWMutex
watchers []Watcher
}
func NewManager() *Manager {
return &Manager{
instances: make(map[string][]*ServiceInstance),
watchers: make([]Watcher, 0),
}
}
func (m *Manager) Add(instance *ServiceInstance) {
m.mu.Lock()
defer m.mu.Unlock()
m.instances[instance.Name] = append(m.instances[instance.Name], instance)
m.notifyWatchers(instance.Name)
}
func (m *Manager) Remove(instanceID string) {
m.mu.Lock()
defer m.mu.Unlock()
for name, instances := range m.instances {
for i, inst := range instances {
if inst.ID == instanceID {
m.instances[name] = append(instances[:i], instances[i+1:]...)
m.notifyWatchers(name)
return
}
}
}
}
func (m *Manager) Get(serviceName string) []*ServiceInstance {
m.mu.RLock()
defer m.mu.RUnlock()
return m.instances[serviceName]
}
func (m *Manager) Watch(watcher Watcher) {
m.mu.Lock()
defer m.mu.Unlock()
m.watchers = append(m.watchers, watcher)
}
func (m *Manager) notifyWatchers(serviceName string) {
for _, watcher := range m.watchers {
go watcher.OnChange(serviceName, m.instances[serviceName])
}
}
type Watcher interface {
OnChange(serviceName string, instances []*ServiceInstance)
}
---
b.最佳实践
a.功能说明
最佳实践总结服务发现的经验和建议。合理设置超时和重试参数。使用连接池提高性能。定期清理过期数据。监控服务发现的性能指标。做好容错和降级处理。定期测试故障场景。保持服务发现组件的版本更新。
b.代码示例
---
package sd
import "time"
type BestPractices struct {
client *Client
healthChecker *HealthChecker
cache *Cache
}
func NewBestPractices(config *Config) (*BestPractices, error) {
client, err := NewClient(config)
if err != nil {
return nil, err
}
return &BestPractices{
client: client,
healthChecker: NewHealthChecker(config.HealthCheckTTL),
cache: NewCache(5 * time.Minute),
}, nil
}
func (bp *BestPractices) RegisterWithHealthCheck(ctx context.Context, instance *ServiceInstance) error {
if err := bp.client.Register(ctx, instance); err != nil {
return err
}
bp.healthChecker.Start(instance.ID, func() bool {
return checkHealth(instance)
})
return nil
}
func (bp *BestPractices) DiscoverWithCache(ctx context.Context, serviceName string) ([]*ServiceInstance, error) {
if cached := bp.cache.Get(serviceName); cached != nil {
return cached.([]*ServiceInstance), nil
}
instances, err := bp.client.Discover(ctx, serviceName)
if err != nil {
return nil, err
}
bp.cache.Set(serviceName, instances)
return instances, nil
}
---
6.2 Consul集成
01.Consul客户端
a.基本实现
a.功能说明
Consul客户端是服务发现的核心功能,提供服务的注册和管理能力。支持服务元数据存储,包括地址、端口、版本等信息。提供服务健康检查,自动剔除不健康实例。支持服务分组和标签,实现灵活的服务管理。Consul客户端应该高可用,避免单点故障。支持动态更新,实时反映服务状态变化。
b.代码示例
---
package sd
import ("context"; "time")
type Registrar interface {
Register() error
Deregister() error
}
type ServiceInstance struct {
ID string
Name string
Address string
Port int
Metadata map[string]string
Tags []string
}
func NewServiceInstance(name, address string, port int) *ServiceInstance {
return &ServiceInstance{
ID: generateID(),
Name: name,
Address: address,
Port: port,
Metadata: make(map[string]string),
Tags: make([]string, 0),
}
}
func (s *ServiceInstance) WithMetadata(key, value string) *ServiceInstance {
s.Metadata[key] = value
return s
}
func (s *ServiceInstance) WithTags(tags ...string) *ServiceInstance {
s.Tags = append(s.Tags, tags...)
return s
}
---
b.配置管理
a.功能说明
配置管理提供服务发现的参数配置,包括注册中心地址、超时时间、重试策略等。支持配置热更新,运行时调整参数。配置应该支持多环境,开发、测试、生产使用不同配置。提供配置验证,确保参数合法性。配置管理应该集中化,便于维护。支持配置加密,保护敏感信息。
b.代码示例
---
package sd
import "time"
type Config struct {
Endpoints []string
Timeout time.Duration
RetryAttempts int
HealthCheckTTL time.Duration
DeregisterAfter time.Duration
}
func DefaultConfig() *Config {
return &Config{
Endpoints: []string{"localhost:8500"},
Timeout: 10 * time.Second,
RetryAttempts: 3,
HealthCheckTTL: 30 * time.Second,
DeregisterAfter: 1 * time.Minute,
}
}
func (c *Config) Validate() error {
if len(c.Endpoints) == 0 {
return errors.New("endpoints required")
}
if c.Timeout <= 0 {
return errors.New("invalid timeout")
}
return nil
}
func (c *Config) WithEndpoints(endpoints ...string) *Config {
c.Endpoints = endpoints
return c
}
---
02.服务注册
a.实现方��
a.功能说明
服务注册提供服务的具体实现方式,包括API调用、配置文件等。支持批量操作,提高效率。提供错误处理和重试机制。服务注册应该是幂等的,重复调用不产生副作用。支持异步操作,避免阻塞。提供操作日志,便于审计和排查。
b.代码示例
---
package sd
import "context"
type Client struct {
config *Config
conn Connection
}
func NewClient(config *Config) (*Client, error) {
if err := config.Validate(); err != nil {
return nil, err
}
conn, err := connect(config.Endpoints)
if err != nil {
return nil, err
}
return &Client{config: config, conn: conn}, nil
}
func (c *Client) Register(ctx context.Context, instance *ServiceInstance) error {
for i := 0; i < c.config.RetryAttempts; i++ {
if err := c.conn.Register(ctx, instance); err == nil {
return nil
}
time.Sleep(time.Second * time.Duration(i+1))
}
return errors.New("register failed after retries")
}
func (c *Client) Deregister(ctx context.Context, instanceID string) error {
return c.conn.Deregister(ctx, instanceID)
}
func (c *Client) Close() error {
return c.conn.Close()
}
---
b.错误处理
a.功能说明
错误处理确保服务发现操作的可靠性。区分临时错误和永久错误,采取不同策略。临时错误支持重试,永久错误直接返回。提供详细的错误信息,便于问题定位。错误处理应该记录日志。支持错误回调,通知上层应用。错误处理不应该影响系统稳定性。
b.代码示例
---
package sd
import "errors"
type ErrorType int
const (
ErrorTypeTemporary ErrorType = iota
ErrorTypePermanent
)
type SDError struct {
Type ErrorType
Message string
Cause error
}
func (e *SDError) Error() string {
return e.Message
}
func (e *SDError) Unwrap() error {
return e.Cause
}
func HandleError(err error, retryFunc func() error) error {
if err == nil {
return nil
}
sdErr, ok := err.(*SDError)
if !ok {
return err
}
if sdErr.Type == ErrorTypeTemporary {
return retryFunc()
}
return sdErr
}
---
03.服务发现
a.核心功能
a.功能说明
服务发现是服务发现的关键功能,提供服务的核心能力。支持实时更新,快速响应变化。提供高性能查询,满足高并发需求。服务发现应该是可扩展的,支持自定义扩展。提供监控指标,便于性能分析。服务发现应该容错,部分失败不影响整体。
b.代码示例
---
package sd
import ("context"; "sync")
type Manager struct {
instances map[string][]*ServiceInstance
mu sync.RWMutex
watchers []Watcher
}
func NewManager() *Manager {
return &Manager{
instances: make(map[string][]*ServiceInstance),
watchers: make([]Watcher, 0),
}
}
func (m *Manager) Add(instance *ServiceInstance) {
m.mu.Lock()
defer m.mu.Unlock()
m.instances[instance.Name] = append(m.instances[instance.Name], instance)
m.notifyWatchers(instance.Name)
}
func (m *Manager) Remove(instanceID string) {
m.mu.Lock()
defer m.mu.Unlock()
for name, instances := range m.instances {
for i, inst := range instances {
if inst.ID == instanceID {
m.instances[name] = append(instances[:i], instances[i+1:]...)
m.notifyWatchers(name)
return
}
}
}
}
func (m *Manager) Get(serviceName string) []*ServiceInstance {
m.mu.RLock()
defer m.mu.RUnlock()
return m.instances[serviceName]
}
func (m *Manager) Watch(watcher Watcher) {
m.mu.Lock()
defer m.mu.Unlock()
m.watchers = append(m.watchers, watcher)
}
func (m *Manager) notifyWatchers(serviceName string) {
for _, watcher := range m.watchers {
go watcher.OnChange(serviceName, m.instances[serviceName])
}
}
type Watcher interface {
OnChange(serviceName string, instances []*ServiceInstance)
}
---
b.最佳实践
a.功能说明
最佳实践总结服务发现的经验和建议。合理设置超时和重试参数。使用连接池提高性能。定期清理过期数据。监控服务发现的性能指标。做好容错和降级处理。定期测试故障场景。保持服务发现组件的版本更新。
b.代码示例
---
package sd
import "time"
type BestPractices struct {
client *Client
healthChecker *HealthChecker
cache *Cache
}
func NewBestPractices(config *Config) (*BestPractices, error) {
client, err := NewClient(config)
if err != nil {
return nil, err
}
return &BestPractices{
client: client,
healthChecker: NewHealthChecker(config.HealthCheckTTL),
cache: NewCache(5 * time.Minute),
}, nil
}
func (bp *BestPractices) RegisterWithHealthCheck(ctx context.Context, instance *ServiceInstance) error {
if err := bp.client.Register(ctx, instance); err != nil {
return err
}
bp.healthChecker.Start(instance.ID, func() bool {
return checkHealth(instance)
})
return nil
}
func (bp *BestPractices) DiscoverWithCache(ctx context.Context, serviceName string) ([]*ServiceInstance, error) {
if cached := bp.cache.Get(serviceName); cached != nil {
return cached.([]*ServiceInstance), nil
}
instances, err := bp.client.Discover(ctx, serviceName)
if err != nil {
return nil, err
}
bp.cache.Set(serviceName, instances)
return instances, nil
}
---
6.3 Etcd集成
01.Etcd客户端
a.基本实现
a.功能说明
Etcd客户端是服务发现的核心功能,提供服务的注册和管理能力。支持服务元数据存储,包括地址、端口、版本等信息。提供服务健康检查,自动剔除不健康实例。支持服务分组和标签,实现灵活的服务管理。Etcd客户端应该高可用,避免单点故障。支持动态更新,实时反映服务状态变化。
b.代码示例
---
package sd
import ("context"; "time")
type Registrar interface {
Register() error
Deregister() error
}
type ServiceInstance struct {
ID string
Name string
Address string
Port int
Metadata map[string]string
Tags []string
}
func NewServiceInstance(name, address string, port int) *ServiceInstance {
return &ServiceInstance{
ID: generateID(),
Name: name,
Address: address,
Port: port,
Metadata: make(map[string]string),
Tags: make([]string, 0),
}
}
func (s *ServiceInstance) WithMetadata(key, value string) *ServiceInstance {
s.Metadata[key] = value
return s
}
func (s *ServiceInstance) WithTags(tags ...string) *ServiceInstance {
s.Tags = append(s.Tags, tags...)
return s
}
---
b.配置管理
a.功能说明
配置管理提供服务发现的参数配置,包括注册中心地址、超时时间、重试策略等。支持配置热更新,运行时调整参数。配置应该支持多环境,开发、测试、生产使用不同配置。提供配置验证,确保参数合法性。配置管理应该集中化,便于维护。支持配置加密,保护敏感信息。
b.代码示例
---
package sd
import "time"
type Config struct {
Endpoints []string
Timeout time.Duration
RetryAttempts int
HealthCheckTTL time.Duration
DeregisterAfter time.Duration
}
func DefaultConfig() *Config {
return &Config{
Endpoints: []string{"localhost:8500"},
Timeout: 10 * time.Second,
RetryAttempts: 3,
HealthCheckTTL: 30 * time.Second,
DeregisterAfter: 1 * time.Minute,
}
}
func (c *Config) Validate() error {
if len(c.Endpoints) == 0 {
return errors.New("endpoints required")
}
if c.Timeout <= 0 {
return errors.New("invalid timeout")
}
return nil
}
func (c *Config) WithEndpoints(endpoints ...string) *Config {
c.Endpoints = endpoints
return c
}
---
02.服务注册
a.实现方��
a.功能说明
服务注册提供服务的具体实现方式,包括API调用、配置文件等。支持批量操作,提高效率。提供错误处理和重试机制。服务注册应该是幂等的,重复调用不产生副作用。支持异步操作,避免阻塞。提供操作日志,便于审计和排查。
b.代码示例
---
package sd
import "context"
type Client struct {
config *Config
conn Connection
}
func NewClient(config *Config) (*Client, error) {
if err := config.Validate(); err != nil {
return nil, err
}
conn, err := connect(config.Endpoints)
if err != nil {
return nil, err
}
return &Client{config: config, conn: conn}, nil
}
func (c *Client) Register(ctx context.Context, instance *ServiceInstance) error {
for i := 0; i < c.config.RetryAttempts; i++ {
if err := c.conn.Register(ctx, instance); err == nil {
return nil
}
time.Sleep(time.Second * time.Duration(i+1))
}
return errors.New("register failed after retries")
}
func (c *Client) Deregister(ctx context.Context, instanceID string) error {
return c.conn.Deregister(ctx, instanceID)
}
func (c *Client) Close() error {
return c.conn.Close()
}
---
b.错误处理
a.功能说明
错误处理确保服务发现操作的可靠性。区分临时错误和永久错误,采取不同策略。临时错误支持重试,永久错误直接返回。提供详细的错误信息,便于问题定位。错误处理应该记录日志。支持错误回调,通知上层应用。错误处理不应该影响系统稳定性。
b.代码示例
---
package sd
import "errors"
type ErrorType int
const (
ErrorTypeTemporary ErrorType = iota
ErrorTypePermanent
)
type SDError struct {
Type ErrorType
Message string
Cause error
}
func (e *SDError) Error() string {
return e.Message
}
func (e *SDError) Unwrap() error {
return e.Cause
}
func HandleError(err error, retryFunc func() error) error {
if err == nil {
return nil
}
sdErr, ok := err.(*SDError)
if !ok {
return err
}
if sdErr.Type == ErrorTypeTemporary {
return retryFunc()
}
return sdErr
}
---
03.服务发现
a.核心功能
a.功能说明
服务发现是服务发现的关键功能,提供服务的核心能力。支持实时更新,快速响应变化。提供高性能查询,满足高并发需求。服务发现应该是可扩展的,支持自定义扩展。提供监控指标,便于性能分析。服务发现应该容错,部分失败不影响整体。
b.代码示例
---
package sd
import ("context"; "sync")
type Manager struct {
instances map[string][]*ServiceInstance
mu sync.RWMutex
watchers []Watcher
}
func NewManager() *Manager {
return &Manager{
instances: make(map[string][]*ServiceInstance),
watchers: make([]Watcher, 0),
}
}
func (m *Manager) Add(instance *ServiceInstance) {
m.mu.Lock()
defer m.mu.Unlock()
m.instances[instance.Name] = append(m.instances[instance.Name], instance)
m.notifyWatchers(instance.Name)
}
func (m *Manager) Remove(instanceID string) {
m.mu.Lock()
defer m.mu.Unlock()
for name, instances := range m.instances {
for i, inst := range instances {
if inst.ID == instanceID {
m.instances[name] = append(instances[:i], instances[i+1:]...)
m.notifyWatchers(name)
return
}
}
}
}
func (m *Manager) Get(serviceName string) []*ServiceInstance {
m.mu.RLock()
defer m.mu.RUnlock()
return m.instances[serviceName]
}
func (m *Manager) Watch(watcher Watcher) {
m.mu.Lock()
defer m.mu.Unlock()
m.watchers = append(m.watchers, watcher)
}
func (m *Manager) notifyWatchers(serviceName string) {
for _, watcher := range m.watchers {
go watcher.OnChange(serviceName, m.instances[serviceName])
}
}
type Watcher interface {
OnChange(serviceName string, instances []*ServiceInstance)
}
---
b.最佳实践
a.功能说明
最佳实践总结服务发现的经验和建议。合理设置超时和重试参数。使用连接池提高性能。定期清理过期数据。监控服务发现的性能指标。做好容错和降级处理。定期测试故障场景。保持服务发现组件的版本更新。
b.代码示例
---
package sd
import "time"
type BestPractices struct {
client *Client
healthChecker *HealthChecker
cache *Cache
}
func NewBestPractices(config *Config) (*BestPractices, error) {
client, err := NewClient(config)
if err != nil {
return nil, err
}
return &BestPractices{
client: client,
healthChecker: NewHealthChecker(config.HealthCheckTTL),
cache: NewCache(5 * time.Minute),
}, nil
}
func (bp *BestPractices) RegisterWithHealthCheck(ctx context.Context, instance *ServiceInstance) error {
if err := bp.client.Register(ctx, instance); err != nil {
return err
}
bp.healthChecker.Start(instance.ID, func() bool {
return checkHealth(instance)
})
return nil
}
func (bp *BestPractices) DiscoverWithCache(ctx context.Context, serviceName string) ([]*ServiceInstance, error) {
if cached := bp.cache.Get(serviceName); cached != nil {
return cached.([]*ServiceInstance), nil
}
instances, err := bp.client.Discover(ctx, serviceName)
if err != nil {
return nil, err
}
bp.cache.Set(serviceName, instances)
return instances, nil
}
---
6.4 Eureka集成
01.Eureka客户端
a.基本实现
a.功能说明
Eureka客户端是服务发现的核心功能,提供服务的注册和管理能力。支持服务元数据存储,包括地址、端口、版本等信息。提供服务健康检查,自动剔除不健康实例。支持服务分组和标签,实现灵活的服务管理。Eureka客户端应该高可用,避免单点故障。支持动态更新,实时反映服务状态变化。
b.代码示例
---
package sd
import ("context"; "time")
type Registrar interface {
Register() error
Deregister() error
}
type ServiceInstance struct {
ID string
Name string
Address string
Port int
Metadata map[string]string
Tags []string
}
func NewServiceInstance(name, address string, port int) *ServiceInstance {
return &ServiceInstance{
ID: generateID(),
Name: name,
Address: address,
Port: port,
Metadata: make(map[string]string),
Tags: make([]string, 0),
}
}
func (s *ServiceInstance) WithMetadata(key, value string) *ServiceInstance {
s.Metadata[key] = value
return s
}
func (s *ServiceInstance) WithTags(tags ...string) *ServiceInstance {
s.Tags = append(s.Tags, tags...)
return s
}
---
b.配置管理
a.功能说明
配置管理提供服务发现的参数配置,包括注册中心地址、超时时间、重试策略等。支持配置热更新,运行时调整参数。配置应该支持多环境,开发、测试、生产使用不同配置。提供配置验证,确保参数合法性。配置管理应该集中化,便于维护。支持配置加密,保护敏感信息。
b.代码示例
---
package sd
import "time"
type Config struct {
Endpoints []string
Timeout time.Duration
RetryAttempts int
HealthCheckTTL time.Duration
DeregisterAfter time.Duration
}
func DefaultConfig() *Config {
return &Config{
Endpoints: []string{"localhost:8500"},
Timeout: 10 * time.Second,
RetryAttempts: 3,
HealthCheckTTL: 30 * time.Second,
DeregisterAfter: 1 * time.Minute,
}
}
func (c *Config) Validate() error {
if len(c.Endpoints) == 0 {
return errors.New("endpoints required")
}
if c.Timeout <= 0 {
return errors.New("invalid timeout")
}
return nil
}
func (c *Config) WithEndpoints(endpoints ...string) *Config {
c.Endpoints = endpoints
return c
}
---
02.服务注册
a.实现方��
a.功能说明
服务注册提供服务的具体实现方式,包括API调用、配置文件等。支持批量操作,提高效率。提供错误处理和重试机制。服务注册应该是幂等的,重复调用不产生副作用。支持异步操作,避免阻塞。提供操作日志,便于审计和排查。
b.代码示例
---
package sd
import "context"
type Client struct {
config *Config
conn Connection
}
func NewClient(config *Config) (*Client, error) {
if err := config.Validate(); err != nil {
return nil, err
}
conn, err := connect(config.Endpoints)
if err != nil {
return nil, err
}
return &Client{config: config, conn: conn}, nil
}
func (c *Client) Register(ctx context.Context, instance *ServiceInstance) error {
for i := 0; i < c.config.RetryAttempts; i++ {
if err := c.conn.Register(ctx, instance); err == nil {
return nil
}
time.Sleep(time.Second * time.Duration(i+1))
}
return errors.New("register failed after retries")
}
func (c *Client) Deregister(ctx context.Context, instanceID string) error {
return c.conn.Deregister(ctx, instanceID)
}
func (c *Client) Close() error {
return c.conn.Close()
}
---
b.错误处理
a.功能说明
错误处理确保服务发现操作的可靠性。区分临时错误和永久错误,采取不同策略。临时错误支持重试,永久错误直接返回。提供详细的错误信息,便于问题定位。错误处理应该记录日志。支持错误回调,通知上层应用。错误处理不应该影响系统稳定性。
b.代码示例
---
package sd
import "errors"
type ErrorType int
const (
ErrorTypeTemporary ErrorType = iota
ErrorTypePermanent
)
type SDError struct {
Type ErrorType
Message string
Cause error
}
func (e *SDError) Error() string {
return e.Message
}
func (e *SDError) Unwrap() error {
return e.Cause
}
func HandleError(err error, retryFunc func() error) error {
if err == nil {
return nil
}
sdErr, ok := err.(*SDError)
if !ok {
return err
}
if sdErr.Type == ErrorTypeTemporary {
return retryFunc()
}
return sdErr
}
---
03.服务发现
a.核心功能
a.功能说明
服务发现是服务发现的关键功能,提供服务的核心能力。支持实时更新,快速响应变化。提供高性能查询,满足高并发需求。服务发现应该是可扩展的,支持自定义扩展。提供监控指标,便于性能分析。服务发现应该容错,部分失败不影响整体。
b.代码示例
---
package sd
import ("context"; "sync")
type Manager struct {
instances map[string][]*ServiceInstance
mu sync.RWMutex
watchers []Watcher
}
func NewManager() *Manager {
return &Manager{
instances: make(map[string][]*ServiceInstance),
watchers: make([]Watcher, 0),
}
}
func (m *Manager) Add(instance *ServiceInstance) {
m.mu.Lock()
defer m.mu.Unlock()
m.instances[instance.Name] = append(m.instances[instance.Name], instance)
m.notifyWatchers(instance.Name)
}
func (m *Manager) Remove(instanceID string) {
m.mu.Lock()
defer m.mu.Unlock()
for name, instances := range m.instances {
for i, inst := range instances {
if inst.ID == instanceID {
m.instances[name] = append(instances[:i], instances[i+1:]...)
m.notifyWatchers(name)
return
}
}
}
}
func (m *Manager) Get(serviceName string) []*ServiceInstance {
m.mu.RLock()
defer m.mu.RUnlock()
return m.instances[serviceName]
}
func (m *Manager) Watch(watcher Watcher) {
m.mu.Lock()
defer m.mu.Unlock()
m.watchers = append(m.watchers, watcher)
}
func (m *Manager) notifyWatchers(serviceName string) {
for _, watcher := range m.watchers {
go watcher.OnChange(serviceName, m.instances[serviceName])
}
}
type Watcher interface {
OnChange(serviceName string, instances []*ServiceInstance)
}
---
b.最佳实践
a.功能说明
最佳实践总结服务发现的经验和建议。合理设置超时和重试参数。使用连接池提高性能。定期清理过期数据。监控服务发现的性能指标。做好容错和降级处理。定期测试故障场景。保持服务发现组件的版本更新。
b.代码示例
---
package sd
import "time"
type BestPractices struct {
client *Client
healthChecker *HealthChecker
cache *Cache
}
func NewBestPractices(config *Config) (*BestPractices, error) {
client, err := NewClient(config)
if err != nil {
return nil, err
}
return &BestPractices{
client: client,
healthChecker: NewHealthChecker(config.HealthCheckTTL),
cache: NewCache(5 * time.Minute),
}, nil
}
func (bp *BestPractices) RegisterWithHealthCheck(ctx context.Context, instance *ServiceInstance) error {
if err := bp.client.Register(ctx, instance); err != nil {
return err
}
bp.healthChecker.Start(instance.ID, func() bool {
return checkHealth(instance)
})
return nil
}
func (bp *BestPractices) DiscoverWithCache(ctx context.Context, serviceName string) ([]*ServiceInstance, error) {
if cached := bp.cache.Get(serviceName); cached != nil {
return cached.([]*ServiceInstance), nil
}
instances, err := bp.client.Discover(ctx, serviceName)
if err != nil {
return nil, err
}
bp.cache.Set(serviceName, instances)
return instances, nil
}
---
6.5 负载均衡
01.负载均衡策略
a.基本实现
a.功能说明
负载均衡策略是服务发现的核心功能,提供服务的注册和管理能力。支持服务元数据存储,包括地址、端口、版本等信息。提供服务健康检查,自动剔除不健康实例。支持服务分组和标签,实现灵活的服务管理。负载均衡策略应该高可用,避免单点故障。支持动态更新,实时反映服务状态变化。
b.代码示例
---
package sd
import ("context"; "time")
type Registrar interface {
Register() error
Deregister() error
}
type ServiceInstance struct {
ID string
Name string
Address string
Port int
Metadata map[string]string
Tags []string
}
func NewServiceInstance(name, address string, port int) *ServiceInstance {
return &ServiceInstance{
ID: generateID(),
Name: name,
Address: address,
Port: port,
Metadata: make(map[string]string),
Tags: make([]string, 0),
}
}
func (s *ServiceInstance) WithMetadata(key, value string) *ServiceInstance {
s.Metadata[key] = value
return s
}
func (s *ServiceInstance) WithTags(tags ...string) *ServiceInstance {
s.Tags = append(s.Tags, tags...)
return s
}
---
b.配置管理
a.功能说明
配置管理提供服务发现的参数配置,包括注册中心地址、超时时间、重试策略等。支持配置热更新,运行时调整参数。配置应该支持多环境,开发、测试、生产使用不同配置。提供配置验证,确保参数合法性。配置管理应该集中化,便于维护。支持配置加密,保护敏感信息。
b.代码示例
---
package sd
import "time"
type Config struct {
Endpoints []string
Timeout time.Duration
RetryAttempts int
HealthCheckTTL time.Duration
DeregisterAfter time.Duration
}
func DefaultConfig() *Config {
return &Config{
Endpoints: []string{"localhost:8500"},
Timeout: 10 * time.Second,
RetryAttempts: 3,
HealthCheckTTL: 30 * time.Second,
DeregisterAfter: 1 * time.Minute,
}
}
func (c *Config) Validate() error {
if len(c.Endpoints) == 0 {
return errors.New("endpoints required")
}
if c.Timeout <= 0 {
return errors.New("invalid timeout")
}
return nil
}
func (c *Config) WithEndpoints(endpoints ...string) *Config {
c.Endpoints = endpoints
return c
}
---
02.自定义负载均衡
a.实现方��
a.功能说明
自定义负载均衡提供服务的具体实现方式,包括API调用、配置文件等。支持批量操作,提高效率。提供错误处理和重试机制。自定义负载均衡应该是幂等的,重复调用不产生副作用。支持异步操作,避免阻塞。提供操作日志,便于审计和排查。
b.代码示例
---
package sd
import "context"
type Client struct {
config *Config
conn Connection
}
func NewClient(config *Config) (*Client, error) {
if err := config.Validate(); err != nil {
return nil, err
}
conn, err := connect(config.Endpoints)
if err != nil {
return nil, err
}
return &Client{config: config, conn: conn}, nil
}
func (c *Client) Register(ctx context.Context, instance *ServiceInstance) error {
for i := 0; i < c.config.RetryAttempts; i++ {
if err := c.conn.Register(ctx, instance); err == nil {
return nil
}
time.Sleep(time.Second * time.Duration(i+1))
}
return errors.New("register failed after retries")
}
func (c *Client) Deregister(ctx context.Context, instanceID string) error {
return c.conn.Deregister(ctx, instanceID)
}
func (c *Client) Close() error {
return c.conn.Close()
}
---
b.错误处理
a.功能说明
错误处理确保服务发现操作的可靠性。区分临时错误和永久错误,采取不同策略。临时错误支持重试,永久错误直接返回。提供详细的错误信息,便于问题定位。错误处理应该记录日志。支持错误回调,通知上层应用。错误处理不应该影响系统稳定性。
b.代码示例
---
package sd
import "errors"
type ErrorType int
const (
ErrorTypeTemporary ErrorType = iota
ErrorTypePermanent
)
type SDError struct {
Type ErrorType
Message string
Cause error
}
func (e *SDError) Error() string {
return e.Message
}
func (e *SDError) Unwrap() error {
return e.Cause
}
func HandleError(err error, retryFunc func() error) error {
if err == nil {
return nil
}
sdErr, ok := err.(*SDError)
if !ok {
return err
}
if sdErr.Type == ErrorTypeTemporary {
return retryFunc()
}
return sdErr
}
---
03.故障转移
a.核心功能
a.功能说明
故障转移是服务发现的关键功能,提供服务的核心能力。支持实时更新,快速响应变化。提供高性能查询,满足高并发需求。故障转移应该是可扩展的,支持自定义扩展。提供监控指标,便于性能分析。故障转移应该容错,部分失败不影响整体。
b.代码示例
---
package sd
import ("context"; "sync")
type Manager struct {
instances map[string][]*ServiceInstance
mu sync.RWMutex
watchers []Watcher
}
func NewManager() *Manager {
return &Manager{
instances: make(map[string][]*ServiceInstance),
watchers: make([]Watcher, 0),
}
}
func (m *Manager) Add(instance *ServiceInstance) {
m.mu.Lock()
defer m.mu.Unlock()
m.instances[instance.Name] = append(m.instances[instance.Name], instance)
m.notifyWatchers(instance.Name)
}
func (m *Manager) Remove(instanceID string) {
m.mu.Lock()
defer m.mu.Unlock()
for name, instances := range m.instances {
for i, inst := range instances {
if inst.ID == instanceID {
m.instances[name] = append(instances[:i], instances[i+1:]...)
m.notifyWatchers(name)
return
}
}
}
}
func (m *Manager) Get(serviceName string) []*ServiceInstance {
m.mu.RLock()
defer m.mu.RUnlock()
return m.instances[serviceName]
}
func (m *Manager) Watch(watcher Watcher) {
m.mu.Lock()
defer m.mu.Unlock()
m.watchers = append(m.watchers, watcher)
}
func (m *Manager) notifyWatchers(serviceName string) {
for _, watcher := range m.watchers {
go watcher.OnChange(serviceName, m.instances[serviceName])
}
}
type Watcher interface {
OnChange(serviceName string, instances []*ServiceInstance)
}
---
b.最佳实践
a.功能说明
最佳实践总结服务发现的经验和建议。合理设置超时和重试参数。使用连接池提高性能。定期清理过期数据。监控服务发现的性能指标。做好容错和降级处理。定期测试故障场景。保持服务发现组件的版本更新。
b.代码示例
---
package sd
import "time"
type BestPractices struct {
client *Client
healthChecker *HealthChecker
cache *Cache
}
func NewBestPractices(config *Config) (*BestPractices, error) {
client, err := NewClient(config)
if err != nil {
return nil, err
}
return &BestPractices{
client: client,
healthChecker: NewHealthChecker(config.HealthCheckTTL),
cache: NewCache(5 * time.Minute),
}, nil
}
func (bp *BestPractices) RegisterWithHealthCheck(ctx context.Context, instance *ServiceInstance) error {
if err := bp.client.Register(ctx, instance); err != nil {
return err
}
bp.healthChecker.Start(instance.ID, func() bool {
return checkHealth(instance)
})
return nil
}
func (bp *BestPractices) DiscoverWithCache(ctx context.Context, serviceName string) ([]*ServiceInstance, error) {
if cached := bp.cache.Get(serviceName); cached != nil {
return cached.([]*ServiceInstance), nil
}
instances, err := bp.client.Discover(ctx, serviceName)
if err != nil {
return nil, err
}
bp.cache.Set(serviceName, instances)
return instances, nil
}
---
6.6 健康检查
01.健康检查机制
a.基本实现
a.功能说明
健康检查机制是服务发现的核心功能,提供服务的注册和管理能力。支持服务元数据存储,包括地址、端口、版本等信息。提供服务健康检查,自动剔除不健康实例。支持服务分组和标签,实现灵活的服务管理。健康检查机制应该高可用,避免单点故障。支持动态更新,实时反映服务状态变化。
b.代码示例
---
package sd
import ("context"; "time")
type Registrar interface {
Register() error
Deregister() error
}
type ServiceInstance struct {
ID string
Name string
Address string
Port int
Metadata map[string]string
Tags []string
}
func NewServiceInstance(name, address string, port int) *ServiceInstance {
return &ServiceInstance{
ID: generateID(),
Name: name,
Address: address,
Port: port,
Metadata: make(map[string]string),
Tags: make([]string, 0),
}
}
func (s *ServiceInstance) WithMetadata(key, value string) *ServiceInstance {
s.Metadata[key] = value
return s
}
func (s *ServiceInstance) WithTags(tags ...string) *ServiceInstance {
s.Tags = append(s.Tags, tags...)
return s
}
---
b.配置管理
a.功能说明
配置管理提供服务发现的参数配置,包括注册中心地址、超时时间、重试策略等。支持配置热更新,运行时调整参数。配置应该支持多环境,开发、测试、生产使用不同配置。提供配置验证,确保参数合法性。配置管理应该集中化,便于维护。支持配置加密,保护敏感信息。
b.代码示例
---
package sd
import "time"
type Config struct {
Endpoints []string
Timeout time.Duration
RetryAttempts int
HealthCheckTTL time.Duration
DeregisterAfter time.Duration
}
func DefaultConfig() *Config {
return &Config{
Endpoints: []string{"localhost:8500"},
Timeout: 10 * time.Second,
RetryAttempts: 3,
HealthCheckTTL: 30 * time.Second,
DeregisterAfter: 1 * time.Minute,
}
}
func (c *Config) Validate() error {
if len(c.Endpoints) == 0 {
return errors.New("endpoints required")
}
if c.Timeout <= 0 {
return errors.New("invalid timeout")
}
return nil
}
func (c *Config) WithEndpoints(endpoints ...string) *Config {
c.Endpoints = endpoints
return c
}
---
02.自定义健康检查
a.实现方��
a.功能说明
自定义健康检查提供服务的具体实现方式,包括API调用、配置文件等。支持批量操作,提高效率。提供错误处理和重试机制。自定义健康检查应该是幂等的,重复调用不产生副作用。支持异步操作,避免阻塞。提供操作日志,便于审计和排查。
b.代码示例
---
package sd
import "context"
type Client struct {
config *Config
conn Connection
}
func NewClient(config *Config) (*Client, error) {
if err := config.Validate(); err != nil {
return nil, err
}
conn, err := connect(config.Endpoints)
if err != nil {
return nil, err
}
return &Client{config: config, conn: conn}, nil
}
func (c *Client) Register(ctx context.Context, instance *ServiceInstance) error {
for i := 0; i < c.config.RetryAttempts; i++ {
if err := c.conn.Register(ctx, instance); err == nil {
return nil
}
time.Sleep(time.Second * time.Duration(i+1))
}
return errors.New("register failed after retries")
}
func (c *Client) Deregister(ctx context.Context, instanceID string) error {
return c.conn.Deregister(ctx, instanceID)
}
func (c *Client) Close() error {
return c.conn.Close()
}
---
b.错误处理
a.功能说明
错误处理确保服务发现操作的可靠性。区分临时错误和永久错误,采取不同策略。临时错误支持重试,永久错误直接返回。提供详细的错误信息,便于问题定位。错误处理应该记录日志。支持错误回调,通知上层应用。错误处理不应该影响系统稳定性。
b.代码示例
---
package sd
import "errors"
type ErrorType int
const (
ErrorTypeTemporary ErrorType = iota
ErrorTypePermanent
)
type SDError struct {
Type ErrorType
Message string
Cause error
}
func (e *SDError) Error() string {
return e.Message
}
func (e *SDError) Unwrap() error {
return e.Cause
}
func HandleError(err error, retryFunc func() error) error {
if err == nil {
return nil
}
sdErr, ok := err.(*SDError)
if !ok {
return err
}
if sdErr.Type == ErrorTypeTemporary {
return retryFunc()
}
return sdErr
}
---
03.健康状态管理
a.核心功能
a.功能说明
健康状态管理是服务发现的关键功能,提供服务的核心能力。支持实时更新,快速响应变化。提供高性能查询,满足高并发需求。健康状态管理应该是可扩展的,支持自定义扩展。提供监控指标,便于性能分析。健康状态管理应该容错,部分失败不影响整体。
b.代码示例
---
package sd
import ("context"; "sync")
type Manager struct {
instances map[string][]*ServiceInstance
mu sync.RWMutex
watchers []Watcher
}
func NewManager() *Manager {
return &Manager{
instances: make(map[string][]*ServiceInstance),
watchers: make([]Watcher, 0),
}
}
func (m *Manager) Add(instance *ServiceInstance) {
m.mu.Lock()
defer m.mu.Unlock()
m.instances[instance.Name] = append(m.instances[instance.Name], instance)
m.notifyWatchers(instance.Name)
}
func (m *Manager) Remove(instanceID string) {
m.mu.Lock()
defer m.mu.Unlock()
for name, instances := range m.instances {
for i, inst := range instances {
if inst.ID == instanceID {
m.instances[name] = append(instances[:i], instances[i+1:]...)
m.notifyWatchers(name)
return
}
}
}
}
func (m *Manager) Get(serviceName string) []*ServiceInstance {
m.mu.RLock()
defer m.mu.RUnlock()
return m.instances[serviceName]
}
func (m *Manager) Watch(watcher Watcher) {
m.mu.Lock()
defer m.mu.Unlock()
m.watchers = append(m.watchers, watcher)
}
func (m *Manager) notifyWatchers(serviceName string) {
for _, watcher := range m.watchers {
go watcher.OnChange(serviceName, m.instances[serviceName])
}
}
type Watcher interface {
OnChange(serviceName string, instances []*ServiceInstance)
}
---
b.最佳实践
a.功能说明
最佳实践总结服务发现的经验和建议。合理设置超时和重试参数。使用连接池提高性能。定期清理过期数据。监控服务发现的性能指标。做好容错和降级处理。定期测试故障场景。保持服务发现组件的版本更新。
b.代码示例
---
package sd
import "time"
type BestPractices struct {
client *Client
healthChecker *HealthChecker
cache *Cache
}
func NewBestPractices(config *Config) (*BestPractices, error) {
client, err := NewClient(config)
if err != nil {
return nil, err
}
return &BestPractices{
client: client,
healthChecker: NewHealthChecker(config.HealthCheckTTL),
cache: NewCache(5 * time.Minute),
}, nil
}
func (bp *BestPractices) RegisterWithHealthCheck(ctx context.Context, instance *ServiceInstance) error {
if err := bp.client.Register(ctx, instance); err != nil {
return err
}
bp.healthChecker.Start(instance.ID, func() bool {
return checkHealth(instance)
})
return nil
}
func (bp *BestPractices) DiscoverWithCache(ctx context.Context, serviceName string) ([]*ServiceInstance, error) {
if cached := bp.cache.Get(serviceName); cached != nil {
return cached.([]*ServiceInstance), nil
}
instances, err := bp.client.Discover(ctx, serviceName)
if err != nil {
return nil, err
}
bp.cache.Set(serviceName, instances)
return instances, nil
}
---
7 实战应用
7.1 项目搭建
01.项目结构
a.基本概念
a.功能说明
项目结构是实战应用的基础部分,提供项目的基本框架和结构。遵循go-kit的最佳实践,采用清晰的分层架构。项目结构应该易于理解和维护,便于团队协作。支持模块化设计,各模块职责清晰。提供完整的示例代码,便于学习和参考。项目结构应该考虑可扩展性,便于后续功能添加。
b.代码示例
---
package main
import ("context"; "fmt"; "net/http"; "os"; "os/signal")
func main() {
var (
httpAddr = ":8080"
logger = log.NewLogfmtLogger(os.Stderr)
)
var svc Service
svc = NewBasicService()
svc = LoggingMiddleware(logger)(svc)
endpoints := MakeEndpoints(svc)
endpoints = ApplyMiddleware(endpoints, logger)
handler := MakeHTTPHandler(endpoints, logger)
errs := make(chan error)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", httpAddr)
errs <- http.ListenAndServe(httpAddr, handler)
}()
logger.Log("exit", <-errs)
}
type Service interface {
CreateUser(ctx context.Context, username, email string) (User, error)
GetUser(ctx context.Context, id string) (User, error)
}
type basicService struct{}
func NewBasicService() Service {
return &basicService{}
}
func (s *basicService) CreateUser(ctx context.Context, username, email string) (User, error) {
return User{ID: generateID(), Username: username, Email: email}, nil
}
func (s *basicService) GetUser(ctx context.Context, id string) (User, error) {
return User{ID: id, Username: "user", Email: "[email protected]"}, nil
}
---
b.实现细节
a.功能说明
实现细节包括具体的代码实现和技术选型。使用标准库和成熟的第三方库。代码应该遵循Go语言规范,保持一致的风格。提供详细的注释,说明关键逻辑。实现应该考虑性能和资源使用。提供错误处理和日志记录。实现应该易于测试和调试。
b.代码示例
---
package service
import "context"
type Endpoints struct {
CreateUserEndpoint endpoint.Endpoint
GetUserEndpoint endpoint.Endpoint
}
func MakeEndpoints(s Service) Endpoints {
return Endpoints{
CreateUserEndpoint: MakeCreateUserEndpoint(s),
GetUserEndpoint: MakeGetUserEndpoint(s),
}
}
func MakeCreateUserEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := s.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err}, nil
}
return CreateUserResponse{User: user}, nil
}
}
type CreateUserRequest struct {
Username string `json:"username"`
Email string `json:"email"`
}
type CreateUserResponse struct {
User User `json:"user,omitempty"`
Err error `json:"error,omitempty"`
}
---
02.依赖管理
a.核心功能
a.功能说明
依赖管理提供项目的核心功能实现,是系统的主要业务逻辑。功能应该完整且健壮,处理各种边界情况。提供清晰的API接口,便于调用。依赖管理应该高性能,满足业务需求。支持并发处理,充分利用系统资源。提供完善的错误处理和日志记录。
b.代码示例
---
package transport
import ("context"; "encoding/json"; "net/http")
func MakeHTTPHandler(endpoints Endpoints, logger log.Logger) http.Handler {
m := http.NewServeMux()
m.Handle("/users", httptransport.NewServer(
endpoints.CreateUserEndpoint,
decodeCreateUserRequest,
encodeResponse,
))
m.Handle("/users/", httptransport.NewServer(
endpoints.GetUserEndpoint,
decodeGetUserRequest,
encodeResponse,
))
return m
}
func decodeCreateUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
func decodeGetUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
id := r.URL.Path[len("/users/"):]
return GetUserRequest{ID: id}, nil
}
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
---
b.扩展功能
a.功能说明
扩展功能提供额外的增强特性,提升系统能力。扩展应该是可选的,不影响核心功能。支持插件化设计,便于功能扩展。扩展功能应该文档完善,便于使用。提供配置选项,灵活控制扩展行为。扩展应该经过充分测试,确保稳定性。
b.代码示例
---
package middleware
import "context"
func ApplyMiddleware(endpoints Endpoints, logger log.Logger) Endpoints {
endpoints.CreateUserEndpoint = LoggingMiddleware(logger)(endpoints.CreateUserEndpoint)
endpoints.CreateUserEndpoint = InstrumentingMiddleware(duration)(endpoints.CreateUserEndpoint)
endpoints.GetUserEndpoint = LoggingMiddleware(logger)(endpoints.GetUserEndpoint)
endpoints.GetUserEndpoint = InstrumentingMiddleware(duration)(endpoints.GetUserEndpoint)
return endpoints
}
func LoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
logger.Log("msg", "calling endpoint")
defer logger.Log("msg", "called endpoint")
return next(ctx, request)
}
}
}
func InstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
duration.Observe(time.Since(begin).Seconds())
}(time.Now())
return next(ctx, request)
}
}
}
---
03.配置文件
a.实践经验
a.功能说明
配置文件总结项目实施的经验和教训,提供实用的指导建议。包括架构设计、技术选型、性能优化等方面。配置文件应该基于实际项目经验,具有参考价值。提供具体的案例和代码示例。配置文件应该持续更新,反映最新的实践。帮助开发者避免常见陷阱,提高开发效率。
b.代码示例
---
package main
import ("context"; "time")
type Config struct {
HTTPAddr string
GRPCAddr string
ConsulAddr string
JaegerAddr string
LogLevel string
ShutdownTimeout time.Duration
}
func LoadConfig() (*Config, error) {
return &Config{
HTTPAddr: getEnv("HTTP_ADDR", ":8080"),
GRPCAddr: getEnv("GRPC_ADDR", ":8081"),
ConsulAddr: getEnv("CONSUL_ADDR", "localhost:8500"),
JaegerAddr: getEnv("JAEGER_ADDR", "localhost:6831"),
LogLevel: getEnv("LOG_LEVEL", "info"),
ShutdownTimeout: 30 * time.Second,
}, nil
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func GracefulShutdown(server *http.Server, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return server.Shutdown(ctx)
}
func Run() error {
config, err := LoadConfig()
if err != nil {
return err
}
logger := setupLogger(config.LogLevel)
tracer, closer, err := setupTracing(config.JaegerAddr)
if err != nil {
return err
}
defer closer.Close()
svc := setupService(logger)
endpoints := setupEndpoints(svc, logger, tracer)
server := setupHTTPServer(config.HTTPAddr, endpoints, logger)
errs := make(chan error, 1)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", config.HTTPAddr)
errs <- server.ListenAndServe()
}()
logger.Log("exit", <-errs)
return GracefulShutdown(server, config.ShutdownTimeout)
}
---
b.总结建议
a.功能说明
总结建议提供项目开发的关键要点和注意事项。强调重要的设计原则和最佳实践。提供问题解决的思路和方法。总结应该简洁明了,便于记忆和应用。包括性能优化、安全加固、运维部署等方面。帮助团队建立统一的开发规范。
b.代码示例
---
package best
import "context"
// 1. 使用接口定义服务
type UserService interface {
Create(ctx context.Context, user User) error
Get(ctx context.Context, id string) (User, error)
}
// 2. 使用中间件增强功能
func WithLogging(svc UserService, logger log.Logger) UserService {
return &loggingMiddleware{next: svc, logger: logger}
}
// 3. 使用Context传递请求信息
func (s *service) Create(ctx context.Context, user User) error {
requestID := ctx.Value("request_id")
s.logger.Log("request_id", requestID, "action", "create_user")
return s.repo.Save(ctx, user)
}
// 4. 统一错误处理
type AppError struct {
Code int
Message string
Cause error
}
func (e *AppError) Error() string {
return e.Message
}
// 5. 使用配置管理
type ServiceConfig struct {
Database DatabaseConfig
Redis RedisConfig
HTTP HTTPConfig
}
func LoadServiceConfig(path string) (*ServiceConfig, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var config ServiceConfig
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, err
}
return &config, nil
}
---
7.2 配置管理
01.配置加载
a.基本概念
a.功能说明
配置加载是实战应用的基础部分,提供项目的基本框架和结构。遵循go-kit的最佳实践,采用清晰的分层架构。配置加载应该易于理解和维护,便于团队协作。支持模块化设计,各模块职责清晰。提供完整的示例代码,便于学习和参考。配置加载应该考虑可扩展性,便于后续功能添加。
b.代码示例
---
package main
import ("context"; "fmt"; "net/http"; "os"; "os/signal")
func main() {
var (
httpAddr = ":8080"
logger = log.NewLogfmtLogger(os.Stderr)
)
var svc Service
svc = NewBasicService()
svc = LoggingMiddleware(logger)(svc)
endpoints := MakeEndpoints(svc)
endpoints = ApplyMiddleware(endpoints, logger)
handler := MakeHTTPHandler(endpoints, logger)
errs := make(chan error)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", httpAddr)
errs <- http.ListenAndServe(httpAddr, handler)
}()
logger.Log("exit", <-errs)
}
type Service interface {
CreateUser(ctx context.Context, username, email string) (User, error)
GetUser(ctx context.Context, id string) (User, error)
}
type basicService struct{}
func NewBasicService() Service {
return &basicService{}
}
func (s *basicService) CreateUser(ctx context.Context, username, email string) (User, error) {
return User{ID: generateID(), Username: username, Email: email}, nil
}
func (s *basicService) GetUser(ctx context.Context, id string) (User, error) {
return User{ID: id, Username: "user", Email: "[email protected]"}, nil
}
---
b.实现细节
a.功能说明
实现细节包括具体的代码实现和技术选型。使用标准库和成熟的第三方库。代码应该遵循Go语言规范,保持一致的风格。提供详细的注释,说明关键逻辑。实现应该考虑性能和资源使用。提供错误处理和日志记录。实现应该易于测试和调试。
b.代码示例
---
package service
import "context"
type Endpoints struct {
CreateUserEndpoint endpoint.Endpoint
GetUserEndpoint endpoint.Endpoint
}
func MakeEndpoints(s Service) Endpoints {
return Endpoints{
CreateUserEndpoint: MakeCreateUserEndpoint(s),
GetUserEndpoint: MakeGetUserEndpoint(s),
}
}
func MakeCreateUserEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := s.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err}, nil
}
return CreateUserResponse{User: user}, nil
}
}
type CreateUserRequest struct {
Username string `json:"username"`
Email string `json:"email"`
}
type CreateUserResponse struct {
User User `json:"user,omitempty"`
Err error `json:"error,omitempty"`
}
---
02.环境配置
a.核心功能
a.功能说明
环境配置提供项目的核心功能实现,是系统的主要业务逻辑。功能应该完整且健壮,处理各种边界情况。提供清晰的API接口,便于调用。环境配置应该高性能,满足业务需求。支持并发处理,充分利用系统资源。提供完善的错误处理和日志记录。
b.代码示例
---
package transport
import ("context"; "encoding/json"; "net/http")
func MakeHTTPHandler(endpoints Endpoints, logger log.Logger) http.Handler {
m := http.NewServeMux()
m.Handle("/users", httptransport.NewServer(
endpoints.CreateUserEndpoint,
decodeCreateUserRequest,
encodeResponse,
))
m.Handle("/users/", httptransport.NewServer(
endpoints.GetUserEndpoint,
decodeGetUserRequest,
encodeResponse,
))
return m
}
func decodeCreateUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
func decodeGetUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
id := r.URL.Path[len("/users/"):]
return GetUserRequest{ID: id}, nil
}
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
---
b.扩展功能
a.功能说明
扩展功能提供额外的增强特性,提升系统能力。扩展应该是可选的,不影响核心功能。支持插件化设计,便于功能扩展。扩展功能应该文档完善,便于使用。提供配置选项,灵活控制扩展行为。扩展应该经过充分测试,确保稳定性。
b.代码示例
---
package middleware
import "context"
func ApplyMiddleware(endpoints Endpoints, logger log.Logger) Endpoints {
endpoints.CreateUserEndpoint = LoggingMiddleware(logger)(endpoints.CreateUserEndpoint)
endpoints.CreateUserEndpoint = InstrumentingMiddleware(duration)(endpoints.CreateUserEndpoint)
endpoints.GetUserEndpoint = LoggingMiddleware(logger)(endpoints.GetUserEndpoint)
endpoints.GetUserEndpoint = InstrumentingMiddleware(duration)(endpoints.GetUserEndpoint)
return endpoints
}
func LoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
logger.Log("msg", "calling endpoint")
defer logger.Log("msg", "called endpoint")
return next(ctx, request)
}
}
}
func InstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
duration.Observe(time.Since(begin).Seconds())
}(time.Now())
return next(ctx, request)
}
}
}
---
03.动态配置
a.实践经验
a.功能说明
动态配置总结项目实施的经验和教训,提供实用的指导建议。包括架构设计、技术选型、性能优化等方面。动态配置应该基于实际项目经验,具有参考价值。提供具体的案例和代码示例。动态配置应该持续更新,反映最新的实践。帮助开发者避免常见陷阱,提高开发效率。
b.代码示例
---
package main
import ("context"; "time")
type Config struct {
HTTPAddr string
GRPCAddr string
ConsulAddr string
JaegerAddr string
LogLevel string
ShutdownTimeout time.Duration
}
func LoadConfig() (*Config, error) {
return &Config{
HTTPAddr: getEnv("HTTP_ADDR", ":8080"),
GRPCAddr: getEnv("GRPC_ADDR", ":8081"),
ConsulAddr: getEnv("CONSUL_ADDR", "localhost:8500"),
JaegerAddr: getEnv("JAEGER_ADDR", "localhost:6831"),
LogLevel: getEnv("LOG_LEVEL", "info"),
ShutdownTimeout: 30 * time.Second,
}, nil
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func GracefulShutdown(server *http.Server, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return server.Shutdown(ctx)
}
func Run() error {
config, err := LoadConfig()
if err != nil {
return err
}
logger := setupLogger(config.LogLevel)
tracer, closer, err := setupTracing(config.JaegerAddr)
if err != nil {
return err
}
defer closer.Close()
svc := setupService(logger)
endpoints := setupEndpoints(svc, logger, tracer)
server := setupHTTPServer(config.HTTPAddr, endpoints, logger)
errs := make(chan error, 1)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", config.HTTPAddr)
errs <- server.ListenAndServe()
}()
logger.Log("exit", <-errs)
return GracefulShutdown(server, config.ShutdownTimeout)
}
---
b.总结建议
a.功能说明
总结建议提供项目开发的关键要点和注意事项。强调重要的设计原则和最佳实践。提供问题解决的思路和方法。总结应该简洁明了,便于记忆和应用。包括性能优化、安全加固、运维部署等方面。帮助团队建立统一的开发规范。
b.代码示例
---
package best
import "context"
// 1. 使用接口定义服务
type UserService interface {
Create(ctx context.Context, user User) error
Get(ctx context.Context, id string) (User, error)
}
// 2. 使用中间件增强功能
func WithLogging(svc UserService, logger log.Logger) UserService {
return &loggingMiddleware{next: svc, logger: logger}
}
// 3. 使用Context传递请求信息
func (s *service) Create(ctx context.Context, user User) error {
requestID := ctx.Value("request_id")
s.logger.Log("request_id", requestID, "action", "create_user")
return s.repo.Save(ctx, user)
}
// 4. 统一错误处理
type AppError struct {
Code int
Message string
Cause error
}
func (e *AppError) Error() string {
return e.Message
}
// 5. 使用配置管理
type ServiceConfig struct {
Database DatabaseConfig
Redis RedisConfig
HTTP HTTPConfig
}
func LoadServiceConfig(path string) (*ServiceConfig, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var config ServiceConfig
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, err
}
return &config, nil
}
---
7.3 完整案例
01.用户服务
a.基本概念
a.功能说明
用户服务是实战应用的基础部分,提供项目的基本框架和结构。遵循go-kit的最佳实践,采用清晰的分层架构。用户服务应该易于理解和维护,便于团队协作。支持模块化设计,各模块职责清晰。提供完整的示例代码,便于学习和参考。用户服务应该考虑可扩展性,便于后续功能添加。
b.代码示例
---
package main
import ("context"; "fmt"; "net/http"; "os"; "os/signal")
func main() {
var (
httpAddr = ":8080"
logger = log.NewLogfmtLogger(os.Stderr)
)
var svc Service
svc = NewBasicService()
svc = LoggingMiddleware(logger)(svc)
endpoints := MakeEndpoints(svc)
endpoints = ApplyMiddleware(endpoints, logger)
handler := MakeHTTPHandler(endpoints, logger)
errs := make(chan error)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", httpAddr)
errs <- http.ListenAndServe(httpAddr, handler)
}()
logger.Log("exit", <-errs)
}
type Service interface {
CreateUser(ctx context.Context, username, email string) (User, error)
GetUser(ctx context.Context, id string) (User, error)
}
type basicService struct{}
func NewBasicService() Service {
return &basicService{}
}
func (s *basicService) CreateUser(ctx context.Context, username, email string) (User, error) {
return User{ID: generateID(), Username: username, Email: email}, nil
}
func (s *basicService) GetUser(ctx context.Context, id string) (User, error) {
return User{ID: id, Username: "user", Email: "[email protected]"}, nil
}
---
b.实现细节
a.功能说明
实现细节包括具体的代码实现和技术选型。使用标准库和成熟的第三方库。代码应该遵循Go语言规范,保持一致的风格。提供详细的注释,说明关键逻辑。实现应该考虑性能和资源使用。提供错误处理和日志记录。实现应该易于测试和调试。
b.代码示例
---
package service
import "context"
type Endpoints struct {
CreateUserEndpoint endpoint.Endpoint
GetUserEndpoint endpoint.Endpoint
}
func MakeEndpoints(s Service) Endpoints {
return Endpoints{
CreateUserEndpoint: MakeCreateUserEndpoint(s),
GetUserEndpoint: MakeGetUserEndpoint(s),
}
}
func MakeCreateUserEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := s.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err}, nil
}
return CreateUserResponse{User: user}, nil
}
}
type CreateUserRequest struct {
Username string `json:"username"`
Email string `json:"email"`
}
type CreateUserResponse struct {
User User `json:"user,omitempty"`
Err error `json:"error,omitempty"`
}
---
02.订单服务
a.核心功能
a.功能说明
订单服务提供项目的核心功能实现,是系统的主要业务逻辑。功能应该完整且健壮,处理各种边界情况。提供清晰的API接口,便于调用。订单服务应该高性能,满足业务需求。支持并发处理,充分利用系统资源。提供完善的错误处理和日志记录。
b.代码示例
---
package transport
import ("context"; "encoding/json"; "net/http")
func MakeHTTPHandler(endpoints Endpoints, logger log.Logger) http.Handler {
m := http.NewServeMux()
m.Handle("/users", httptransport.NewServer(
endpoints.CreateUserEndpoint,
decodeCreateUserRequest,
encodeResponse,
))
m.Handle("/users/", httptransport.NewServer(
endpoints.GetUserEndpoint,
decodeGetUserRequest,
encodeResponse,
))
return m
}
func decodeCreateUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
func decodeGetUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
id := r.URL.Path[len("/users/"):]
return GetUserRequest{ID: id}, nil
}
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
---
b.扩展功能
a.功能说明
扩展功能提供额外的增强特性,提升系统能力。扩展应该是可选的,不影响核心功能。支持插件化设计,便于功能扩展。扩展功能应该文档完善,便于使用。提供配置选项,灵活控制扩展行为。扩展应该经过充分测试,确保稳定性。
b.代码示例
---
package middleware
import "context"
func ApplyMiddleware(endpoints Endpoints, logger log.Logger) Endpoints {
endpoints.CreateUserEndpoint = LoggingMiddleware(logger)(endpoints.CreateUserEndpoint)
endpoints.CreateUserEndpoint = InstrumentingMiddleware(duration)(endpoints.CreateUserEndpoint)
endpoints.GetUserEndpoint = LoggingMiddleware(logger)(endpoints.GetUserEndpoint)
endpoints.GetUserEndpoint = InstrumentingMiddleware(duration)(endpoints.GetUserEndpoint)
return endpoints
}
func LoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
logger.Log("msg", "calling endpoint")
defer logger.Log("msg", "called endpoint")
return next(ctx, request)
}
}
}
func InstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
duration.Observe(time.Since(begin).Seconds())
}(time.Now())
return next(ctx, request)
}
}
}
---
03.服务集成
a.实践经验
a.功能说明
服务集成总结项目实施的经验和教训,提供实用的指导建议。包括架构设计、技术选型、性能优化等方面。服务集成应该基于实际项目经验,具有参考价值。提供具体的案例和代码示例。服务集成应该持续更新,反映最新的实践。帮助开发者避免常见陷阱,提高开发效率。
b.代码示例
---
package main
import ("context"; "time")
type Config struct {
HTTPAddr string
GRPCAddr string
ConsulAddr string
JaegerAddr string
LogLevel string
ShutdownTimeout time.Duration
}
func LoadConfig() (*Config, error) {
return &Config{
HTTPAddr: getEnv("HTTP_ADDR", ":8080"),
GRPCAddr: getEnv("GRPC_ADDR", ":8081"),
ConsulAddr: getEnv("CONSUL_ADDR", "localhost:8500"),
JaegerAddr: getEnv("JAEGER_ADDR", "localhost:6831"),
LogLevel: getEnv("LOG_LEVEL", "info"),
ShutdownTimeout: 30 * time.Second,
}, nil
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func GracefulShutdown(server *http.Server, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return server.Shutdown(ctx)
}
func Run() error {
config, err := LoadConfig()
if err != nil {
return err
}
logger := setupLogger(config.LogLevel)
tracer, closer, err := setupTracing(config.JaegerAddr)
if err != nil {
return err
}
defer closer.Close()
svc := setupService(logger)
endpoints := setupEndpoints(svc, logger, tracer)
server := setupHTTPServer(config.HTTPAddr, endpoints, logger)
errs := make(chan error, 1)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", config.HTTPAddr)
errs <- server.ListenAndServe()
}()
logger.Log("exit", <-errs)
return GracefulShutdown(server, config.ShutdownTimeout)
}
---
b.总结建议
a.功能说明
总结建议提供项目开发的关键要点和注意事项。强调重要的设计原则和最佳实践。提供问题解决的思路和方法。总结应该简洁明了,便于记忆和应用。包括性能优化、安全加固、运维部署等方面。帮助团队建立统一的开发规范。
b.代码示例
---
package best
import "context"
// 1. 使用接口定义服务
type UserService interface {
Create(ctx context.Context, user User) error
Get(ctx context.Context, id string) (User, error)
}
// 2. 使用中间件增强功能
func WithLogging(svc UserService, logger log.Logger) UserService {
return &loggingMiddleware{next: svc, logger: logger}
}
// 3. 使用Context传递请求信息
func (s *service) Create(ctx context.Context, user User) error {
requestID := ctx.Value("request_id")
s.logger.Log("request_id", requestID, "action", "create_user")
return s.repo.Save(ctx, user)
}
// 4. 统一错误处理
type AppError struct {
Code int
Message string
Cause error
}
func (e *AppError) Error() string {
return e.Message
}
// 5. 使用配置管理
type ServiceConfig struct {
Database DatabaseConfig
Redis RedisConfig
HTTP HTTPConfig
}
func LoadServiceConfig(path string) (*ServiceConfig, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var config ServiceConfig
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, err
}
return &config, nil
}
---
7.4 性能调优
01.性能分析
a.基本概念
a.功能说明
性能分析是实战应用的基础部分,提供项目的基本框架和结构。遵循go-kit的最佳实践,采用清晰的分层架构。性能分析应该易于理解和维护,便于团队协作。支持模块化设计,各模块职责清晰。提供完整的示例代码,便于学习和参考。性能分析应该考虑可扩展性,便于后续功能添加。
b.代码示例
---
package main
import ("context"; "fmt"; "net/http"; "os"; "os/signal")
func main() {
var (
httpAddr = ":8080"
logger = log.NewLogfmtLogger(os.Stderr)
)
var svc Service
svc = NewBasicService()
svc = LoggingMiddleware(logger)(svc)
endpoints := MakeEndpoints(svc)
endpoints = ApplyMiddleware(endpoints, logger)
handler := MakeHTTPHandler(endpoints, logger)
errs := make(chan error)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", httpAddr)
errs <- http.ListenAndServe(httpAddr, handler)
}()
logger.Log("exit", <-errs)
}
type Service interface {
CreateUser(ctx context.Context, username, email string) (User, error)
GetUser(ctx context.Context, id string) (User, error)
}
type basicService struct{}
func NewBasicService() Service {
return &basicService{}
}
func (s *basicService) CreateUser(ctx context.Context, username, email string) (User, error) {
return User{ID: generateID(), Username: username, Email: email}, nil
}
func (s *basicService) GetUser(ctx context.Context, id string) (User, error) {
return User{ID: id, Username: "user", Email: "[email protected]"}, nil
}
---
b.实现细节
a.功能说明
实现细节包括具体的代码实现和技术选型。使用标准库和成熟的第三方库。代码应该遵循Go语言规范,保持一致的风格。提供详细的注释,说明关键逻辑。实现应该考虑性能和资源使用。提供错误处理和日志记录。实现应该易于测试和调试。
b.代码示例
---
package service
import "context"
type Endpoints struct {
CreateUserEndpoint endpoint.Endpoint
GetUserEndpoint endpoint.Endpoint
}
func MakeEndpoints(s Service) Endpoints {
return Endpoints{
CreateUserEndpoint: MakeCreateUserEndpoint(s),
GetUserEndpoint: MakeGetUserEndpoint(s),
}
}
func MakeCreateUserEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := s.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err}, nil
}
return CreateUserResponse{User: user}, nil
}
}
type CreateUserRequest struct {
Username string `json:"username"`
Email string `json:"email"`
}
type CreateUserResponse struct {
User User `json:"user,omitempty"`
Err error `json:"error,omitempty"`
}
---
02.优化策略
a.核心功能
a.功能说明
优化策略提供项目的核心功能实现,是系统的主要业务逻辑。功能应该完整且健壮,处理各种边界情况。提供清晰的API接口,便于调用。优化策略应该高性能,满足业务需求。支持并发处理,充分利用系统资源。提供完善的错误处理和日志记录。
b.代码示例
---
package transport
import ("context"; "encoding/json"; "net/http")
func MakeHTTPHandler(endpoints Endpoints, logger log.Logger) http.Handler {
m := http.NewServeMux()
m.Handle("/users", httptransport.NewServer(
endpoints.CreateUserEndpoint,
decodeCreateUserRequest,
encodeResponse,
))
m.Handle("/users/", httptransport.NewServer(
endpoints.GetUserEndpoint,
decodeGetUserRequest,
encodeResponse,
))
return m
}
func decodeCreateUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
func decodeGetUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
id := r.URL.Path[len("/users/"):]
return GetUserRequest{ID: id}, nil
}
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
---
b.扩展功能
a.功能说明
扩展功能提供额外的增强特性,提升系统能力。扩展应该是可选的,不影响核心功能。支持插件化设计,便于功能扩展。扩展功能应该文档完善,便于使用。提供配置选项,灵活控制扩展行为。扩展应该经过充分测试,确保稳定性。
b.代码示例
---
package middleware
import "context"
func ApplyMiddleware(endpoints Endpoints, logger log.Logger) Endpoints {
endpoints.CreateUserEndpoint = LoggingMiddleware(logger)(endpoints.CreateUserEndpoint)
endpoints.CreateUserEndpoint = InstrumentingMiddleware(duration)(endpoints.CreateUserEndpoint)
endpoints.GetUserEndpoint = LoggingMiddleware(logger)(endpoints.GetUserEndpoint)
endpoints.GetUserEndpoint = InstrumentingMiddleware(duration)(endpoints.GetUserEndpoint)
return endpoints
}
func LoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
logger.Log("msg", "calling endpoint")
defer logger.Log("msg", "called endpoint")
return next(ctx, request)
}
}
}
func InstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
duration.Observe(time.Since(begin).Seconds())
}(time.Now())
return next(ctx, request)
}
}
}
---
03.压力测试
a.实践经验
a.功能说明
压力测试总结项目实施的经验和教训,提供实用的指导建议。包括架构设计、技术选型、性能优化等方面。压力测试应该基于实际项目经验,具有参考价值。提供具体的案例和代码示例。压力测试应该持续更新,反映最新的实践。帮助开发者避免常见陷阱,提高开发效率。
b.代码示例
---
package main
import ("context"; "time")
type Config struct {
HTTPAddr string
GRPCAddr string
ConsulAddr string
JaegerAddr string
LogLevel string
ShutdownTimeout time.Duration
}
func LoadConfig() (*Config, error) {
return &Config{
HTTPAddr: getEnv("HTTP_ADDR", ":8080"),
GRPCAddr: getEnv("GRPC_ADDR", ":8081"),
ConsulAddr: getEnv("CONSUL_ADDR", "localhost:8500"),
JaegerAddr: getEnv("JAEGER_ADDR", "localhost:6831"),
LogLevel: getEnv("LOG_LEVEL", "info"),
ShutdownTimeout: 30 * time.Second,
}, nil
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func GracefulShutdown(server *http.Server, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return server.Shutdown(ctx)
}
func Run() error {
config, err := LoadConfig()
if err != nil {
return err
}
logger := setupLogger(config.LogLevel)
tracer, closer, err := setupTracing(config.JaegerAddr)
if err != nil {
return err
}
defer closer.Close()
svc := setupService(logger)
endpoints := setupEndpoints(svc, logger, tracer)
server := setupHTTPServer(config.HTTPAddr, endpoints, logger)
errs := make(chan error, 1)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", config.HTTPAddr)
errs <- server.ListenAndServe()
}()
logger.Log("exit", <-errs)
return GracefulShutdown(server, config.ShutdownTimeout)
}
---
b.总结建议
a.功能说明
总结建议提供项目开发的关键要点和注意事项。强调重要的设计原则和最佳实践。提供问题解决的思路和方法。总结应该简洁明了,便于记忆和应用。包括性能优化、安全加固、运维部署等方面。帮助团队建立统一的开发规范。
b.代码示例
---
package best
import "context"
// 1. 使用接口定义服务
type UserService interface {
Create(ctx context.Context, user User) error
Get(ctx context.Context, id string) (User, error)
}
// 2. 使用中间件增强功能
func WithLogging(svc UserService, logger log.Logger) UserService {
return &loggingMiddleware{next: svc, logger: logger}
}
// 3. 使用Context传递请求信息
func (s *service) Create(ctx context.Context, user User) error {
requestID := ctx.Value("request_id")
s.logger.Log("request_id", requestID, "action", "create_user")
return s.repo.Save(ctx, user)
}
// 4. 统一错误处理
type AppError struct {
Code int
Message string
Cause error
}
func (e *AppError) Error() string {
return e.Message
}
// 5. 使用配置管理
type ServiceConfig struct {
Database DatabaseConfig
Redis RedisConfig
HTTP HTTPConfig
}
func LoadServiceConfig(path string) (*ServiceConfig, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var config ServiceConfig
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, err
}
return &config, nil
}
---
7.5 监控运维
01.监控系统
a.基本概念
a.功能说明
监控系统是实战应用的基础部分,提供项目的基本框架和结构。遵循go-kit的最佳实践,采用清晰的分层架构。监控系统应该易于理解和维护,便于团队协作。支持模块化设计,各模块职责清晰。提供完整的示例代码,便于学习和参考。监控系统应该考虑可扩展性,便于后续功能添加。
b.代码示例
---
package main
import ("context"; "fmt"; "net/http"; "os"; "os/signal")
func main() {
var (
httpAddr = ":8080"
logger = log.NewLogfmtLogger(os.Stderr)
)
var svc Service
svc = NewBasicService()
svc = LoggingMiddleware(logger)(svc)
endpoints := MakeEndpoints(svc)
endpoints = ApplyMiddleware(endpoints, logger)
handler := MakeHTTPHandler(endpoints, logger)
errs := make(chan error)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", httpAddr)
errs <- http.ListenAndServe(httpAddr, handler)
}()
logger.Log("exit", <-errs)
}
type Service interface {
CreateUser(ctx context.Context, username, email string) (User, error)
GetUser(ctx context.Context, id string) (User, error)
}
type basicService struct{}
func NewBasicService() Service {
return &basicService{}
}
func (s *basicService) CreateUser(ctx context.Context, username, email string) (User, error) {
return User{ID: generateID(), Username: username, Email: email}, nil
}
func (s *basicService) GetUser(ctx context.Context, id string) (User, error) {
return User{ID: id, Username: "user", Email: "[email protected]"}, nil
}
---
b.实现细节
a.功能说明
实现细节包括具体的代码实现和技术选型。使用标准库和成熟的第三方库。代码应该遵循Go语言规范,保持一致的风格。提供详细的注释,说明关键逻辑。实现应该考虑性能和资源使用。提供错误处理和日志记录。实现应该易于测试和调试。
b.代码示例
---
package service
import "context"
type Endpoints struct {
CreateUserEndpoint endpoint.Endpoint
GetUserEndpoint endpoint.Endpoint
}
func MakeEndpoints(s Service) Endpoints {
return Endpoints{
CreateUserEndpoint: MakeCreateUserEndpoint(s),
GetUserEndpoint: MakeGetUserEndpoint(s),
}
}
func MakeCreateUserEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := s.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err}, nil
}
return CreateUserResponse{User: user}, nil
}
}
type CreateUserRequest struct {
Username string `json:"username"`
Email string `json:"email"`
}
type CreateUserResponse struct {
User User `json:"user,omitempty"`
Err error `json:"error,omitempty"`
}
---
02.日志收集
a.核心功能
a.功能说明
日志收集提供项目的核心功能实现,是系统的主要业务逻辑。功能应该完整且健壮,处理各种边界情况。提供清晰的API接口,便于调用。日志收集应该高性能,满足业务需求。支持并发处理,充分利用系统资源。提供完善的错误处理和日志记录。
b.代码示例
---
package transport
import ("context"; "encoding/json"; "net/http")
func MakeHTTPHandler(endpoints Endpoints, logger log.Logger) http.Handler {
m := http.NewServeMux()
m.Handle("/users", httptransport.NewServer(
endpoints.CreateUserEndpoint,
decodeCreateUserRequest,
encodeResponse,
))
m.Handle("/users/", httptransport.NewServer(
endpoints.GetUserEndpoint,
decodeGetUserRequest,
encodeResponse,
))
return m
}
func decodeCreateUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
func decodeGetUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
id := r.URL.Path[len("/users/"):]
return GetUserRequest{ID: id}, nil
}
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
---
b.扩展功能
a.功能说明
扩展功能提供额外的增强特性,提升系统能力。扩展应该是可选的,不影响核心功能。支持插件化设计,便于功能扩展。扩展功能应该文档完善,便于使用。提供配置选项,灵活控制扩展行为。扩展应该经过充分测试,确保稳定性。
b.代码示例
---
package middleware
import "context"
func ApplyMiddleware(endpoints Endpoints, logger log.Logger) Endpoints {
endpoints.CreateUserEndpoint = LoggingMiddleware(logger)(endpoints.CreateUserEndpoint)
endpoints.CreateUserEndpoint = InstrumentingMiddleware(duration)(endpoints.CreateUserEndpoint)
endpoints.GetUserEndpoint = LoggingMiddleware(logger)(endpoints.GetUserEndpoint)
endpoints.GetUserEndpoint = InstrumentingMiddleware(duration)(endpoints.GetUserEndpoint)
return endpoints
}
func LoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
logger.Log("msg", "calling endpoint")
defer logger.Log("msg", "called endpoint")
return next(ctx, request)
}
}
}
func InstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
duration.Observe(time.Since(begin).Seconds())
}(time.Now())
return next(ctx, request)
}
}
}
---
03.告警机制
a.实践经验
a.功能说明
告警机制总结项目实施的经验和教训,提供实用的指导建议。包括架构设计、技术选型、性能优化等方面。告警机制应该基于实际项目经验,具有参考价值。提供具体的案例和代码示例。告警机制应该持续更新,反映最新的实践。帮助开发者避免常见陷阱,提高开发效率。
b.代码示例
---
package main
import ("context"; "time")
type Config struct {
HTTPAddr string
GRPCAddr string
ConsulAddr string
JaegerAddr string
LogLevel string
ShutdownTimeout time.Duration
}
func LoadConfig() (*Config, error) {
return &Config{
HTTPAddr: getEnv("HTTP_ADDR", ":8080"),
GRPCAddr: getEnv("GRPC_ADDR", ":8081"),
ConsulAddr: getEnv("CONSUL_ADDR", "localhost:8500"),
JaegerAddr: getEnv("JAEGER_ADDR", "localhost:6831"),
LogLevel: getEnv("LOG_LEVEL", "info"),
ShutdownTimeout: 30 * time.Second,
}, nil
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func GracefulShutdown(server *http.Server, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return server.Shutdown(ctx)
}
func Run() error {
config, err := LoadConfig()
if err != nil {
return err
}
logger := setupLogger(config.LogLevel)
tracer, closer, err := setupTracing(config.JaegerAddr)
if err != nil {
return err
}
defer closer.Close()
svc := setupService(logger)
endpoints := setupEndpoints(svc, logger, tracer)
server := setupHTTPServer(config.HTTPAddr, endpoints, logger)
errs := make(chan error, 1)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", config.HTTPAddr)
errs <- server.ListenAndServe()
}()
logger.Log("exit", <-errs)
return GracefulShutdown(server, config.ShutdownTimeout)
}
---
b.总结建议
a.功能说明
总结建议提供项目开发的关键要点和注意事项。强调重要的设计原则和最佳实践。提供问题解决的思路和方法。总结应该简洁明了,便于记忆和应用。包括性能优化、安全加固、运维部署等方面。帮助团队建立统一的开发规范。
b.代码示例
---
package best
import "context"
// 1. 使用接口定义服务
type UserService interface {
Create(ctx context.Context, user User) error
Get(ctx context.Context, id string) (User, error)
}
// 2. 使用中间件增强功能
func WithLogging(svc UserService, logger log.Logger) UserService {
return &loggingMiddleware{next: svc, logger: logger}
}
// 3. 使用Context传递请求信息
func (s *service) Create(ctx context.Context, user User) error {
requestID := ctx.Value("request_id")
s.logger.Log("request_id", requestID, "action", "create_user")
return s.repo.Save(ctx, user)
}
// 4. 统一错误处理
type AppError struct {
Code int
Message string
Cause error
}
func (e *AppError) Error() string {
return e.Message
}
// 5. 使用配置管理
type ServiceConfig struct {
Database DatabaseConfig
Redis RedisConfig
HTTP HTTPConfig
}
func LoadServiceConfig(path string) (*ServiceConfig, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var config ServiceConfig
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, err
}
return &config, nil
}
---
7.6 常见问题
01.问题排查
a.基本概念
a.功能说明
问题排查是实战应用的基础部分,提供项目的基本框架和结构。遵循go-kit的最佳实践,采用清晰的分层架构。问题排查应该易于理解和维护,便于团队协作。支持模块化设计,各模块职责清晰。提供完整的示例代码,便于学习和参考。问题排查应该考虑可扩展性,便于后续功能添加。
b.代码示例
---
package main
import ("context"; "fmt"; "net/http"; "os"; "os/signal")
func main() {
var (
httpAddr = ":8080"
logger = log.NewLogfmtLogger(os.Stderr)
)
var svc Service
svc = NewBasicService()
svc = LoggingMiddleware(logger)(svc)
endpoints := MakeEndpoints(svc)
endpoints = ApplyMiddleware(endpoints, logger)
handler := MakeHTTPHandler(endpoints, logger)
errs := make(chan error)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", httpAddr)
errs <- http.ListenAndServe(httpAddr, handler)
}()
logger.Log("exit", <-errs)
}
type Service interface {
CreateUser(ctx context.Context, username, email string) (User, error)
GetUser(ctx context.Context, id string) (User, error)
}
type basicService struct{}
func NewBasicService() Service {
return &basicService{}
}
func (s *basicService) CreateUser(ctx context.Context, username, email string) (User, error) {
return User{ID: generateID(), Username: username, Email: email}, nil
}
func (s *basicService) GetUser(ctx context.Context, id string) (User, error) {
return User{ID: id, Username: "user", Email: "[email protected]"}, nil
}
---
b.实现细节
a.功能说明
实现细节包括具体的代码实现和技术选型。使用标准库和成熟的第三方库。代码应该遵循Go语言规范,保持一致的风格。提供详细的注释,说明关键逻辑。实现应该考虑性能和资源使用。提供错误处理和日志记录。实现应该易于测试和调试。
b.代码示例
---
package service
import "context"
type Endpoints struct {
CreateUserEndpoint endpoint.Endpoint
GetUserEndpoint endpoint.Endpoint
}
func MakeEndpoints(s Service) Endpoints {
return Endpoints{
CreateUserEndpoint: MakeCreateUserEndpoint(s),
GetUserEndpoint: MakeGetUserEndpoint(s),
}
}
func MakeCreateUserEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := s.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err}, nil
}
return CreateUserResponse{User: user}, nil
}
}
type CreateUserRequest struct {
Username string `json:"username"`
Email string `json:"email"`
}
type CreateUserResponse struct {
User User `json:"user,omitempty"`
Err error `json:"error,omitempty"`
}
---
02.错误处理
a.核心功能
a.功能说明
错误处理提供项目的核心功能实现,是系统的主要业务逻辑。功能应该完整且健壮,处理各种边界情况。提供清晰的API接口,便于调用。错误处理应该高性能,满足业务需求。支持并发处理,充分利用系统资源。提供完善的错误处理和日志记录。
b.代码示例
---
package transport
import ("context"; "encoding/json"; "net/http")
func MakeHTTPHandler(endpoints Endpoints, logger log.Logger) http.Handler {
m := http.NewServeMux()
m.Handle("/users", httptransport.NewServer(
endpoints.CreateUserEndpoint,
decodeCreateUserRequest,
encodeResponse,
))
m.Handle("/users/", httptransport.NewServer(
endpoints.GetUserEndpoint,
decodeGetUserRequest,
encodeResponse,
))
return m
}
func decodeCreateUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
func decodeGetUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
id := r.URL.Path[len("/users/"):]
return GetUserRequest{ID: id}, nil
}
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
---
b.扩展功能
a.功能说明
扩展功能提供额外的增强特性,提升系统能力。扩展应该是可选的,不影响核心功能。支持插件化设计,便于功能扩展。扩展功能应该文档完善,便于使用。提供配置选项,灵活控制扩展行为。扩展应该经过充分测试,确保稳定性。
b.代码示例
---
package middleware
import "context"
func ApplyMiddleware(endpoints Endpoints, logger log.Logger) Endpoints {
endpoints.CreateUserEndpoint = LoggingMiddleware(logger)(endpoints.CreateUserEndpoint)
endpoints.CreateUserEndpoint = InstrumentingMiddleware(duration)(endpoints.CreateUserEndpoint)
endpoints.GetUserEndpoint = LoggingMiddleware(logger)(endpoints.GetUserEndpoint)
endpoints.GetUserEndpoint = InstrumentingMiddleware(duration)(endpoints.GetUserEndpoint)
return endpoints
}
func LoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
logger.Log("msg", "calling endpoint")
defer logger.Log("msg", "called endpoint")
return next(ctx, request)
}
}
}
func InstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
duration.Observe(time.Since(begin).Seconds())
}(time.Now())
return next(ctx, request)
}
}
}
---
03.调试技巧
a.实践经验
a.功能说明
调试技巧总结项目实施的经验和教训,提供实用的指导建议。包括架构设计、技术选型、性能优化等方面。调试技巧应该基于实际项目经验,具有参考价值。提供具体的案例和代码示例。调试技巧应该持续更新,反映最新的实践。帮助开发者避免常见陷阱,提高开发效率。
b.代码示例
---
package main
import ("context"; "time")
type Config struct {
HTTPAddr string
GRPCAddr string
ConsulAddr string
JaegerAddr string
LogLevel string
ShutdownTimeout time.Duration
}
func LoadConfig() (*Config, error) {
return &Config{
HTTPAddr: getEnv("HTTP_ADDR", ":8080"),
GRPCAddr: getEnv("GRPC_ADDR", ":8081"),
ConsulAddr: getEnv("CONSUL_ADDR", "localhost:8500"),
JaegerAddr: getEnv("JAEGER_ADDR", "localhost:6831"),
LogLevel: getEnv("LOG_LEVEL", "info"),
ShutdownTimeout: 30 * time.Second,
}, nil
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func GracefulShutdown(server *http.Server, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return server.Shutdown(ctx)
}
func Run() error {
config, err := LoadConfig()
if err != nil {
return err
}
logger := setupLogger(config.LogLevel)
tracer, closer, err := setupTracing(config.JaegerAddr)
if err != nil {
return err
}
defer closer.Close()
svc := setupService(logger)
endpoints := setupEndpoints(svc, logger, tracer)
server := setupHTTPServer(config.HTTPAddr, endpoints, logger)
errs := make(chan error, 1)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", config.HTTPAddr)
errs <- server.ListenAndServe()
}()
logger.Log("exit", <-errs)
return GracefulShutdown(server, config.ShutdownTimeout)
}
---
b.总结建议
a.功能说明
总结建议提供项目开发的关键要点和注意事项。强调重要的设计原则和最佳实践。提供问题解决的思路和方法。总结应该简洁明了,便于记忆和应用。包括性能优化、安全加固、运维部署等方面。帮助团队建立统一的开发规范。
b.代码示例
---
package best
import "context"
// 1. 使用接口定义服务
type UserService interface {
Create(ctx context.Context, user User) error
Get(ctx context.Context, id string) (User, error)
}
// 2. 使用中间件增强功能
func WithLogging(svc UserService, logger log.Logger) UserService {
return &loggingMiddleware{next: svc, logger: logger}
}
// 3. 使用Context传递请求信息
func (s *service) Create(ctx context.Context, user User) error {
requestID := ctx.Value("request_id")
s.logger.Log("request_id", requestID, "action", "create_user")
return s.repo.Save(ctx, user)
}
// 4. 统一错误处理
type AppError struct {
Code int
Message string
Cause error
}
func (e *AppError) Error() string {
return e.Message
}
// 5. 使用配置管理
type ServiceConfig struct {
Database DatabaseConfig
Redis RedisConfig
HTTP HTTPConfig
}
func LoadServiceConfig(path string) (*ServiceConfig, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var config ServiceConfig
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, err
}
return &config, nil
}
---
7.7 最佳实践
01.设计原则
a.基本概念
a.功能说明
设计原则是实战应用的基础部分,提供项目的基本框架和结构。遵循go-kit的最佳实践,采用清晰的分层架构。设计原则应该易于理解和维护,便于团队协作。支持模块化设计,各模块职责清晰。提供完整的示例代码,便于学习和参考。设计原则应该考虑可扩展性,便于后续功能添加。
b.代码示例
---
package main
import ("context"; "fmt"; "net/http"; "os"; "os/signal")
func main() {
var (
httpAddr = ":8080"
logger = log.NewLogfmtLogger(os.Stderr)
)
var svc Service
svc = NewBasicService()
svc = LoggingMiddleware(logger)(svc)
endpoints := MakeEndpoints(svc)
endpoints = ApplyMiddleware(endpoints, logger)
handler := MakeHTTPHandler(endpoints, logger)
errs := make(chan error)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", httpAddr)
errs <- http.ListenAndServe(httpAddr, handler)
}()
logger.Log("exit", <-errs)
}
type Service interface {
CreateUser(ctx context.Context, username, email string) (User, error)
GetUser(ctx context.Context, id string) (User, error)
}
type basicService struct{}
func NewBasicService() Service {
return &basicService{}
}
func (s *basicService) CreateUser(ctx context.Context, username, email string) (User, error) {
return User{ID: generateID(), Username: username, Email: email}, nil
}
func (s *basicService) GetUser(ctx context.Context, id string) (User, error) {
return User{ID: id, Username: "user", Email: "[email protected]"}, nil
}
---
b.实现细节
a.功能说明
实现细节包括具体的代码实现和技术选型。使用标准库和成熟的第三方库。代码应该遵循Go语言规范,保持一致的风格。提供详细的注释,说明关键逻辑。实现应该考虑性能和资源使用。提供错误处理和日志记录。实现应该易于测试和调试。
b.代码示例
---
package service
import "context"
type Endpoints struct {
CreateUserEndpoint endpoint.Endpoint
GetUserEndpoint endpoint.Endpoint
}
func MakeEndpoints(s Service) Endpoints {
return Endpoints{
CreateUserEndpoint: MakeCreateUserEndpoint(s),
GetUserEndpoint: MakeGetUserEndpoint(s),
}
}
func MakeCreateUserEndpoint(s Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(CreateUserRequest)
user, err := s.CreateUser(ctx, req.Username, req.Email)
if err != nil {
return CreateUserResponse{Err: err}, nil
}
return CreateUserResponse{User: user}, nil
}
}
type CreateUserRequest struct {
Username string `json:"username"`
Email string `json:"email"`
}
type CreateUserResponse struct {
User User `json:"user,omitempty"`
Err error `json:"error,omitempty"`
}
---
02.代码规范
a.核心功能
a.功能说明
代码规范提供项目的核心功能实现,是系统的主要业务逻辑。功能应该完整且健壮,处理各种边界情况。提供清晰的API接口,便于调用。代码规范应该高性能,满足业务需求。支持并发处理,充分利用系统资源。提供完善的错误处理和日志记录。
b.代码示例
---
package transport
import ("context"; "encoding/json"; "net/http")
func MakeHTTPHandler(endpoints Endpoints, logger log.Logger) http.Handler {
m := http.NewServeMux()
m.Handle("/users", httptransport.NewServer(
endpoints.CreateUserEndpoint,
decodeCreateUserRequest,
encodeResponse,
))
m.Handle("/users/", httptransport.NewServer(
endpoints.GetUserEndpoint,
decodeGetUserRequest,
encodeResponse,
))
return m
}
func decodeCreateUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
var req CreateUserRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, err
}
return req, nil
}
func decodeGetUserRequest(_ context.Context, r *http.Request) (interface{}, error) {
id := r.URL.Path[len("/users/"):]
return GetUserRequest{ID: id}, nil
}
func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
return json.NewEncoder(w).Encode(response)
}
---
b.扩展功能
a.功能说明
扩展功能提供额外的增强特性,提升系统能力。扩展应该是可选的,不影响核心功能。支持插件化设计,便于功能扩展。扩展功能应该文档完善,便于使用。提供配置选项,灵活控制扩展行为。扩展应该经过充分测试,确保稳定性。
b.代码示例
---
package middleware
import "context"
func ApplyMiddleware(endpoints Endpoints, logger log.Logger) Endpoints {
endpoints.CreateUserEndpoint = LoggingMiddleware(logger)(endpoints.CreateUserEndpoint)
endpoints.CreateUserEndpoint = InstrumentingMiddleware(duration)(endpoints.CreateUserEndpoint)
endpoints.GetUserEndpoint = LoggingMiddleware(logger)(endpoints.GetUserEndpoint)
endpoints.GetUserEndpoint = InstrumentingMiddleware(duration)(endpoints.GetUserEndpoint)
return endpoints
}
func LoggingMiddleware(logger log.Logger) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
logger.Log("msg", "calling endpoint")
defer logger.Log("msg", "called endpoint")
return next(ctx, request)
}
}
}
func InstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
defer func(begin time.Time) {
duration.Observe(time.Since(begin).Seconds())
}(time.Now())
return next(ctx, request)
}
}
}
---
03.部署策略
a.实践经验
a.功能说明
部署策略总结项目实施的经验和教训,提供实用的指导建议。包括架构设计、技术选型、性能优化等方面。部署策略应该基于实际项目经验,具有参考价值。提供具体的案例和代码示例。部署策略应该持续更新,反映最新的实践。帮助开发者避免常见陷阱,提高开发效率。
b.代码示例
---
package main
import ("context"; "time")
type Config struct {
HTTPAddr string
GRPCAddr string
ConsulAddr string
JaegerAddr string
LogLevel string
ShutdownTimeout time.Duration
}
func LoadConfig() (*Config, error) {
return &Config{
HTTPAddr: getEnv("HTTP_ADDR", ":8080"),
GRPCAddr: getEnv("GRPC_ADDR", ":8081"),
ConsulAddr: getEnv("CONSUL_ADDR", "localhost:8500"),
JaegerAddr: getEnv("JAEGER_ADDR", "localhost:6831"),
LogLevel: getEnv("LOG_LEVEL", "info"),
ShutdownTimeout: 30 * time.Second,
}, nil
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func GracefulShutdown(server *http.Server, timeout time.Duration) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return server.Shutdown(ctx)
}
func Run() error {
config, err := LoadConfig()
if err != nil {
return err
}
logger := setupLogger(config.LogLevel)
tracer, closer, err := setupTracing(config.JaegerAddr)
if err != nil {
return err
}
defer closer.Close()
svc := setupService(logger)
endpoints := setupEndpoints(svc, logger, tracer)
server := setupHTTPServer(config.HTTPAddr, endpoints, logger)
errs := make(chan error, 1)
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
errs <- fmt.Errorf("%s", <-c)
}()
go func() {
logger.Log("transport", "HTTP", "addr", config.HTTPAddr)
errs <- server.ListenAndServe()
}()
logger.Log("exit", <-errs)
return GracefulShutdown(server, config.ShutdownTimeout)
}
---
b.总结建议
a.功能说明
总结建议提供项目开发的关键要点和注意事项。强调重要的设计原则和最佳实践。提供问题解决的思路和方法。总结应该简洁明了,便于记忆和应用。包括性能优化、安全加固、运维部署等方面。帮助团队建立统一的开发规范。
b.代码示例
---
package best
import "context"
// 1. 使用接口定义服务
type UserService interface {
Create(ctx context.Context, user User) error
Get(ctx context.Context, id string) (User, error)
}
// 2. 使用中间件增强功能
func WithLogging(svc UserService, logger log.Logger) UserService {
return &loggingMiddleware{next: svc, logger: logger}
}
// 3. 使用Context传递请求信息
func (s *service) Create(ctx context.Context, user User) error {
requestID := ctx.Value("request_id")
s.logger.Log("request_id", requestID, "action", "create_user")
return s.repo.Save(ctx, user)
}
// 4. 统一错误处理
type AppError struct {
Code int
Message string
Cause error
}
func (e *AppError) Error() string {
return e.Message
}
// 5. 使用配置管理
type ServiceConfig struct {
Database DatabaseConfig
Redis RedisConfig
HTTP HTTPConfig
}
func LoadServiceConfig(path string) (*ServiceConfig, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var config ServiceConfig
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, err
}
return &config, nil
}
---