服务器不支持IPv6引起的网络连接超时

# 服务器不支持IPv6引起的网络连接超时

8月26日中午接到QA信息,部分用户反馈中配保网络连接巨慢,由于身边没有真机,不能进行场景复现。临时提供web服务解决用户问题。

8月27日进行真机测试,在移动网络的环境下,复现问题。在Android的帮助下review代码,未发现异常代码。怀疑是某个配置/DNS解析问题导致,随即搜集了一些资料 (opens new window)。但由于有其他优先级较高任务未深入。

8月28日再次debug,隐约发现有一处异常

okhttp3.internal.connection.RouteException: java.net.SocketTimeoutException: failed to connect to mobileapi.qipeipu.com/64:ff9b::79c9:1078 (port 443) from /2409:8955:4d0:6097:a263:657:3094:6c08 (port 42236) after 10000ms

由于网络知识匮乏,当时怀疑是DNS解析出错,后去向运维求证,得知服务器不支持IPv6。

[Online Ping IPv6 (opens new window)]

IPv6 Ping Output:
PING mobileapi.qipeipu.com(64:ff9b::79c9:1078 (64:ff9b::79c9:1078)) 32 data bytes

--- mobileapi.qipeipu.com ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3000ms


 ---- Finished ------ 

定位到问题,给出临时解决方案。

  1. 设置
  2. 更多(无线和网络)/移动网络
  3. 接入点名称
  4. 选择默认的接入点
  5. 【APN协议】和【APN漫游协议】设置成IPv4
  6. 保存

8月29日搜索相关资料

Android: Use only IPV6 IP , when the wifi router provides both IPV4 and IPV6 addresses (opens new window)

http://square.github.io/okhttp/3.x/okhttp/okhttp3/Dns.html (opens new window)

在Android同事的帮助下,实现了DNS解析选择器。

public class DnsSelector implements Dns {
	...
    addresses = Dns.SYSTEM.lookup(hostname);

    switch (mode) {
        case IPV6_FIRST:
            Collections.sort(addresses, IPv6FirstComparator);
            break;
        case IPV4_FIRST:
            Collections.sort(addresses, IPv4FirstComparator);
            break;
        case IPV6_ONLY:
            List<InetAddress> newInet6Addresses = new ArrayList<>();
            for (InetAddress inetAddress : addresses) {
                if (Inet6Address.class.isInstance(inetAddress)) {
                    newInet6Addresses.add(inetAddress);
                }
            }
            addresses = newInet6Addresses;
            break;
        case IPV4_ONLY:
            List<InetAddress> newInet4Addresses = new ArrayList<>();
            for (InetAddress inetAddress : addresses) {
                if (Inet4Address.class.isInstance(inetAddress)) {
                    newInet4Addresses.add(inetAddress);
                }
            }
            addresses = newInet4Addresses;
            break;
    }

    return addresses;
	}
	...
}

当天下午修复上线。

后续的思考:

  1. 为什么Sentry没有监听到网络连接异常?

    答:Sentry默认只会捕捉应用崩溃异常,需自己实现caught其他异常的方法。

  2. 为什么用户只反馈慢而没出现网络网络错误提示?(干扰问题定位)

    答:由于okhttp的重试机制 (opens new window),默认的如果连接到路由有超时会尝试下一条路线(如果有的话)。

# 问题出现原因

  1. 服务器不支持IPv6
  2. 路由连接时间过长(10s)

# 总结

定位这个bug的花费大量时间的主要原因是

  1. 没有深度debug,认定问题不会出现在okhttp上。
  2. 由于有其他任务没有跟app组深度协作定位问题,个人知识面窄。