再谈弱网测试

在去年,写过一篇手游弱网测试的文章,主要是介绍在客户端,运用Network Emulator Tookit工具模拟弱网情况,进行相关的测试工作,在当时,这个方法较好的满足了项目组弱网的专项测试工作。但最近发生了一个事情,导致了需要对弱网测试的方法更进一步探究,事情是这样的:某天项目组在集中测试的时候,发现客户端卡顿非常明显,程序怀疑是弱网情况下,造成的客户端卡顿。虽然事后排查证明,不是网络的锅,但程序这边希望QA可以把弱网加入到平时的测试工作中,而不是作为一个专项测试,只有在一些大版本的时候进行。

当然,我们依然可以运用Network Emulator Tookit工具,达到程序的期望,但我在想:为什么平时测试的时候,没有使用弱网测试呢,而只是作为一个专项测试来开展呢?一方面当然是,基础的网络库较为稳定,不需要我们时刻去测试;另一方面,还是由于测试环境的配置不够自动化。虽然说Network Emulator Tookit工具的使用不算复杂,但毕竟需要QA手动去搭建,人嘛,都用惰性的,你们肯定懂的。所以这次研究的主要目的是,如何自动化的配置一个弱网测试环境,方便的将弱网测试加入到日常的测试工作中。

一般而言,app分为客户端和server端,可以简单的认为,弱网加在客户端或者服务器的测试效果是差不多的,都能起到模拟app在弱网情况下的效果。这次调研过程中,考虑过几种不同的方案,有些没有实践,有些实践了,下面我会针对每个方案简单的说下思考过程和实践过程。

第一种方法是利用Facebook的开源框架ATC。但需要自己搭建一个wifi,这和公司IT部门的安全指导相违背,同时,由于自己搭建的wifi存在物理距离上的限制,所以没有采用这个方法。

第二种方法是设置手机wifi的默认网关属性,将默认网关的值修改到某个linux寄主的网关,然后通过tc命令,对这台linux机器的网络进行流程控制,达到模拟弱网的效果。可惜的是,由于公司wifi不支持修改默认网关,所以失败了。

第三种方法是设置sock5代理。

Windows系统下,通过Proxifier这个软件,设置全局sock5代理,实际测试发现,当我通过Proxifier软件,直接将sock5的代理设置成远程的配置的代理服务器后,一般的应用程序,比如chrome、音乐播放器等流程成功的走了设置的代理服务器。此时我在代理服务器上进行弱网设置,就达到了测试的目的。

但是很遗憾,对游戏进行如上操作,确没有到达预期的延迟效果。通过查看login进程的日志,可以看到游戏客户端的登入ip的确显示的是代理服务器的ip,说明成功了。但是当我通过iftop去查看网卡流量的时候,发现游戏内的通讯都不走代理了。这是为什么呢?原来我们游戏默认通讯(除了和login通讯是tcp)采用的是基于udp的协议,而很可惜,Proxifier这个软件,在设置成系统全局代理后,只能转发http、https、tcp协议,不支持udp协议。如果你测试的应用程序不是基于tcp的,那么上述方法是可行的。

我们的app是支持网络协议切换的,即可以通过配置文件设置是基础tcp还是udp的,默认是udp,当我将app的网络协议修改成tcp后,使用上述的方法实测可行。

当然还有一种方法,就是把Proxifier软件换成另一个支持udp转发的软件,但目前笔者没有找到,如果谁知道,也可以告诉我下。

手机端,通过设置wifi的http代理选项,可以开启sock5代理(通过设置PAC文件,网上有较多攻略,这里就不详细介绍了)。但是很遗憾,运行游戏,查看服务端的登入日志,显示没有通过代理服务器,但是打开微博、浏览器等,都显示是通过代理服务器访问的。估计在手机端,也需要下载某个应用,开启手机的全局代理,而不能简单的在wifi属性项中设置。

这里要特别说明下sock5代理的。

如上图,当我们开启代理后,客户端和服务端的通讯都是通过代理来访问的,但这里有几个需要注意的几个点是:

1:在代理服务器(一般是linux)上,通过tc实现某个客户端的流量限制,不可行。因为tc的流量限制一般发生在出队列,而当客户端的经过代理程序后,其ip就发生改变了,我们不知道改变后的ip是多少,所以也就不能针对某个客户端设置一个流量限制了。当然了,你可以通过开N个不同端口的代理,每个客户端连接不同的代理,然后在tc通过针对不通的端口的判断,实现某个客户端的流量限制。

