学习Influxdb

最近要实现接口监控, 准备用主流的时序数据库influxdb.

基本概念

influxdb的库也 database, 概念和mysql一样
influxdb的表叫 MEASUREMENTS, 意义更贴切, 测量的复数形式.
influxdb的一行数据叫 point, 就像做物理实验的打点, 每个点有其值和属性
influxdb的字段分类为 tag 和 field, field就是值, tag是其属性. 拿接口来说, 字段有 service_name, instance_id, method, handler_name, method, request_url, response_code, content_length, response_size, duration. 显然, 前面7个字段是tag, 特点是一般不是数值变量, 可枚举的, 所以influxdb对tag加了索引. 后面3个是field, 是数值变量, 是范围变化的, 不需要加索引.

插入数据

对于插入数据, influxdb同时提供了单条和批量插入的API. 开始不知道有批量方式, 来一条插一条, influxdb CPU巨高. 后面在官网文档找到了办法, 改用批量插入, 大大降低了CPU占用, 官方推荐是5k~1w条数据一批.

https://docs.influxdata.com/influxdb/v1.7/concepts/glossary/#batch

InfluxData recommends batch sizes of 5,000-10,000 points,
although different use cases may be better served by significantly smaller or larger batches.

influxdb同时提供了HTTP接口和UDP接口. UDP的好处在于减少了HTTP头部的开销, 性能更好

常用命令

  1. # 创建数据库
    CREATE DATABASE "db_name"
    # 显示所有数据库
    SHOW DATABASES
    # 删除数据库
    DROP DATABASE "db_name"
    # 使用数据库
    USE mydb
  2. # 显示该数据库中的表
    SHOW MEASUREMENTS
    # 创建表, 插入数据时会自动创建
    # 删除表
    DROP MEASUREMENT "measurementName"
  3. 查看数据保留策略 retention polices

    SHOW RETENTION POLICIES ON "testDB"
  4. 创建新的Retention Policies并设置为默认值

    # DURATION 保留多少天
    # REPLICATION 副本数
    CREATE RETENTION POLICY "rp_name" ON "db_name" DURATION 30d REPLICATION 1 DEFAULT

连续查询

# 创建一个连续查询, 每10秒计算一个接口响应耗时平均值到新表
CREATE CONTINUOUS QUERY cq_http_handler_stats_duration ON statsdb BEGIN SELECT mean("duration") INTO http_handler_stats_average_duraion_10s FROM http_handler_stats GROUP BY time(10s) END
# 如果成功创建, 那么过了第一个周期后, SHOW MEASUREMENTS 能看到多了一张表
# 显示创建的连续查询
SHOW CONTINUOUS QUERIES
# 删除
DROP CONTINUOUS QUERY cq_http_handler_stats_duration ON statsdb

图表

grafana 是 influxdb 的良好伴侣, 写个query语句就能得到很炫酷的图形. 比如接口请求量图表按handler_name, response_code, time(10s) group by就得到了.

分享到 评论

GRPC文档阅读心得

主要是两个文档, grpc repo的文档 https://github.com/grpc/grpc/tree/master/doc , grpc-go repo的文档 https://github.com/grpc/grpc-go/tree/master/Documentation.

grpc-go 文档


gRPC Server Reflection Tutorial

在代码中import "google.golang.org/grpc/reflection"包, 然后加一行代码reflection.Register(s), 就可以启用 server reflection. 就可以用grpc_cli去进行获得服务列表, 方法列表, message结构体定义了. reflection.Register(s)实际上是注册了一个特殊的service, 它能列出server中已注册的服务和方法等信息.

Compression

encoding.RegisterCompressor方法取注册一个压缩器, 启用了压缩的话, 服务端和客户端双方都要进行同样的处理, 服务端在newServer时要带上compressor的serverOption, 客户端在dail的时候要带上WithDefaultCallOptions的DialOption, DialOption加上压缩解压的处理, 不然会得到一个 Internal error, 和HTTP方式一样, 压缩类型体现在content-type的header上.

Concurrency

