
写在前面
上线遇到问题不可怕,关键是要有解决问题的思路。要大胆猜测问题产生的原因,同时静下心来通过做实验的方式验证自己的猜想。解决问题的过程就是自我成长的过程。
这篇文章产生的背景,来自于一次app生产上线,从焦头烂额到问题完美解决,期间经历了无数坎坷,最终总结出了这篇血与泪的教训,让我们的故事正式开始吧~
0-生产冒烟的非必现bug
2018年5月16日,经过开发、测试、产品验收,3.5.5版本终于上线了。
“请求不到数据,特别慢,用的4g”,语气中带些焦躁。
“没事啊,我的都正常,内网外网都可以,是你手机出问题了吧”
“我的是先开始慢,然后就快了”
“我的也是”
......
混乱的状态没持续多久,问题调差行动就正式开始了。
1-问题出在了哪?
最
难的,莫过于定位问题。很多时候,找出了问题所在,问题已经解决了一大半。对于这种有的手机好,有的手机不行的问题,最让人头疼。我们能做的,就是收集大量和问题有关的信息,找规律。
梳理现象
3.5.5版本,部分机型使用移动4g网络访问,访问接口,速度异常缓慢(10s左右),但是过后会变快。h5正常
更换手机apn,访问速度会变快
同一台手机,更换手机卡,访问速度非常快
同一个手机卡,换到别的手机,访问速度非常快
问题猜想
问题无非可以分为两大类,服务器或客户端。
客户端可能存在的问题:
1.1 网络请求工具httpurlconnection
1.2 网络框架volley
1.3 编译版本
1.4 机型
服务器可能存在的问题:
1.5 公司服务器网络处理
1.6 公司服务器移动网络层问题
逐一排查
1.1 网络请求工具HttpURLConnection
猜想:请求过多? 证书校验 ?
1.1.1 单一网络请求
由于app内有大量网络请求,为了防止其他请求干扰结果,改动app,在欢迎页面请求接口,挑选在真实情况中发生请求慢的情况的接口,只请求一个接口一次,发现速度很慢。
说明:网络请求慢和多次请求没有关系
1.1.1 HttpURLConnection请求
请求序列
响应时间
1
10s
2
1s
3
1s
4
1s
5
1s
1.1.2 使用OkHttp:OkHttp POST请求:
请求序列
响应时间
1
10s
2
1s
3
1s
4
1s
5
1s
说明,网络请求慢和网络库没有直接关系
1.2 Volley网络框架
猜想:如果volley框架有问题,那么:
a 不用volley请求,如果请求依然很慢,则说明volley可能没问题;
b 如果替换volley中的HttpURLConnection,请求依然很慢,则说明Volley本身没有问题,是链接网络的类库出现问题
因为1.1中已经证明,HttpURLConnection请求第一次会很慢,因此a是正确的,说明volley可能没问题,接下来证明b:
使用OkHttp替换Volley中的HttpUrlconnection,app第一次请求仍然10s
说明:Volley框架是没有影响的
1.3 编译版本
调整编译版本,多次重复上述实验,无影响。
说明:编译版本无问题
1.4 机型
实验当天:小米、锤子、OPPO、三星S8、华为P6均有问题
说明:机型无影响
1.5 公司服务器网络处理
猜想,问题产生当天,同事说其他app快的飞起,那说明别人家的务器肯定没问题,挑选宜人贷做对比实验:
测试组合
响应时间(3次求均值)
http://www.me.com
10s
https://www.yirendai.com
2s以内
由此确定:公司服务器所处网络、公司服务器处理肯定存在问题
1.6 公司服务器移动网络层问题
1.6.1 cient发送http请求后,到底什么时候才开始tcp链接?


请求发出后,tcp链接一直没有建立。在请求发出的这10s秒钟,到底发生了什么呢?为什么过了这么久才开始建立tcp链接?
1.6.2 分析http请求源码
通过查阅资料,发现Okhttp底层处理tcp链接的类为AndroidPlatform.class,打断点进入:

惊奇发现,socket建立的链接地址,居然不是公司的ipv4服务器,而是一个ipv6地址!更为惊奇的是,socket链接超时链接时间恰巧为10s!
让断点执行完毕。
等待10s后,断点再次进入!

这次进来的是socket链接ip为公司ipv4地址,说明ipv6建立tcp链接超时,重试另一个ip,请求瞬间成功。
抓包分析现象:


发现,DNS服务器先后返回了2个域名! 第一次是有问题的的ipv6地址,第二次是正确的公司域名,因此确定问题所在

使用wifi环境,发现域名完全正确,更加说明了是DNS服务器的问题
2-如何解决
1 服务器在DNS中去掉ipv6的域名
2 Android客户端,自定义DNS解析,过滤掉ipv6
3 购买httpDNS
根据自己的情况,以上方法任选
3-那些让我们困惑的点
3.1 为什么只有移动有问题
电信抓包:

联通抓包:

wifi抓包:

实验证明,电信、联通、wifi,都只请求了一次dns
只请求一次的深层次原因,在3.2中解答。
3.2 网络框架为啥发起了两次DNS请求
android dns 请求流程图如下:

java中调用

最终调用的方法是bionic库中的getaddr:https://android.googlesource.com/platform/bionic/+/master/libc/dns/net/getaddrinfo.c

我们看到,ai_family是java传过来的,对应AF_UNSPEC。通过分析代码,首先判断ipv6、ipv4是否可用,然后会先后配置ipv6、ipv4的请求。

判断ipv6、ipv4是否可用的方法为_have_ipv6和_have_ipv4,方法描述写的非常清楚:
The following functions determine whether IPv4 or IPv6 connectivity is available
通过代码分析得知:移动网络之所以请求两次DNS,是因为ipv6网络可用
为了验证上述说法,使用ifconfig命令,分别查看手机上插入移动、联通、电信手机卡的网络情况
移动卡:

联通卡:

电信卡

wifi:

发现:只有移动网络的ipv6是可用的,拥有公网可访问2000开头的ip,其他机型只有fe80开头的local ip,验证了以上猜想
3.3 Ipv 6可用,为啥会超时
3.3.1 使用ipv6test
发现https://www.me.com的AAAA DNS没问题,但是webserver不同

测试ipv6.google.com

3.3.2 验证公司服务器
在digitalocean上申请一台支持ipv6的Droplet(选择digitalocean做实验平台,是因为digitalocean支持ipv6)

可以解析到ipv6.google.com

ping6可以ping通服务器

curl也可以取到数据,证明这台服务器支持ipv6
然后测试公司提供的ipv6


事实证明,公司域名拥有ipv6地址,但是ipv6地址不可以访问.
备注:公司拥有ipv6地址的原因是符合AppStore审核规则
3.4 为什么更改apn为wap以后,链接速度变快了


因为换成wap的apn之后,socket可以通过wap的代理,直接建立链接

4-总结与收获
1 遇到网络问题,可以从socket层代码进行分析
2 多了解android底层代码,可能日常开发过程中使用的机会很少,但是可以为以后解决疑难问题打下基础
3 ipv6国情:整体ipv6覆盖率非常非常低,主要的ipv6网络是教育网,商用ipv6几乎为0。通过做实验,发现移动、联通、电信三大运营商,只有移动支持ipv6




