字节跳动自研线上引流回放系统的架构演进( 三 )


3.2 流量解析引流源采集上来的原始流量还是第四层协议,为了支持一些更复杂的功能,比如过滤,多路输出,历史流量存储,流量查询及流量可视化等等,我们需要将四层流量解析到七层 。字节跳动内部服务使用得比较多的协议是 Thrift 和 HTTP ,这两个根据协议规范即可很好地完成解析 。
流量解析有一个难点是判断流量的边界,区别于 HTTP/2 等的 Pipeline 连接复用传输形式,Thrift 和 HTTP/1.X 在单条连接上严格按照请求-响应对来进行传输,因此可以通过请求和响应的切换分隔出完整的请求或响应流量 。
3.3 流量应用对于线上采集的流量,不同用户会有不同的业务用途,如压测平台可能希望将流量先持久化到 Kafka,然后利用 Kafka 的高吞吐发压;有些研发同学只是简单从线上引一份流量转发到自己的开发环境做新特性测试;有些同学希望转发 QPS 能达到一定水位以实现压测的目的;还有的是特定流量会触发线上 coredump ,他们希望把这段流量录制下来线下 debug 等等 。针对不同的场景,我们实现了若干流量输出形式 。

字节跳动自研线上引流回放系统的架构演进

文章插图
 
下面会着重介绍转发和存储 。
3.3.1 转发
字节跳动自研线上引流回放系统的架构演进

文章插图
 
结构如上图,emitter 会在 zookeeper 上注册自身,scheduler 感知到 emitter 节点信息,将任务根据各个 emitter 节点的标签和统计信息过滤/打分,然后调度到最合适的节点上 。这里有个问题是为什么不直接使用无状态服务,由每个 emitter 实例均等地转发,而采用 sharding 方案,主要是基于下面几点考虑:
  1. 如果每个任务均摊到所有实例上执行,那每个实例需要和全部下游 endpoint 建立连接,在海量任务下的 goroutine、连接、内存等资源占用是不可接受的
  2. 通过将单个任务的处理收敛到少数实例上,提高了对单个 endpoint 的请求密度,从而避免因为连接 idle 时间过长被对端 close,复用了连接 。
由于 emitter 对性能比较敏感,我们为此也做了很多优化,比如使用了 fasthttp 的 goroutine 池避免频繁申请 goroutine,对连接的 reader/writer 对象池化,动态调节每个 endpoint 的工作线程数量以自适应用户指定 QPS,避免 goroutine 浪费及闲置长连接退化成短连接,全程无锁化,通过 channel+select 做线程同步和数据传递等等 。
3.3.2 存储
字节跳动自研线上引流回放系统的架构演进

文章插图
 
存储分为了两层,数据层和索引层,采用双写模型,并有定时任务从数据层纠错索引层保证两者的最终一致性 。存储需 要支持回放和查询两种语义,Data Layer 抽象成了一个支持 KV 查询,按 Key 有序,大容量的存储模型,Index Layer 是 Multi-index->Key 映射模型,通过这两层即可满足流量查询+回放的需求 。所以 Data Layer 和 Index Layer 底层实现是模块化的,只需符合上述模型并实现模型定义 API 。
Data Layer 的理想数据结构是 LSM tree,写入性能出色,对于流量回放场景,需要按 key 有序扫描流量记录,因为 LSM 满足按 key 的局部性和有序性,可以充分利用 page cache 和磁盘顺序读达到较高回放性能 。分布式 LSM Tree 业界比较成熟的开源产品是 HBase,字节跳动内部也有对标产品 Bytable,我们同时实现了基于这两个引擎的 Data Layer,经过一系列性能 benchmark 我们选择了 Bytable 作为 Data Layer 实现 。
【字节跳动自研线上引流回放系统的架构演进】Index Layer 使用了 ES 的能力,因而可以支持用户的复合条件查询,我们会预先内置一些查询索引,如源服务,目标服务,方法名,traceid 等等,流量查询目前的使用场景一个是作为服务 mock 的数据源,可以屏蔽掉功能测试或者 diff 中不必要的外部依赖,还有一个功能是流量可视化,用户通过请求时间,traceid 等等,查看特定请求的内容 。其他场景功能还有待进一步发掘 。
3.4 业务场景支持3.4.1 支持通用的 diff 能力Diff 验证是互联网公司在快速迭代下保持产品质量的一个利器,类似 Twtiter 的 Diffy 项目,都是通过线上流量的录制回放来实现 。但是它的适用场景也很有限,因为是直接在生产环境上通过 AB 环境做回放,无法支持写的流量 。虽然阿里巴巴的 doom 平台可以解决写场景的回放隔离问题,但是它是在应用程序中通过 AOP 来实现的,强绑定 JAVA 生态 。
通过 ByteCopy 的无侵入引流和流量存储回放能力,结合我们自研的 ByteMock 组件,我们提供了面向业务的无侵入 diff 解决方案,并解决了写隔离的问题 。


推荐阅读