Aiki 网络控制和数据包捕获测试技术实践
★
▌导语
移动互联网时代,网络形态呈现三大特点:
(1)多样的接入网络:2G/3G/4G/Wi-Fi,不同的协议,不同的制式,不同的速率。
(2)复杂的现实场景:空旷的大街,拥挤的体育场,飞驰的汽车,地下穿梭的地铁。
(3)动态的随机变化:强网、弱网、无网,下一刻的网络不确定会怎样。
爱奇艺测试团队结合视频播放异常场景的网络模拟需求,在开源方案ATC(Augmented Traffic Control)的基础上,定制开发动态配置能力,优化了API接口,实现了QTC工具(QIYITraffic Control),可模拟网络的动静态复杂变化;同时,实现了一种低损耗的自动化网络抓包方案,支持高精度性能测试场景的网络抓包和数据分析,基本不影响网络自身传输质量。
以下文章与大家一起交流、探讨。
★
▌背景
在功能测试方面,异常网络环境下保证产品功能正确性和保护用户体验,是互联网产品交付的重要指标,需要高可用的网络控制工具在实验室环境模拟各种现实异常网络场景。目前市场上的工具大概分为两类:一是硬件控制,如网络损伤仪;二是软件控制,商业软件如Fiddler与Charles,开源软件如ATC与clumsy等。硬件方案可提供高精度的动态网络控制但成本较大,同时二次定制开发难度偏高;软件方案成本低且精度略差,同时难以模拟动态网络。
在性能测试方面,开播时间是视频类APP重要的性能指标之一,衡量的是用户等待开播的延迟,常规线下测试是收集多次开播时间数据,经过数据处理得出一个线下测试的开播时间与历史值比较,是APP版本上线前都要测试的内容。
▌遇到的问题
1.需要一个稳定模拟指定的、持续动态变化的网络的工具
通过对网络环境建模,任意现实场景都可以划分成强网、弱网、无网状态,并量化成带宽、时延、丢包、抖动、误码等核心指标。一个理想的网络控制工具,就是根据量化指标数据的连续输入,持续模拟网络的动态变化(根据线上问题分析,大部分问题发生在网络变化的时机)。
图1 网络模型图
2.需要一个稳定的低干扰的自动化抓包解决方案
视频开播是集客户端逻辑、网络状况及服务端逻辑三方面共同作用的结果,除了时间数据,往往需要网络包来辅助分析。抓包在常用测试中是一个手动测试项,常常通过360Wi-Fi、小米Wi-Fi等组网支持。这类热点组网简单,可以快速用于问题的定位和分析,但它的性能测试问题也比较突出,主要问题有三个:第一,app设备需要连入热点设备提供的网络,同一网络,存在干扰; 第二,热点设备支持距离短,长时间运行不稳定;第三,手工方案效率低,无法用于批量数据采集。
▌解决方案
图2 整体结构图
如图2给出的整体结构图,下面介绍解决方案是如何解决上面提到的问题。
·QTC方案
根据需求来筛选关键特性,画出方案选型表格,并制定具体的测试活动来填充表格。如下表1,很明显硬件方案能够满足测试需求,但基于ATC开源软件做二次定制开发,同样能构建一个低成本的工具并满足需求。
图3 工具方案选型表
图4 QTC方案设计图
1.产品形态
硬件要求:双网卡工作站 + 无线接入设备
软件要求:Linux系统 + python2.7
服务部署时间:< 5分钟
适用场景:设备需要接入QTC服务搭建的LAN(QTC热点,或者QTC交换机)
不适用场景:带宽配置<64 bps,网络参数切换时间 < 2s
图5 QTC框架设计图
2.使用方法
使用浏览器作为配置页面,跨平台使用,支持Android/IOS/windows多端设备使用。直接在测试设备上进行参数配置,无需其它测试设备协助,在无线热点覆盖区域内实现随时随地的移动测试。
图6 QTC使用UI界面
3.技术细节: 动态场景配置(自研)
设计新的数据结构来存储动态场景(兼容静态场景数据结构),创建新进程来动态设置,使用进程间同步机制传递进程信息来刷新前端UI。
图7 QTC场景参数结构
图8 QTC配置流程图
·低损耗网络抓包方案
基于问题,解决方案由硬件解决方案和软件解决方案共同组成。
1.硬件方案
主要解决稳定和抓包两个问题。测试团队经过实践,落地基于镜像交换机+无线AP的组网方案。该方案,核心在于镜像交换机,它将手机的网络流量镜像一份到图中PC机器的网口,PC网口后续用于抓包,这样就把应用的网络响应与抓包在物理层分离,应用和抓包机器互不干扰,下图比较了常用抓包方案和新方案的优缺点。
图9 抓包方案对比
新方案通过使用无线路由器释放热点,无线网络较360 Wi-Fi和笔记本无线网卡有一个质的提升,举个例子,实际测试实践,360 Wi-Fi网络连入的手机,连接距离超过2米以后,网络经常变的不稳定,新方案在10米左右都能提供一个视频秒播的体验,使用路由器提供网络更加的稳定。
2.软件方案
主要解决自动化的问题。自动化的一个关键问题就是,手机如何自动化的开启和关闭抓包功能,这里使用网络请求的方式搭建这一通路。PC机本地实现一个HTTP 的监控服务,服务内部实现与抓包工具的交互;手机在执行自动化的场景中,如果需要抓包,通过HTTP请求的方式开启关闭PC机的抓包功能,实现自动化。
▌价值
通过和IT团队合作,将QTC节点部署到公司办公核心网络,在指定办公区域放射公共wifi信号,只要能接收到QTC热点信号的地方,就可以自行设置接入设备的网络环境,模拟各种异常网络场景。不区分业务形态,不区分接入的设备类型,且不同设备之间互不干扰,实现“移动测试”,同时在后台服务器上,可以抓包追踪所有的网络流量,实现“精准运维”。目前QTC服务已经应用于播放SDK、基线APP、TV app和短视频业务等多个产品的线下弱网测试,赢得了技术团队的认可和好评,有力提升了产品质量和研发效率。以TV app为例,借助QTC工具,新增26条弱网测试用例,已支持两个产品版本测试,平均每版本发现弱网功能bug约10个。
基于新的抓包组网,爱奇艺基线APP性能测试接入后,开播等性能测试可以自动收集网络包数据。通过使用抓包工具定位到了APP点击转场慢、网络请求DNS慢导致的开播性能问题。
▌未来规划
1. 现实场景的模型化,寻找更精准的测量方法和测试工具来采集现实场景的网络参数,并拓展采集维度,构建网络场景数据库。
2. 借助AI技术做对网络场景数据做智能挖掘,找到异常场景的特征值,提高测试的精准性。
3. 不仅是开播,只要客户端功能是需要网络请求,都可以使用网络插件来自动化收集网络包用于后续分析,未来将会扩展到更多的自动化测试场景中。
end
也许你还想看
扫一扫下方二维码,更多精彩内容陪伴你!
推荐阅读
-
Aiki 网络控制和数据包捕获测试技术实践
-
【Netty】「萌新入门」(七)ByteBuf 的性能优化-堆内存的分配和释放都是由 Java 虚拟机自动管理的,这意味着它们可以快速地被分配和释放,但是也会产生一些开销。 直接内存需要手动分配和释放,因为它由操作系统管理,这使得分配和释放的速度更快,但是也需要更多的系统资源。 另外,直接内存可以映射到本地文件中,这对于需要频繁读写文件的应用程序非常有用。 此外,直接内存还可以避免在使用 NIO 进行网络传输时发生数据拷贝的情况。在使用传统的 I/O 时,数据必须先从文件或网络中读取到堆内存中,然后再从堆内存中复制到直接缓冲区中,最后再通过 SocketChannel 发送到网络中。而使用直接缓冲区时,数据可以直接从文件或网络中读取到直接缓冲区中,并且可以直接从直接缓冲区中发送到网络中,避免了不必要的数据拷贝和内存分配。 通过 ByteBufAllocator.DEFAULT.directBuffer 方法来创建基于直接内存的 ByteBuf: ByteBuf directBuf = ByteBufAllocator.DEFAULT.directBuffer(16); 通过 ByteBufAllocator.DEFAULT.heapBuffer 方法来创建基于堆内存的 ByteBuf: ByteBuf heapBuf = ByteBufAllocator.DEFAULT.heapBuffer(16); 注意: 直接内存是一种特殊的内存分配方式,可以通过在堆外申请内存来避免 JVM 堆内存的限制,从而提高读写性能和降低 GC 压力。但是,直接内存的创建和销毁代价昂贵,因此需要慎重使用。 此外,由于直接内存不受 JVM 垃圾回收的管理,我们需要主动释放这部分内存,否则会造成内存泄漏。通常情况下,可以使用 ByteBuffer.clear 方法来释放直接内存中的数据,或者使用 ByteBuffer.cleaner 方法来手动释放直接内存空间。 测试代码: public static void testCreateByteBuf { ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(16); System.out.println(buf.getClass); ByteBuf heapBuf = ByteBufAllocator.DEFAULT.heapBuffer(16); System.out.println(heapBuf.getClass); ByteBuf directBuf = ByteBufAllocator.DEFAULT.directBuffer(16); System.out.println(directBuf.getClass); } 运行结果: class io.netty.buffer.PooledUnsafeDirectByteBuf class io.netty.buffer.PooledUnsafeHeapByteBuf class io.netty.buffer.PooledUnsafeDirectByteBuf 池化技术 在 Netty 中,池化技术指的是通过对象池来重用已经创建的对象,从而避免了频繁地创建和销毁对象,这种技术可以提高系统的性能和可伸缩性。 通过设置 VM options,来决定池化功能是否开启: -Dio.netty.allocator.type={unpooled|pooled} 在 Netty 4.1 版本以后,非 Android 平台默认启用池化实现,Android 平台启用非池化实现; 这里我们使用非池化功能进行测试,依旧使用的是上面的测试代码 testCreateByteBuf,运行结果如下所示: class io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeDirectByteBuf class io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf class io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeDirectByteBuf 可以看到,ByteBuf 类由 PooledUnsafeDirectByteBuf 变成了 UnpooledUnsafeDirectByteBuf; 在没有池化的情况下,每次使用都需要创建新的 ByteBuf 实例,这个操作会涉及到内存的分配和初始化,如果是直接内存则代价更为昂贵,而且频繁的内存分配也可能导致内存碎片问题,增加 GC 压力。 使用池化技术可以避免频繁内存分配带来的开销,并且重用池中的 ByteBuf 实例,减少了内存占用和内存碎片问题。另外,池化技术还可以采用类似 jemalloc 的内存分配算法,进一步提升分配效率。 在高并发环境下,池化技术的优点更加明显,因为内存的分配和释放都是比较耗时的操作,频繁的内存分配和释放会导致系统性能下降,甚至可能出现内存溢出的风险。使用池化技术可以将内存分配和释放的操作集中到预先分配的池中,从而有效地降低系统的内存开销和风险。 内存释放 当在 Netty 中使用 ByteBuf 来处理数据时,需要特别注意内存回收问题。 Netty 提供了不同类型的 ByteBuf 实现,包括堆内存(JVM 内存)实现 UnpooledHeapByteBuf 和堆外内存(直接内存)实现 UnpooledDirectByteBuf,以及池化技术实现的 PooledByteBuf 及其子类。 UnpooledHeapByteBuf:通过 Java 的垃圾回收机制来自动回收内存; UnpooledDirectByteBuf:由于 JVM 的垃圾回收机制无法管理这些内存,因此需要手动调用 release 方法来释放内存; PooledByteBuf:使用了池化机制,需要更复杂的规则来回收内存; 由于池化技术的特殊性质,释放 PooledByteBuf 对象所使用的内存并不是立即被回收的,而是被放入一个内存池中,待下次分配内存时再次使用。因此,释放 PooledByteBuf 对象的内存可能会延迟到后续的某个时间点。为了避免内存泄漏和占用过多内存,我们需要根据实际情况来设置池化技术的相关参数,以便及时回收内存; Netty 采用了引用计数法来控制 ByteBuf 对象的内存回收,在博文 「源码解析」ByteBuf 的引用计数机制 中将会通过解读源码的形式对 ByteBuf 的引用计数法进行深入理解; 每个 ByteBuf 对象被创建时,都会初始化为1,表示该对象的初始计数为1。 在使用 ByteBuf 对象过程中,如果当前 handler 已经使用完该对象,需要通过调用 release 方法将计数减1,当计数为0时,底层内存会被回收,该对象也就被销毁了。此时即使 ByteBuf 对象还在,其各个方法均无法正常使用。 但是,如果当前 handler 还需要继续使用该对象,可以通过调用 retain 方法将计数加1,这样即使其他 handler 已经调用了 release 方法,该对象的内存仍然不会被回收。这种机制可以有效地避免了内存泄漏和意外访问已经释放的内存的情况。 一般来说,应该尽可能地保证 retain 和 release 方法成对出现,以确保计数正确。