Dial得到的ClientConn是并发安全.
stream的读写不是并发安全的, sendMsg或RecvMsg不能在多个goroutine中并发地调用,但可以分别在两个goroutine中处理send和Recv.

Encoding

序列化反序列化

自定义消息编码解码, 注册一个实现 Codec 接口的对象即可, 然后在Dial或Call时带上grpc.CallContentSubtype这个CallOption, 这样就可以自动处理这个带这个content-type的请求. 默认为 proto

压缩解压缩

自定义压缩解压缩, 注册一个实现 Compressor接口的对象即可, 然后在Dial或Call时带上grpc.UseCompressor这个CallOption.

[Mocking Service for gRPC

](https://github.com/grpc/grpc-go/blob/master/Documentation/gomock-example.md)

主要讲如何在单元测试中mock, 用gomock命令行生成实现xx接口的代码, 没什么特别的

[Authentication

](https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-auth-support.md)

主要讲如何进行身份验证, 没什么特别的

Metadata

metadata类似HTTP1中的header, 数据结构都是一样的type MD map[string][]string,
key都是大小写不敏感的, 但实现规范和HTTP1不一样, HTTP1是按单词之间用连字符”-“分隔, 每个单词第一个字母大写这样的规范来的, 处理起来消耗更大, 而metadata是全转为小写, 实际使用过程中, 提前规范化key能提高不必要的strings.ToLower调用.
用-bin结尾的来传递二进制数据.

服务端handler用metadata.FromIncomingContext(ctx)拿到metadata, 客户端用metadata.AppendToOutgoingContext来附加kv到ctx中.

如果服务端handler又想附加一些信息返回client, 那么就要通过header和trailer传递, 类似responseHeader.

func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
// create and send header
header := metadata.Pairs("header-key", "val")
grpc.SendHeader(ctx, header)
// create and set trailer
trailer := metadata.Pairs("trailer-key", "val")
grpc.SetTrailer(ctx, trailer)
}

然后客户端在调用的时候传要保存的header和trailler的指针到CallOption中, 调用完后指针指向的metadata map就有数据了, 坦率地讲, 我觉得这样处理很麻烦.

var header, trailer metadata.MD // variable to store header and trailer
r, err := client.SomeRPC(
ctx,
someRequest,
grpc.Header(&header), // will retrieve header
grpc.Trailer(&trailer), // will retrieve trailer
)
// do something with header and trailer

Keepalive

gRPC会定时发http2 ping帧来判断连接是否挂掉, 如果ping没有在一定时期内ack, 那么连接会被close.

Log Levels

grpc-go包默认用gpclog包打日志, grpclog包默认是用多个*log.Logger来实现日志级别, 默认输出到stderr, 对于生产环境, 肯定要集成到自己的日志流里去, 接口是个好东西, grpclog包允许setLog, 实现grpclog.LoggerV2接口即可.

info日志包括:

grpclog里的info是为了debug

  • DNS 收到了更新
  • 负载均衡器 更新了选择的目标
  • 重要的grpc 状态变更

    warn日志包括:

    warning日志是出现了一些错误, 但还不至于panic.
  • DNS无法解析给定的target
  • 连接server时出错
  • 连接丢失或中断

    error日志包括:

    grpc内部有些error不是用户发起的函数调用, 所以无法返回error给调用者, 只能内部自己打error日志
  • 函数签名没有error, 但调用方传了个错误的参数过来.
  • 内部错误.

    Fatal日志:

    fatal日志是出现了不可恢复的内部错误, 要panic.

grpc 文档


之前一直有个误区, 多个连接比单个连接要快, 看了 grpc-go issues1grpc-go issues2 以及 HTTP2文档 才发现, 由于HTTP2有多路复用的特性, 对于同一个sever, 只需要维护一个连接就好了, 没有必要用多个连接去并行复用数据流. 连接数量减少对提升 HTTPS 部署的性能来说是一项特别重要的功能:可以减少开销较大的 TLS 连接数、提升会话重用率,以及从整体上减少所需的客户端和服务器资源。

分享到 评论