如何基于eBPF实现跨语言、无侵入的流量录制?

测试是产品发布上线的一个重要环节,但随着业务规模和复杂度不断提高,每次上线需要回归的功能越来越多,给测试工作带来了巨大的压力。在这样的大背景下,越来越多的团队开始使用流量回放对服务进行回归测试。

在建设流量回放能力之前,我们必须将线上服务的流量录制下来。通常要结合对流量特征的要求、实现成本、对业务的侵入性等方面综合考虑,选择不同的实现方式。

65e14fce145f07c3c8cf283e67c09149.jpeg

对于 Java 和 PHP 语言,目前业界已经有比较成熟的解决方案 jvm-sandbox-repeater、rdebug,基本可以做到低成本、无侵入式的流量录制;但 Go 语言由于缺少像 jvm 或 libc 等可利用的中间层,现有的方案 sharingan 需要修改官方 Go 源码并且侵入业务代码,稳定性风险较大;并且随着官方 Go 版本升级,需要持续维护迭代,使用和维护成本较高。

鉴于滴滴多语言的技术栈,我们经过调研发现可以通过 eBPF 实现一种跨语言无侵入的流量录制方案,大幅降低流量录制的使用和维护成本。        

流量录制原理

录制内容

流量回放时需要对下游依赖服务进行 mock,因此录制的一条完整流量中不仅需要包含入口调用的请求/响应,还需要包含处理这次请求时所调用依赖服务的请求/响应。

5d02c205072f4e1e180fffc09b163837.jpeg

实现思路

在介绍流量录制方案之前,我们先来看一个请求的处理过程(简化后):

a823246c46434390182904d7b1dc3aa8.jpeg

观察上述流程我们发现目标服务处理一个请求的大致流程如下:

  • 首先,调用 accept 获得一个调用方的连接;

  • 第二步,在这个连接上通过调用 recv 读取请求数据,解析请求;

  • 第三步,目标服务开始执行业务逻辑,过程中可能需要调用一个或多个依赖服务,对于每一次依赖服务调用,目标服务需要通过 connect 与依赖服务建立连接,然后在这个连接上通过 send 发送请求数据,通过 recv 接收依赖服务响应;

  • 最后,目标服务通过 send 给调用方返回响应数据。

为了实现流量录制,我们需要把图中所有的请求和响应数据保存下来。传统的流量录制方法需要跟踪服务框架、RPC框架、依赖服务sdk等所有涉及发送/接收数据的方法,将数据收集并保存下来。由于框架和sdk多种多样,需要大量的代码改造和开发工作,成本难以控制。

这里我们考虑更通用的方式:跟踪 socket 相关操作,例如 accept、connect、send、recv 等。通过这种方式我们可以不用关心业务中使用的应用层协议、框架、sdk等,实现更通用的流量录制方法。

但是,由于实现录制的位置更底层,能够获取的上下文信息更少,只有每个 socket 发送和接收的数据是不够的。我们需要借助其他信息对原始数据进行串联,从而组装完整的一条流量。

区分不同的请求

线上服务处理的请求大多是并发的,同时会有多个请求交织在一起,我们录制到原始数据是分散的,如何把同一个请求的数据合并,把不同请求的数据区分开呢?通过分析实际的请求处理过程,我们不难发现:

1、通常情况下,每个请求是在单独的线程中进行处理的。   

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值