2:既然不能通过linux上的tc去针对某个客户端限速,那么自然我们想到了,通过代理程序去实现,因为代理程序可以识别到不同客户端的原始ip,目前实现的的代理程序中,能够通过参数加上一个固定的延迟,还没有实现类似tc那样全面的模拟弱网的各种情况,这也是后面我需要去完善的。具体如何实现一个sock5代理的原理,网上有很多介绍,我也就不多说了。

如果你的app通讯是基于http的,那么你也可以直接通过使用Charles这个成熟的软件,实现我刚才说的。Charles可以在window、linux上运行,并开启一个代理,你可以通过对其配置文件的修改,指定开启代理的端口号,最终达到对指定客户端实现弱网测试的要求。

第四种方法:修改客户端的代码或者配置文件,改变需要连接的服务器ip,修改成某个linux机器,然后在这台linux机器上,通过iptable进行包转发,同时配合tc,实现流量的控制,达到弱网的测试要求。这种方法,由于笔者对iptable不是很熟,而且时间有限,就没有实际部署测试了。

第五种是方法直接在服务器对应的linux上,使用tc。

这里又可以分为三个小方法。

a:在对应的服务器所在linux上,对所有的出口流量加一个弱网环境:

tc qdisc add dev eth1 root netem delay 100ms loss 1%

这样所有连接到这个服务器的客户端都会加一个100毫秒的延迟和百分之1的丢包率。不过这个方法,首先你要确认好,你的服务器所在的linux的出口网卡是哪个,其次当这台服务器执行上述命令后,我通过xshell等工具连接控制linux的时候,也会收到弱网的影响,体验不好。

b:对指定目标ip的流量进行一个限制:

    tc qdisc del dev eth1 root

    tc qdisc add dev eth1 roothandle 1: prio

    tc qdisc add dev eth1parent 1:1 handle 10: netem delay 1s loss 2%

    tc qdisc add dev eth1parent 1:2 handle 20: pfifo_fast

    tc filter del dev eth1

    tc filter add dev eth1protocol ip parent 1: prio 1 u32 match ip dst 123.58.160.153 flowid 1:1   

这样,所有目标ip是123.58.160.153,都会被加上延迟1秒,百分之2的丢包率。这里有个问题,我们的服务器是架设在外网的,也就是说客户端通过公司内部wifi访问这台服务器的时候,显示的ip都是同一个,即公司的统一出口ip。所以想通过这个方法对某一个客户端设置一个弱网环境不可行。当然,可以通过把服务器架设到内网,或者用4G访问都可以绕过这个问题。

c:对指定端口的流量进行一个限制:

    tc qdisc add dev eth1 roothandle 1: prio bands 4

    tc qdisc add dev eth1parent 1:4 handle 40: netem delay 5s

    tc filter add dev eth1protocol ip parent 1:0 prio 4 u32 match ip sport 5655 0xffff flowid 1:4

    tc filter add dev eth1protocol ip parent 1:0 prio 4 u32 match ip sport 5656 0xffff flowid 1:4

通过指定的源端口,对流量进行一个控制,达到了弱网测试的目标。上面的5655 5656是我们服务器提供对外服务的接口。所有的客户端,会按照负载均衡的原则,连接不同的端口,我们可以通过对不通的端口附加不同的弱网策略,达到测试目的。

最终我采取的是上面c中提到的方法:对指定端口的流量进行一个限制,然后配合Jenkins达到一键配置弱网测试环境的效果。最终的执行界面是这样:

QA点击建置按钮后,就能按照填写的要求,构造一个弱网环境了。

当然,也可以简单点,通过下拉选项选择弱网环境,如下:

或者自己填写相应的弱网参数:

通过上述介绍的这些方法,不管被测应用是何种性质的,应该都能实现弱网测试环境的搭建,如果不能,希望可以得到你的反馈,一起讨论商量。

补充资料:

在运用TC对流量进行控制的过程中,对TC写法的理解还是花费了一定的时间,我总结了下面的关键理解点:

TC分为队列、类别、过滤器。过滤器对数据进行过滤,决定分配到哪个类别,每个类别对应一个队列,队列呢,是可以有限制的,比如带宽啊,比如延迟啊。过滤器中就可以针对ip、端口去过滤到具体的某个类别中,然后这个类别是属于某个队列的,我在这个队列设置中加了延迟,丢包等。最后根据TC的命令,其实画出来的结构图就是一颗树。

接下去需要要完善的点:

1:加入带宽的限制,用来模拟2G、3G、4G的情况。这个可以在TC中实现。

2:完善sock5代理程序,加入根据ip地址进行延迟设置和丢包率设置。