PS: 人生和代码一样,不反思、不迭代就无法获得成长。
最近接触到单双栈问题,简单梳理下,双栈与单栈,也就是 IPV4 相比,双栈客户端的应用程序会遇到明显的连接延时,使得双栈客户端的用户体验变差,下面了解一下双栈问题以及其解决方法,主要内容如下:
双栈选择问题及 IPV6/IPV4 选择问题,首见于 RFC1671 文档,IPV6(Internet Protocol version 6) 就是互联网协议第六版,IPV4(Internet Protocol version 4) 就是互联网协议第四版,IPV6 主要解决的问题是 IPV4 的地址资源日益枯竭,IPv6 增强的关键是将 IP 地址空间从 32 位扩展到 128 位,从根本上实现不受限制的唯一 IP 地址,双栈选择问题是在互联网迅速发展的大背景下出现,当处于双栈状态下,DNS 解析出来的地址有两类地址,,单栈状态下,DNS 解析出来是 IPV4 或 IPV6 地址,当然目前说到单栈一般都是默认单栈 IPV4。
当 IPV6 不能访问时,支持 IPV6 的程序需要延迟几秒钟才能正常切换到 IPV4,这会影响用户体验,为了不影响用户体验有些系统会直接禁用 IPV6。
IPV6 不能访问的原因如下:
Reasons for such failure include no connection to the IPv6 Internet, broken 6to4 or Teredo tunnels, and broken IPv6 peering.
下面看下 IPV6 连接失败的流程图:
如上图所示,客户端域名解析获取到 IPV6、IPV4 地址,然后先请求 IPV6 未连接成功,几秒后切换到 IPV4 连接成功,其中 IPV6 等待连接到连接失败的这段时间就是 IPV6 无法访问时的耗时。
RFC6555 中定义了一种减少可见延迟的算法要求 Happy Eyeballs,其最基础的两个目标如下:
下面是上述思想的示意图如下:
如上图所示,客户端同时通过 IPV6 和 IPV4 发送两个 TCP SYN 包,IPV6 未连接成功,IPV4 则响应成功,重试 IPV6 直到用户放弃连接 IPV6 直接切换到 IPV4 即可。
执行完上述过程后,客户端则知道 IPV6 和 IPV4 地址是否连接成功,客户端可以把结果缓存起来,避免后续的连接尝试中影响网络,如在上面示例中 IPV6 连接是失败,后续连接中可以直接切换到 IPV4 进行连接,可以为缓存连接结果设定一个有效期,如 10 分钟,之后可刷新连接状态,这就是在一定程度上减少了 IPV6 连接异常时的耗时,有助于增加用户体验。
下面是一个 IPV6 工作正常的示意图:
如上图所示,客户端同时通过 IPV6 和 IPV4 发送两个 TCP SYN 包,IPV6 和 IPV4 都连接成功,IPV6 直接连接成功,则直接走 IPV6,忽略 IPV4 即可,同样记录 IPV6 连接状态,可在设定的有效期内直接 IPV6 进行连接,
只要客户端主机是支持双栈的,Happy Eyeballs 机制就会一直存在,只要存在仅支持 IPV4 的服务器存在,Happy Eyeballs 就会一直存在,随着时间的推移,IPV4 将会逐渐退出历史舞台,Happy Eyeballs 的实现场景可能不同,但是基本遵循上述要求。
一般应用层使用到的域名解析 API 如下:
public static InetAddress[] getAllByName(String host)
上述方法会返回域名 host 对应的 IP 地址,此时就可以根据 IP 地址是 IPV6 地址还是 IPV4 地址进行单双栈问题的适配了,具体实现可以根据需求进行调整和完善,上面介绍的 Happy Eyeballs 主要就是在解决双栈时带来的延时问题,大型 App 应该都有自己的对应算法实现,有的甚至 DNS 这块也是自己实现的,这里了解一下。