Go-kit
Go kit教程04——中间件和日志
本文主要介绍了Go kit 中的中间件,并以日志中间件为例演示了如何设计和实现中间件。
上一篇中,我们对go kit搭建的项目进行了目录结构拆分
中间件
在 go kit 中,它对中间件的定义是一个接收Endpoint并返回Endpoint的函数,具体定义如下。
type Middleware func(Endpoint) Endpoint
在中间件接收Endpoint参数和返回Endpoint之间,你可以做任何事。
例如,下面的示例演示了如何实现一个基础的日志中间件。
import (
"context"
"github.com/go-kit/kit/endpoint"
"github.com/go-kit/log"
)
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)
}
}
}
transport层日志
这里想要记录transport层的日志信息,将先前的NewHTTPServer按如下方式进行改造即可。
func NewHTTPServer(svc AddService, logger log.Logger) http.Handler {
// sum
sum := makeSumEndpoint(svc)
// 使用loggingMiddleware为sum端点加上日志
sum = loggingMiddleware(log.With(logger, "method", "sum"))(sum)
sumHandler := httptransport.NewServer(
sum,
decodeSumRequest,
encodeResponse,
)
// concat
concat := makeConcatEndpoint(svc)
// 使用loggingMiddleware为concat端点加上日志
concat = loggingMiddleware(log.With(logger, "method", "concat"))(concat)
concatHandler := httptransport.NewServer(
concat,
decodeConcatRequest,
encodeResponse,
)
r := gin.Default()
r.POST("/sum", gin.WrapH(sumHandler))
r.POST("/concat", gin.WrapH(concatHandler))
return r
}
应用层日志
第一种 引用全局的logger
第二种 结构体注入 (缺点:后期更改日志工具不方便)
官方方法
如果要在应用程序层面添加日志,例如需要记录下详细的请求参数,那么就需要为我们的服务来定义中间件。
由于我们的 AddService 服务定义为接口类型,所以我们只需要定义一个新类型(把原先的实现和一个额外的logger包装起来)并实现这个接口即可。
先定义类型。
type logMiddleware struct {
logger log.Logger
next AddService
}
再实现接口。
func (mw logMiddleware) Sum(ctx context.Context, a, b int) (res int, err error) {
defer func(begin time.Time) {
mw.logger.Log(
"method", "sum",
"a", a,
"b", b,
"output", res,
"err", err,
"took", time.Since(begin),
)
}(time.Now())
res, err = mw.next.Sum(ctx, a, b)
return
}
func (mw logMiddleware) Concat(ctx context.Context, a, b string) (res string, err error) {
defer func(begin time.Time) {
mw.logger.Log(
"method", "sum",
"a", a,
"b", b,
"output", res,
"err", err,
"took", time.Since(begin),
)
}(time.Now())
res, err = mw.next.Concat(ctx, a, b)
return
}
// NewLogMiddleware 创建一个带日志的add service
func NewLogMiddleware(logger log.Logger, svc AddService) AddService {
return &logMiddleware{
logger: logger,
next: svc,
}
}
集成zap日志库
上述示例默认使用的是github.com/go-kit/log,你也可以使用其他的日志库,例如下面示例中使用社区常用的zap日志库。
ratelimit限流中间件
import "golang.org/x/time/rate"
var (
ErrRateLimit = errors.New("request rate limit")
)
// rateMiddleware 限流中间件
func rateMiddleware(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, ErrRateLimit
}
return next(ctx, request)
}
}
}
使用限流中间件。
import "golang.org/x/time/rate"
sum = rateMiddleware(rate.NewLimiter(1, 1))(sum)
Go-Zero
连接mysql
mysql -uroot -p -hlocalhost
使用 goctl model mysql … 自动生成model 可选择在最后加上 -c 来生成带缓存的代码
加不加缓存对于逻辑层是无感知的,但是需要添加上配置以连接缓存
日志 logx
在开发阶段需要查看一些信息的,可以使用debug级别或者Info级别日志
对于私密的信息不允许落盘和网络传输的使用debug级别日志
而对于需要探究哪出错的日志,使用Error级别
使用docker启动mysql、redis、etcd、grpcui
mysql
docker run --name mysql -e MYSQL_ROOT_PASSWORD=123 -d mysql:latest
redis
docker pull redis
docker run --name reids -e redis_password=123 -d redis:latest
etcd
docker run -it --name Etcd bitnami/etcd
grpcui
go install github.com/fullstorydev/grpcui/cmd/grpcui@latest