京东App秒级百G日志传输存储架构设计与实战( 二 )


世界上有一个著名的法则叫"奥卡姆剃刀定律" , 讲的是程序员该如何选择合适的剃刀 , 来让自己的秀发光滑柔顺有光泽 。
其实不是的 , 该定律主要就是八个字"如无必要 , 勿增实体" 。当一个流程难以支撑当前的业务时 , 我们就该审视一下 , 哪些步骤是不必要的 。

京东App秒级百G日志传输存储架构设计与实战

文章插图
 
从这个通用流程中 , 其实我们很容易就能发现 , 我们经历了很多读写 , 每次读写都伴随着磁盘的读写(包括MQ也是写磁盘的) , 和频繁的序列化反序列化 , 以及翻倍的网络IO 。
那么让我们挥舞起奥卡姆的剃刀 , 做一些删减 , 把非必要的部分给删掉 , 就变成了下图的流程:
京东App秒级百G日志传输存储架构设计与实战

文章插图
 
我们发现 , 其实写本地磁盘、和MQ都是没有必要的 , 我们完全可以将日志数据写到本地内存 , 然后搞个线程 , 定时通过UDP将日志直接发送到worker端即可 。
worker接收到之后 , 解析一下 , 写入自己的内存队列 , 再起数个异步线程 , 批量将队列的数据写入ClickHouse数据库即可 。
大家可能看到了 , 下图的流程中 , 那个圆圈明显比上图的圆圈要小 , 这是为什么呢?因为我做了压缩 。
前文讲过 , 我们单条报文40k-2M , 这是一个非常大的报文 , 这里面都是一些用户请求的入参Json和出参Json以及一些中途日志 , 我们完全没有必要将原文原封不动往外传输 。
通过采用主流的snappy、zstd等压缩工具类 , 可以直接将字符串压缩成byte[]再往外传输 , 这个被压缩后的字符串 , 直至入库都是byte[] , 全程不对大报文解压 。
那么这个压缩能压多少呢 , 80%-90% , 一个60k的报文 , 往外送时就剩6-8k了 , 可想而知 , 仅仅压缩一下原始数据 , 就在整个流程中 , 节省了巨大的带宽 , 同时也大幅提升了worker的吞吐量 。
这里有个小细节 , udp单个最大报文是64kb , 如果我们压缩后 , 还是超过了64kb的话 , udp是送不出去的 , 这里可以选择发个http请求送到worker即可 。
通过上图 , 我们可以看到 , 当流程中的某些环节并不是必需的时 , 我们应该果断砍掉 , 不要轻易照搬网上的方案 , 而应该选择更适合自己的方案 。下面我们详细讲一下系统是如何设计、运转的 。
更强悍的日志搜集系统
京东App秒级百G日志传输存储架构设计与实战

文章插图
 
我们来审视一下这个链路极短的日志搜集系统 。
配置中心:用来存储worker的IP地址 , 供客户端获取自己模块所分配的worker集群的ip 。
client:客户端启动后 , 从配置中心拉取分配给自己这个模块的worker集群的IP , 并轮询将搜集的日志压缩后发送过去 , 通过UDP的方式 。
worker:每个模块会分配数量不等的worker机器 , 启动后上报自己的IP地址到配置中心 。接受到客户端发来的日志后 , 解析相应的字段 , 批量写入clickhouse数据库 。
clickhouse:一个强大的数据库 , 压缩比很高 , 写入性能极强 , 按天分片 , 查询速度佳 。非常适合应用于日志系统这种写入极大 , 查询较少的系统 。
dashboard:可视化界面 , 从clickhouse查询数据展示给用户 , 具有多条件多维度查询功能 。
大家都能看出来 , 这其中最关键的地方是worker端 , 它的承接流量、消费性能、入库性能将决定着整个链路能否良好地运转 。
我们主要分别讲解一下client端和worker端的实现 。
京东App秒级百G日志传输存储架构设计与实战

文章插图
 
Client端聚合日志一次请求中 , 我们通常要保留的日志信息主要有:
(1)请求的出入参>
如果是http web应用 , 要获取出入参比较简单的方式就是通过自定义filter即可 。client的sdk里定义了一个filter , 接入方通过配置该filter生效即可搜集到出入参 。


推荐阅读