百度App网络深度优化:DNS 和连接优化
<img src="https://mmbiz.qpic.cn/mmbiz_jpg/XIibZ0YbvibkVpjar0iatrJ7krDLgzvpJEGjWka29v3P2m9ptp7icCcmLWNas0NIphRmJA44cmmwHBfIGicEW1yOuQg/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;">作者|蔡锐(百度 App 资深工程师)
编辑|覃云
网络优化是客户端几大技术方向中公认的一个深度<span style="color: black;">行业</span>,百度 App <span style="color: black;">亦</span>不例外,今天,<span style="color: black;">咱们</span>在<span style="color: black;">这儿</span>向<span style="color: black;">大众</span>介绍百度 App 网络深度优化的实践经验,内容<span style="color: black;">重点</span><span style="color: black;">包含</span> DNS 优化和连接优化,<span style="color: black;">期盼</span>对<span style="color: black;">大众</span>在网络方向的学习和实践有所<span style="color: black;">帮忙</span>。
<span style="color: black;"><span style="color: black;">1、</span>DNS 优化</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度起家于搜索,<span style="color: black;">全部</span><span style="color: black;">机构</span>的网络架构和<span style="color: black;">安排</span>都是基于标准的 internet 协议,<span style="color: black;">日前</span><span style="color: black;">已然</span>是全栈 HTTPS,来到移动互联网时代后,总的<span style="color: black;">基本</span>架构不变,但在客户端上<span style="color: black;">必须</span>做<span style="color: black;">非常多</span>优化工作。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">DNS(Domain Name System),它的<span style="color: black;">功效</span>是<span style="color: black;">按照</span>域名查出 IP <span style="color: black;">位置</span>,它是 HTTP 协议的前提,<span style="color: black;">仅有</span>将域名正确的解析成 IP <span style="color: black;">位置</span>后,后面的 HTTP 流程<span style="color: black;">才可</span>进行,<span style="color: black;">因此</span><span style="color: black;">通常</span>做网络优化会首选优化 DNS。</p>
<span style="color: black;">1. 背景</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">DNS 优化核心<span style="color: black;">必须</span><span style="color: black;">处理</span>的问题有两点:</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">【1】<span style="color: black;">因为</span> DNS 劫持或故障<span style="color: black;">导致</span>的服务不可用,<span style="color: black;">从而</span>影响用户体验,影响<span style="color: black;">机构</span>的收入。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">【2】<span style="color: black;">因为</span> DNS 调度不准确<span style="color: black;">引起</span>的性能退化,<span style="color: black;">从而</span>影响用户体验。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度 App 承载着亿级流量,每年都会遇到运营商 DNS 劫持或运营商 DNS 故障,整体影响非常<span style="color: black;">欠好</span>,<span style="color: black;">因此</span> DNS 优化刻不容缓,<span style="color: black;">经过</span>下图会更直观的<span style="color: black;">认识</span>运营商劫持或故障的原理。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/XIibZ0YbvibkVpjar0iatrJ7krDLgzvpJEGsLbJfTU68Lcw6OoVjo95LBDiaXyncNW33hjG85icREUkaVzew8SPfp0g/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>
运营商劫持或故障的原理
<span style="color: black;">2.HTTPDNS</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">既然<span style="color: black;">咱们</span>面临这么严峻的问题,<span style="color: black;">那样</span><span style="color: black;">咱们</span><span style="color: black;">怎样</span>优化 DNS 呢?答案<span style="color: black;">便是</span> HTTPDNS。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">大部分标准 DNS 都是基于 UDP 与 DNS 服务器交互的,HTTPDNS 则是利用 HTTP 协议与 DNS 服务器交互,绕开了运营商的 Local DNS 服务,有效防止了域名劫持,<span style="color: black;">加强</span>域名解析效率,下图是 HTTPDNS 的原理。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/XIibZ0YbvibkVpjar0iatrJ7krDLgzvpJEGL49bcTjiabxsPwqeCtFibCA0ZFHpdVfnCciaVXazn4N0xK0asztT52enw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>
HTTPDNS 原理 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度 App HTTPDNS 端上的实现是基于百度 SYS 团队的 HTTPDNS 服务,下图介绍了 HTTPDNS 的服务端<span style="color: black;">安排</span>结构。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/XIibZ0YbvibkVpjar0iatrJ7krDLgzvpJEGNpW0vI6GMC1Q2oBoTbCdW1xYM01AQQFUWnCVPibkDr6F5dMEaibmXerg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>
HTTPDNS <span style="color: black;">安排</span>结构 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">HTTPDNS 服务是基于 BGP 接入的,BGP 英文 Border Gateway Protocol,即边界网关协议,是一种在自治系统之间动态的交换路由信息的路由协议,BGP 可以<span style="color: black;">按照</span>当前用户的运营商路由到百度服务点的对应集群上,<span style="color: black;">针对</span>第三方域名,服务点会<span style="color: black;">经过</span>百度<span style="color: black;">安排</span>在运营商的 CDN 节点向其他域名权威 DNS 发起<span style="color: black;">查找</span>,<span style="color: black;">查找</span>这个运营商下域名的最优 IP。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度 App 独立实现了端的 HTTPDNS SDK,下图介绍了端 HTTPDNS 的整体架构。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/XIibZ0YbvibkVpjar0iatrJ7krDLgzvpJEGXDaicWSzliaHwQxEYL7j7licb5icEpnAmicN1FHibvjtBrdOGz212EibMUt1g/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>端 HTTPDNS 的整体架构
DNS 接口层<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">DNS 接口层<span style="color: black;">处理</span>的问题是屏蔽底层的细节,对外<span style="color: black;">供给</span>简单整洁的 API,降低<span style="color: black;">运用</span>者的上手成本,<span style="color: black;">加强</span><span style="color: black;">研发</span>效率。</p>
DNS 策略层 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">DNS 策略层<span style="color: black;">经过</span>多种策略的组合,使 HTTPDNS 服务在性能,稳定性,可用性上均保持较高的水准,下面讲解下<span style="color: black;">每一个</span>策略设计的初衷和<span style="color: black;">详细</span>实现。</p>
容灾策略 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">这是一个非常关键的策略,<span style="color: black;">重点</span><span style="color: black;">处理</span> HTTPDNS 服务可用性的问题,实践证明,这个策略<span style="color: black;">帮忙</span>百度 App 在<span style="color: black;">反常</span><span style="color: black;">状况</span>下挽救回<span style="color: black;">非常多</span>流量。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">【1】当 HTTPDNS 服务不可用并且本地<span style="color: black;">亦</span><span style="color: black;">无</span>缓存<span style="color: black;">或</span>缓存失效的时候,会触发降级策略,降级成运营商的 localDNS <span style="color: black;">方法</span>,虽然存在运营商事故<span style="color: black;">或</span>劫持的<span style="color: black;">危害</span>,但<span style="color: black;">保证</span>了 DNS 服务的可用性。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">【2】当 HTTPDNS 服务和 localDNS 服务双双不可用的<span style="color: black;">状况</span>下,会触发 backup 策略,<span style="color: black;">运用</span>端上的 backup IP。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">什么是 backup IP?backup IP 是多组<span style="color: black;">按照</span>域名<span style="color: black;">归类</span>的 IP 列表,可云端动态更新,方便后续运维<span style="color: black;">朋友</span><span style="color: black;">调节</span>服务端的节点 IP,不是所有域名都有对应的 backup IP 列表,<span style="color: black;">日前</span>百度 App 只能<span style="color: black;">保准</span>核心域名的可用性。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">既然是一组 IP,便有<span style="color: black;">选择</span>问题,backup IP <span style="color: black;">选择</span>机制是<span style="color: black;">怎么样</span>的呢?<span style="color: black;">咱们</span>的中心思想<span style="color: black;">便是</span>要在端上利用最小的代价,并且<span style="color: black;">思虑</span>服务端的负载<span style="color: black;">平衡</span>,得到相对正确<span style="color: black;">或</span><span style="color: black;">恰当</span>的<span style="color: black;">选择</span>结果。<span style="color: black;">经过</span>运营商和地理信息,<span style="color: black;">能够</span><span style="color: black;">选取</span>一个相对较优的 IP,但获取地理信息<span style="color: black;">必须</span>很大耗时,外加频次很高,代价很大,<span style="color: black;">因此</span><span style="color: black;">咱们</span><span style="color: black;">选取</span>了 RR 算法来代替上面的<span style="color: black;">办法</span>(RR 算法是 Round-Robin,轮询调度),<span style="color: black;">这般</span>客户端的代价降低到最小,服务端<span style="color: black;">亦</span>实现了负载<span style="color: black;">平衡</span>。</p>
安全策略 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">【1】HTTPDNS <span style="color: black;">处理</span>的核心问题<span style="color: black;">便是</span>安全,标准的 DNS <span style="color: black;">查找</span>大部分是基于 UDP 的,但<span style="color: black;">亦</span>有基于 TCP 的,<span style="color: black;">倘若</span> UDP 被封禁,就<span style="color: black;">必须</span><span style="color: black;">运用</span> TCP。不管是 UDP 还是 TCP,安全性都是<span style="color: black;">无</span><span style="color: black;">保证</span>的,HTTPDNS <span style="color: black;">查找</span>是基于标准的 HTTP 协议,为了<span style="color: black;">保准</span>安全<span style="color: black;">咱们</span>会在 HTTP 上加一层 TLS(安全传输层协议),这便是 HTTPS。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">【2】<span style="color: black;">处理</span>了传输层协议的安全性后,<span style="color: black;">咱们</span>要<span style="color: black;">处理</span>下域名解析的问题,上面<span style="color: black;">咱们</span><span style="color: black;">说到</span> HTTPDNS 服务是基于 BGP 接入的,在端上采用 VIP 方式请求 HTTPDNS 数据(VIP 即 Virtual IP,VIP 并<span style="color: black;">无</span>与某设备存在必定的绑定关系,会跟随主备切换之类的<span style="color: black;">状况</span><span style="color: black;">出现</span>而变换,VIP <span style="color: black;">供给</span>的服务是对应到某一台或若干台服务器的),既然请求原始数据<span style="color: black;">必须</span><span style="color: black;">运用</span> IP 直连的方式,<span style="color: black;">那样</span>就摆脱了运营商 localDNS 的解析限制,<span style="color: black;">这般</span>即使运营商<span style="color: black;">显现</span>了故障<span style="color: black;">或</span>被劫持,都不会影响百度 App 的可用性。</p>
任务调度策略 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">HTTPDNS 服务<span style="color: black;">供给</span>了两类 HTTP 接口,用于请求最优域名结果。<span style="color: black;">第1</span>种是多域名接口,针对<span style="color: black;">区别</span>的<span style="color: black;">制品</span>线,下发<span style="color: black;">制品</span>线配置的域名,第二种是单域名接口,只返回你要<span style="color: black;">查找</span>的那个域名结果,<span style="color: black;">这般</span>的设计和标准的 DNS <span style="color: black;">查找</span>基本是<span style="color: black;">同样</span>的,只<span style="color: black;">不外</span>是从 UDP 协议变<span style="color: black;">成为了</span> HTTP 协议。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">【1】多域名接口会在 App 冷<span style="color: black;">起步</span>和网络切换的时候请求一次,目的是在 App 的网络环境初始化<span style="color: black;">或</span>变化的时候预先获取域名结果,<span style="color: black;">这般</span><span style="color: black;">亦</span>会减少单域名接口的请求次数。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">【2】单域名接口会在本地 cache 过期后,由用户的操作触发网络请求,<span style="color: black;">从而</span>做一次单域名请求,用户这次操作的 DNS 结果会降级成 localDNS 的结果,但在<span style="color: black;">无</span>过期的<span style="color: black;">状况</span>下,下次会返回 HTTPDNS 的结果。</p>
IP <span style="color: black;">选择</span>策略 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">IP <span style="color: black;">选择</span>策略<span style="color: black;">处理</span>的核心问题是最优 IP 的<span style="color: black;">选择</span>,避免<span style="color: black;">由于</span>接入点的<span style="color: black;">选择</span>错误<span style="color: black;">导致</span>的跨运营商耗时。HTTPDNS 服务会将最优 IP <span style="color: black;">根据</span><span style="color: black;">次序</span>下发,客户端默认<span style="color: black;">选择</span><span style="color: black;">第1</span>个,<span style="color: black;">这儿</span><span style="color: black;">无</span>做客户端的连通性校验的<span style="color: black;">原由</span>,<span style="color: black;">重点</span>还是担心端上的性能问题,<span style="color: black;">不外</span>有容灾策略兜底,综合<span style="color: black;">评定</span>还是<span style="color: black;">能够</span>接受的。</p>
缓存策略 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">大众</span><span style="color: black;">针对</span> DNS 缓存并不陌生,它<span style="color: black;">重点</span>是为了<span style="color: black;">提高</span><span style="color: black;">拜访</span>效率,操作系统,网络库等都会做 DNS 缓存。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">DNS 缓存中一个<span style="color: black;">要紧</span>的概念<span style="color: black;">便是</span> TTL(Time-To-Live),在 localDNS 中针对<span style="color: black;">区别</span>的域名,TTL 的时间是不<span style="color: black;">同样</span>的,在 HTTPDNS 中这个值由服务端动态下发,百度 App <span style="color: black;">日前</span>所有的域名 TTL 的配置是 5 分钟,过期后<span style="color: black;">倘若</span><span style="color: black;">无</span>新的 IP 将继续沿用老的 IP,当然<span style="color: black;">亦</span><span style="color: black;">能够</span><span style="color: black;">选取</span>不沿用老的 IP,而降级成 localDNS 的 IP,<span style="color: black;">那样</span>这就取决于 localDNS <span style="color: black;">针对</span>过期 IP 的处理。</p>
命中率策略 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">倘若</span> HTTPDNS 的命中率是 100%,在<span style="color: black;">保准</span> HTTPDNS 服务稳定<span style="color: black;">有效</span>的前提下,<span style="color: black;">咱们</span>就<span style="color: black;">能够</span>做到防劫持,<span style="color: black;">提高</span><span style="color: black;">精细</span>调度的能力。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">【1】为了提升 HTTPDNS 的命中率,<span style="color: black;">咱们</span><span style="color: black;">选取</span><span style="color: black;">运用</span>多域名接口,在冷<span style="color: black;">起步</span>和网络切换的时候,批量拉取域名结果并缓存在本地,便于接下来的请求<span style="color: black;">运用</span>。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">【2】为了再一次<span style="color: black;">提高</span> HTTPDNS 的命中率,当用户操作触发网络请求,获取域名对应的 IP 时,会提前进行本地过期时间判断,时间是 60s,<span style="color: black;">倘若</span>过期,会发起单域名的请求并缓存起来,<span style="color: black;">这般</span>会<span style="color: black;">连续</span>延长域名结果的过期时间。本地过期时间与上面<span style="color: black;">说到</span>的 TTL 是客户端和服务端的双重过期时间,目的是在<span style="color: black;">反常</span><span style="color: black;">状况</span>下<span style="color: black;">能够</span>双重<span style="color: black;">保准</span>过期时间的准确性。</p>
<span style="color: black;">基本</span>能力层 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">基本</span>能力层<span style="color: black;">重点</span><span style="color: black;">供给</span>给 DNS 策略层所<span style="color: black;">必须</span>的<span style="color: black;">基本</span>能力,<span style="color: black;">包含</span> IPv4/IPv6 协议栈探测的能力,数据传输的能力,缓存实现的能力,下面将讲解每种能力的<span style="color: black;">详细</span>实现</p>
IPv4/IPv6 协议栈探测 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度 App 的 IPv6 改造正在如火如荼的进行中,端上在 HTTPDNS 的 IP <span style="color: black;">选择</span>上<span style="color: black;">怎样</span><span style="color: black;">晓得</span><span style="color: black;">日前</span>属于哪个协议栈<span style="color: black;">作为</span>关键性问题,并且这种判断<span style="color: black;">需求</span>性能极高,<span style="color: black;">由于</span> IP <span style="color: black;">选择</span>的频次实在是太高了。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">咱们</span><span style="color: black;">选择</span>的<span style="color: black;">方法</span>是 UDP Connect,<span style="color: black;">那样</span>何为 UDP Connect?<span style="color: black;">大众</span>都<span style="color: black;">晓得</span> TCP 是面向连接的,传输数据前客户端都要调用 connect <span style="color: black;">办法</span><span style="color: black;">经过</span>三次握手<span style="color: black;">创立</span>连接,UDP 是面向<span style="color: black;">没</span>连接的,<span style="color: black;">没</span>需<span style="color: black;">创立</span>连接便能收发数据,<span style="color: black;">然则</span><span style="color: black;">倘若</span><span style="color: black;">咱们</span>调用了 UDP 的 connect <span style="color: black;">办法</span>会<span style="color: black;">出现</span>什么呢?当<span style="color: black;">咱们</span>调用 UDP 的 connect <span style="color: black;">办法</span>时,系统会检测其端口<span style="color: black;">是不是</span>可用,<span style="color: black;">位置</span><span style="color: black;">是不是</span>正确,<span style="color: black;">而后</span>记录对端的 IP <span style="color: black;">位置</span>和端口号,返回给调用者,<span style="color: black;">因此</span> UDP Connect 不会像 TCP Connect 发起三次握手,<span style="color: black;">出现</span>网络真实损耗,UDP 客户端<span style="color: black;">仅有</span>调用 send <span style="color: black;">或</span> sendto <span style="color: black;">办法</span>后才会真正发起真实网络损耗。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/XIibZ0YbvibkVpjar0iatrJ7krDLgzvpJEG2HmdaY4o3ibxosU5CHic1viaia7HibozluNOqFxqKSmtoPcXpYI8jbsiaNgA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>
UDP Connect 原理 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">有了 UDP Connect 的<span style="color: black;">基本</span><span style="color: black;">保证</span>,<span style="color: black;">咱们</span>在上层做了缓存机制,用来减少系统调用的损耗,<span style="color: black;">机会</span>上<span style="color: black;">日前</span>仅在冷<span style="color: black;">起步</span>和网络切换会触发探测,在同一种网络制式下探测一次基本<span style="color: black;">能够</span><span style="color: black;">保证</span>当前网络是 IPv4 栈还是 IPv6 栈。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">日前</span>百度 App 客户端<span style="color: black;">针对</span> IPv4/IPv6 双栈的策略是保守的,仅在 IPv6-only 的<span style="color: black;">状况</span>下<span style="color: black;">运用</span> v6 的 IP,其余<span style="color: black;">运用</span>的都是 v4 的 IP,双栈下的<span style="color: black;">方法</span>后续<span style="color: black;">必须</span>优化,业内<span style="color: black;">日前</span>标准的做法是 happy eyeball 算法,什么叫 happy eyeball 呢?<span style="color: black;">便是</span>不会<span style="color: black;">由于</span> IPv4 或 IPv6 的故障问题,<span style="color: black;">引起</span>用户的眼球<span style="color: black;">始终</span>在等待加载<span style="color: black;">或</span>出错,这<span style="color: black;">便是</span> happy eyeball 名字的由来。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">happy eyeball 有 v1 版本 RFC6555 和 v2 版本 RFC8305,前者是 Cisco 提出来的,后者是苹果提出来的。happy eyeball <span style="color: black;">处理</span>的核心问题是,<span style="color: black;">繁杂</span>环境下 v4 和 v6 IP <span style="color: black;">选择</span>的问题,它是一套整体<span style="color: black;">处理</span><span style="color: black;">方法</span>,<span style="color: black;">针对</span>域名<span style="color: black;">查找</span>的处理,<span style="color: black;">位置</span>的排序,连接的尝试等方面均做出了规定,感兴趣的<span style="color: black;">朋友</span><span style="color: black;">能够</span>查看:</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">https://tools.ietf.org/html/rfc6555</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">https://tools.ietf.org/html/rfc8305</p>
2. 数据传输 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">数据传输<span style="color: black;">重点</span><span style="color: black;">供给</span>网络请求的能力和数据解析的能力。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">【1】网络请求失败重试的机制,获取 HTTPDNS 结果的成功率会大大影响 HTTPDNS 的命中率,<span style="color: black;">因此</span>客户端会有一个三次重试的机制,<span style="color: black;">保证</span>成功率。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">【2】数据解析<span style="color: black;">反常</span>的机制,<span style="color: black;">倘若</span>获取的 HTTPDNS 的结果存在<span style="color: black;">反常</span>,将不会覆盖端上的缓存。</p>
3. 缓存实现 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">缓存的实现基本<span style="color: black;">能够</span>分为磁盘缓存和内存缓存,<span style="color: black;">针对</span> HTTPDNS 的缓存场景,<span style="color: black;">咱们</span>是选其一还是都<span style="color: black;">选取</span>呢?百度 App <span style="color: black;">选取</span>的是内存缓存,目的是防止<span style="color: black;">咱们</span>自己的服务<span style="color: black;">显现</span>问题,运维<span style="color: black;">朋友</span>在紧急<span style="color: black;">状况</span>下切换流量,<span style="color: black;">倘若</span>做了磁盘缓存,会<span style="color: black;">引起</span>百度 App 在重启后<span style="color: black;">亦</span>可能不可用,但这种问题会<span style="color: black;">引起</span> APP 在冷<span style="color: black;">起步</span><span style="color: black;">时期</span>,HTTPDNS 结果未返回前,还是存在故障<span style="color: black;">或</span>劫持的<span style="color: black;">危害</span>,综合<span style="color: black;">评定</span>来看<span style="color: black;">能够</span>接受,<span style="color: black;">倘若</span><span style="color: black;">显现</span>这种极端<span style="color: black;">状况</span>,影响的是冷<span style="color: black;">起步</span><span style="color: black;">周期</span>的<span style="color: black;">有些</span>请求,但只要 HTTPDNS 结果返回后便会恢复正常。</p>
<span style="color: black;">3.HTTPDNS 的最佳实践</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度 App <span style="color: black;">日前</span>客户端网络架构<span style="color: black;">因为</span>历史<span style="color: black;">原由</span>还未统一,<span style="color: black;">不外</span><span style="color: black;">咱们</span>正朝着这个<span style="color: black;">目的</span><span style="color: black;">奋斗</span>,下面着重介绍下 HTTPDNS 在 Android 和 iOS 网络架构中的位置及实践。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">HTTPDNS 在 Android 网络架构的位置及实践百度 App 的 Android 网络流量都在 okhttp 之上,上层进行了网络门面的封装,封装内部的实现细节和对外友好的 API,供各个业务和<span style="color: black;">基本</span>模块<span style="color: black;">运用</span>,在 okhttp 上<span style="color: black;">咱们</span>扩展了 DNS 模块,<span style="color: black;">运用</span> HTTPDNS 替换了原有的系统 DNS。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/XIibZ0YbvibkVpjar0iatrJ7krDLgzvpJEGOqnD9Q7qP3hjcqibpQOuzugT3Fn08BJf1TvvTYJFS2jX44TGeDl9W4Q/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>
HTTPDNS 在 Android 网络架构的位置
HTTPDNS 在 iOS 网络架构的位置及实践 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度 App 的 iOS 网络流量都在 cronet(chromium 的 net 模块)之上,上层<span style="color: black;">咱们</span><span style="color: black;">运用</span> AOP 的方式将 cronet stack 注入进 URLSession 里,<span style="color: black;">这般</span><span style="color: black;">咱们</span>就<span style="color: black;">能够</span>直接<span style="color: black;">运用</span> URLSession 的 API 进行网络的操作<span style="color: black;">况且</span>更易于系统<span style="color: black;">守护</span>,在上层封装了网络门面,供各个业务和<span style="color: black;">基本</span>模块<span style="color: black;">运用</span>,在 cronet 内部<span style="color: black;">咱们</span>修改了 DNS 模块,除了原有的系统 DNS <span style="color: black;">规律</span>外,还添加了 HTTPDNS 的<span style="color: black;">规律</span>。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">iOS 上还有一部分流量是在原生 URLSession 上,<span style="color: black;">重点</span>是有些第三方业务<span style="color: black;">无</span><span style="color: black;">运用</span> cronet 但还想单独<span style="color: black;">运用</span> HTTPDNS 的能力,<span style="color: black;">因此</span>就有了下面的 HTTPDNS 封装层,<span style="color: black;">办法</span>是在上层直接将域名替换成 IP,域名<span style="color: black;">针对</span>底层<span style="color: black;">非常多</span>机制是至关<span style="color: black;">要紧</span>的,<span style="color: black;">例如</span> https 校验,cookie,重定向,SNI(Server Name Indication)等,<span style="color: black;">因此</span>将域名修改<span style="color: black;">成为了</span> IP 直连后,<span style="color: black;">咱们</span>又处理了以上三种<span style="color: black;">状况</span>,<span style="color: black;">保准</span>请求的可用性。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/XIibZ0YbvibkVpjar0iatrJ7krDLgzvpJEG3moTicZb67TTLApS3LVtfKlVYKurLrDicLwia1kv9mefsFd7p0dPj0RLg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>
HTTPDNS 在 iOS 网络架构的位置
<span style="color: black;">4. 收益</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">DNS 优化的收益<span style="color: black;">重点</span>有两点,一是防止 DNS 的劫持(在出问题时<span style="color: black;">显出</span>尤为<span style="color: black;">要紧</span>),降低网络时延(在调度不准确的<span style="color: black;">状况</span>下,会增大网络的时延,降低用户的体验),这两点收益<span style="color: black;">必须</span>结合业务<span style="color: black;">来讲</span>,以百度 App Feed 业务为例,<span style="color: black;">第1</span>点上<span style="color: black;">咱们</span>取得了比<span style="color: black;">很强</span>的效果,iOS 劫持率由 0.12% 降低到 0.0002%,Android 劫持率由 0.25% 降低到 0.05%,第二点的收益不<span style="color: black;">显著</span>,<span style="color: black;">原由</span>在于 Feed 业务<span style="color: black;">重点</span><span style="color: black;">目的</span>群体在国内,百度在国内节点布局相对丰富,服务整体质量<span style="color: black;">亦</span>较高,即使<span style="color: black;">显现</span>调度不准确的<span style="color: black;">状况</span>,差值<span style="color: black;">亦</span>不会太大,但<span style="color: black;">倘若</span>在国外<span style="color: black;">状况</span>可能会差<span style="color: black;">非常多</span>。</p>
<span style="color: black;"><span style="color: black;">2、</span>连接优化</span>
<span style="color: black;">1. 背景</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">连接优化<span style="color: black;">必须</span><span style="color: black;">处理</span>两个核心问题:</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">连接<span style="color: black;">创立</span>耗时较长,<span style="color: black;">引起</span>请求的总时长变长,<span style="color: black;">从而</span>影响用户体验。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">在多变的网络环境下,连接<span style="color: black;">创立</span>的过程可能会失败,<span style="color: black;">引起</span>成功率下降,<span style="color: black;">从而</span>影响用户体验。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度 App 承载着亿级流量,<span style="color: black;">针对</span>每一个请求都<span style="color: black;">必须</span>追求耗时短,成功率高的体验。从协议<span style="color: black;">方向</span>出发,<span style="color: black;">怎样</span><span style="color: black;">才可</span>做到这一点呢?<span style="color: black;">首要</span><span style="color: black;">咱们</span>来看下<span style="color: black;">创立</span>连接耗时的原理。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="https://mmbiz.qpic.cn/mmbiz_png/XIibZ0YbvibkVpjar0iatrJ7krDLgzvpJEGSbte0qZRalHCBd4l30OYoxiaChGfiaq9d68Khbqr2Q7VDWcDnW2crzmg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1" style="width: 50%; margin-bottom: 20px;"></p>
<span style="color: black;">创立</span>连接耗时的原理 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">从上图<span style="color: black;">咱们</span>能清晰的看出:</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">1.DNS Query <span style="color: black;">必须</span> 1 个 RTT(Round-Trip Time,即往返时间),百度 App 都是基于 HTTPDNS 服务的,<span style="color: black;">因此</span>大部分会命中缓存,<span style="color: black;">倘若</span>降级走了系统 DNS,<span style="color: black;">亦</span>会命中缓存,命中不了的<span style="color: black;">因为</span>是基于 UDP 协议,<span style="color: black;">因此</span>在连接耗时上<span style="color: black;">无</span>太大的影响,线上的数据<span style="color: black;">亦</span>能说明这点。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">2.TCP 要经历 SYN,SYN/ACK,ACK 三次握手的 1.5 个 RTT,<span style="color: black;">不外</span> ACK 和 ClientHello 合并了,<span style="color: black;">因此</span><span style="color: black;">便是</span> 1 个 RTT。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">3.TLS(Transport Layer Security,即传输层安全性协议)<span style="color: black;">必须</span>经过握手和密钥交换 2 个 RTT。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">综上所述,DNS,TLS,TCP 握手<span style="color: black;">周期</span>用了 4 个 RTT 才到了 ApplicationData <span style="color: black;">周期</span>,<span style="color: black;">亦</span><span style="color: black;">便是</span>数据<span style="color: black;">起始</span>传输<span style="color: black;">周期</span>。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">经过</span>上面的分析<span style="color: black;">能够</span>总结出,<span style="color: black;">倘若</span><span style="color: black;">咱们</span>能<span style="color: black;">尽可能</span>的将 TLS 和 TCP 的 RTT 减少,将会大大降低连接耗时的时间。</p>
<span style="color: black;">2. 连接优化<span style="color: black;">咱们</span>都能做什么?</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度 App 的优化<span style="color: black;">目的</span>分为两类,一类是 TLS 的连接优化,一类是 TCP 的连接优化。</p>
TLS 的连接优化 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">TLS 的连接优化,<span style="color: black;">必须</span>服务端和客户端都<span style="color: black;">必须</span>支持,<span style="color: black;">一起</span>完成优化手段,<span style="color: black;">包含</span> Session Resumption 和 False Start。</p>
Session Resumption <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">Session Resumption 中文意思是会话复用,下图讲解了 Session Resumption 的协议原理。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
Session Resumption 的协议原理 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">经过</span>上图<span style="color: black;">能够</span>看出 TLS 密钥协商交换的过程<span style="color: black;">无</span>了,但<span style="color: black;">详细</span>是<span style="color: black;">怎样</span>实现的呢?<span style="color: black;">包括</span>两种方式,一种是 Sesssion Identifier,一种是 Session Ticket。</p>
1)Session Identifier <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">Session Identifier 中文为会话标识符,更像<span style="color: black;">咱们</span>熟知的 session 的概念。是 TLS 握手中生成的 Session ID。服务端会将 Session ID <span style="color: black;">保留</span>起来,客户端<span style="color: black;">亦</span>会存储 Session ID,在后续的 ClientHello 中带上它,服务端<span style="color: black;">倘若</span>能找到匹配的信息,就<span style="color: black;">能够</span>完成一次快速握手。</p>
2)Session Ticket <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">Session Identifier 存在<span style="color: black;">有些</span><span style="color: black;">坏处</span>端,<span style="color: black;">例如</span>客户端多次请求<span style="color: black;">倘若</span><span style="color: black;">无</span>落在同一台<span style="color: black;">设备</span>上就<span style="color: black;">没</span>法找到匹配的信息,但 Session Ticket <span style="color: black;">能够</span>。Session Ticket 更像<span style="color: black;">咱们</span>熟知的 cookie 的概念,Session Ticket 用<span style="color: black;">仅有</span>服务端<span style="color: black;">晓得</span>的安全密钥加密过的会话信息,<span style="color: black;">保留</span>在客户端上。客户端在 ClientHello 时带上了 Session Ticket,服务器<span style="color: black;">倘若</span>能成功解密就<span style="color: black;">能够</span>完成快速握手。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">不管是 Session Identifier 还是 Session Ticket 都存在时效性问题,不是永久生效,<span style="color: black;">针对</span>这两种方式<span style="color: black;">大众</span><span style="color: black;">能够</span>查看:</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">https://tools.ietf.org/html/rfc5077</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度 App 的网络协议层对这两种方式都是支持的,省去了 TLS 握手过程中证书下载,密钥协商交换的环节,节省了 1 个 RTT 的时间。</p>
False Start <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">False Start 的中文意思是抢跑,下图讲解了 False Start 的协议原理。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
False Start 的协议原理 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">上图很清晰的说明在 TLS <span style="color: black;">第1</span>步握手成功后,客户端在发送 Change Cipher Spec Finished 的<span style="color: black;">同期</span><span style="color: black;">起始</span>数据传输,服务端在 TLS 握手完成时直接返<span style="color: black;">回复</span>用数据。应用数据的发送<span style="color: black;">实质</span>上并未等到握手<span style="color: black;">所有</span>完成,<span style="color: black;">因此</span><span style="color: black;">叫作</span>之为抢跑。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">从结果看省去了 1 个 RTT 的时间。False Start 有两个前提<span style="color: black;">要求</span>,一是要<span style="color: black;">经过</span>应用层协议协商 ALPN(Application Layer Protocol Negotiation)握手,二是要支持前向安全的加密算法。False Start 在未完成握手的<span style="color: black;">状况</span>下就发送了数据,前向安全<span style="color: black;">能够</span><span style="color: black;">加强</span>安全性,<span style="color: black;">详细</span>协议实现,<span style="color: black;">大众</span><span style="color: black;">能够</span>查看:</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">https://tools.ietf.org/html/rfc7918</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度 App 的网络协议层对 False Start 是支持的。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这儿</span>说句题外话,其实 TCP 层有个类似的连接优化手段叫 Fast Open,感兴趣的<span style="color: black;">朋友</span>,<span style="color: black;">能够</span>查看:</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">https://tools.ietf.org/html/rfc7413</p>Session Resumption 和 False Start 的区别<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">两者<span style="color: black;">针对</span> TLS <span style="color: black;">来讲</span>都是节省一个 RTT,Session Resumption 在<span style="color: black;">第1</span>次握手时还是<span style="color: black;">必须</span> 2 个 RTT,在第二次握手时<span style="color: black;">才可</span>复用减少到 1 个 RTT。False Start 是端上的<span style="color: black;">行径</span>,故每次都会减少到 1 个 RTT。</p>
TCP 的连接优化 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">TCP 的连接优化,<span style="color: black;">咱们</span>先从连接池说起,<span style="color: black;">首要</span>让<span style="color: black;">咱们</span>来认识下连接池都有<span style="color: black;">那些</span>类型。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">1. 连接池</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
连接池的类型 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">上图展示了连接池的<span style="color: black;">区别</span>类型,都是<span style="color: black;">大众</span>耳熟能详的协议连接池,有低级连接池,<span style="color: black;">包括</span> TCP 连接池(管理 HTTP 请求的连接)和 WebSocket 连接池(管理 WebSocket 连接)。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">有高级连接池,<span style="color: black;">包含</span> HTTP 代理连接池(管理 HTTP 代理请求的连接),SpdySession 连接池(管理 SPDY 和 HTTP/2 请求的连接),SOCKS 连接池(管理 SOCKS 和 SOCKS5 代理的连接),SSL 连接池(管理 HTTPS 请求的连接)。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">区别</span>类型的连接池以组合的形式互相复用能力。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">1)SSL 连接池管理的是 SSLSocket,但 SSLSocket 又依赖于 TCP 连接池<span style="color: black;">供给</span>的 TCPSocket。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">2)HTTP 代理连接池<span style="color: black;">倘若</span>走 HTTP 协议,<span style="color: black;">那样</span>就<span style="color: black;">必须</span> TCP 连接池<span style="color: black;">供给</span> TCPSocket,<span style="color: black;">倘若</span>走 HTTPS 协议,<span style="color: black;">那样</span>就<span style="color: black;">必须</span> SSL 连接池<span style="color: black;">供给</span> SSLSocket。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">3)SpdySession 连接池依赖 SSL 连接池<span style="color: black;">供给</span> SSLSocket,<span style="color: black;">这儿</span><span style="color: black;">必须</span>说明下,虽然 HTTP/2 协议<span style="color: black;">无</span>强制绑定 HTTPS,<span style="color: black;">然则</span>在<span style="color: black;">实质</span><span style="color: black;">研发</span>中确实都是绑定 HTTPS,百度 App <span style="color: black;">运用</span> ALPN 来协商 HTTP/2。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">4)SOCKS 连接池管理的 SOCKSSocket 和 SOCKS5Socket 都<span style="color: black;">必须</span>依赖 TCP 连接池<span style="color: black;">供给</span>的 TCPSocket,虽然 SOCKS5 支持 UDP,但 cronet 网络库暂时<span style="color: black;">无</span>实现。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">5)WebSocket 连接池依赖 TCP 连接池<span style="color: black;">供给</span>的 TCPSocket,声明下<span style="color: black;">这儿</span><span style="color: black;">无</span>说明 WSS(Web Socket Secure)的<span style="color: black;">状况</span>。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">TCP 连接优化是一个比较<span style="color: black;">繁杂</span>的内容,百度 App 做了针对性场景优化,<span style="color: black;">包含</span>预连接,连接重建,备用连接,复合连接。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">2. 预连接</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
预连接和连接重建 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">预连接,预先创建好的连接。它<span style="color: black;">处理</span>的场景是在 App <span style="color: black;">运用</span><span style="color: black;">周期</span><span style="color: black;">能够</span><span style="color: black;">没</span>耗时的获取连接。下面用四个问答来解释预连接。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">问题一:预连接<span style="color: black;">是不是</span>能<span style="color: black;">处理</span>所有网络请求的提前连接建立?</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答:答案<span style="color: black;">是不是</span>定的,预连接<span style="color: black;">必须</span>业务方进行核心业务的<span style="color: black;">评定</span>,针对核心的域名进行预连接的<span style="color: black;">创立</span>。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">问题二:预连接既然针对的是特定的域名,<span style="color: black;">那样</span>是<span style="color: black;">怎样</span>配置的呢?</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答:采用域名 + 连接数的方式进行配置,<span style="color: black;">例如</span> https://a.baidu.com|2,<span style="color: black;">暗示</span>给 a.baidu.com 这个域名配置两条预连接,<span style="color: black;">这儿</span>要说明下,在 HTTP/1.x 协议下,网络库的实现都会<span style="color: black;">针对</span>单域名有最大连接数的限制,<span style="color: black;">区别</span>网络库的个数限制不<span style="color: black;">同样</span>,有 5 个<span style="color: black;">亦</span>有 6 个,但<span style="color: black;">针对</span> HTTP/2 协议,这个连接数就只能是 1 个。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">问题三:预连接是<span style="color: black;">怎样</span><span style="color: black;">创立</span>的?</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答:在网络库初始化的时候,会<span style="color: black;">按照</span><span style="color: black;">运用</span>者的配置延迟 5s 进行预连接的<span style="color: black;">创立</span>,<span style="color: black;">重点</span>是<span style="color: black;">思虑</span>网络库在冷<span style="color: black;">起步</span>下<span style="color: black;">针对</span><span style="color: black;">起步</span>性能的影响,为了<span style="color: black;">保准</span>网络库的整体性能,预连接的总个数限制在 20 个。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">问题四:预连接是<span style="color: black;">怎样</span>保持的?</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答:在网络库初始化的时候,除了进行预连接的<span style="color: black;">创立</span>,还会创建一个预连接的<span style="color: black;">按时</span>器,这个<span style="color: black;">按时</span>器会每隔 31s,这个值的设定取决于 BFE(Baidu Front End,是七层流量的统一接入系统)和 BGW(Baidu Gate Way,百度自主<span style="color: black;">开发</span>的四层负载<span style="color: black;">平衡</span>平台)对超时的最小值设定,<span style="color: black;">按照</span><span style="color: black;">运用</span>者的配置重新<span style="color: black;">创立</span>连接。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">3. 连接重建</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">连接重建,将连接重新<span style="color: black;">创立</span>。它<span style="color: black;">处理</span>的场景是 App 网络状态<span style="color: black;">出现</span>变化,IP <span style="color: black;">位置</span>变化,<span style="color: black;">引起</span>连接不可用。下面用三个问答来解释连接重建。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">问题一:连接重建是否针对连接池里的所有连接?</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答:答案是肯定的。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">问题二:连接重建的过程是什么样的?</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答:在网络状态变化的时候,<span style="color: black;">第1</span>步会清除掉连接池里的 idle socket,何为 idle socket?即空闲 socket,<span style="color: black;">针对</span>从未<span style="color: black;">运用</span>过的空闲 socket 超过 60 秒清除,<span style="color: black;">针对</span><span style="color: black;">运用</span>过的空闲 socket 超过 90 秒清除。第二步重建连接<span style="color: black;">必须</span>等待 200ms,目的是等待 DNS 先重建完成。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">问题三:连接重建<span style="color: black;">针对</span>性能有影响吗?</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答:出于性能<span style="color: black;">思虑</span>,连接重建的连接个数限制是 100 个。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">4. 备用连接</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
备用连接和复合连接 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">备用连接,预备的连接。它<span style="color: black;">处理</span>的场景是正常发送一个请求当 group 内<span style="color: black;">没</span>连接可用的时候(何为 group?group 是管理 socket 的最小单元,内部<span style="color: black;">包括</span>活跃 socket,空闲 socket,连接任务,等待请求)。下面用三个问答来解释备用连接。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">问题一:备用连接<span style="color: black;">是不是</span>针对所有请求?</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答:答案是肯定的。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">问题二:备用连接的过程是什么样的?</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答:当有请求来临时,连接池内<span style="color: black;">没</span>连接可用,会<span style="color: black;">起步</span>一个<span style="color: black;">按时</span>器开启备用连接,<span style="color: black;">按时</span>器的间隔时间是 250ms,与主连接进行竞争,<span style="color: black;">倘若</span>主连接<span style="color: black;">由于</span>网络抖动<span style="color: black;">或</span>网络状态<span style="color: black;">欠好</span>,<span style="color: black;">引起</span>连接失败,<span style="color: black;">那样</span>备用连接就直接发送请求。<span style="color: black;">倘若</span>主连接成功,<span style="color: black;">那样</span>备用连接就被取消掉。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">问题三:备用连接的目的是什么?</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答:在连接池<span style="color: black;">没</span>连接的<span style="color: black;">状况</span>下,务必是要创建连接的,在主连接之外加一个备用连接,会大大<span style="color: black;">提高</span>创建连接的成功率,从而<span style="color: black;">提高</span>用户体验。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">5. 复合连接</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">复合连接,即多条连接。它<span style="color: black;">处理</span>的场景是为了多个 IP <span style="color: black;">位置</span>的连接<span style="color: black;">选择</span>问题。下面用三个问答来解释复合连接。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">问题一:复合连接<span style="color: black;">是不是</span>针对所有请求?</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答:答案是肯定的。复合连接<span style="color: black;">能够</span>全局开关,百度 App 现<span style="color: black;">周期</span>暂时<span style="color: black;">无</span>开启复合连接。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">问题二:复合连接的过程是什么样的?</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答:众所周知域名 DNS <span style="color: black;">查找</span><span style="color: black;">通常</span><span style="color: black;">状况</span>下会返回多个 IP,<span style="color: black;">咱们</span>以域名<span style="color: black;">查找</span>返回两个 IP 为例</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">1)<span style="color: black;">倘若</span>结果中存在 IPv6 的<span style="color: black;">位置</span>,<span style="color: black;">那样</span>会优先<span style="color: black;">选择</span> IPv6 的<span style="color: black;">位置</span>,这个规则 follow HappyEyeBall 机制(可参考系列一<span style="color: black;">针对</span> HappyEyeBall 的介绍)。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">2) 接下来这两个 IP 会<span style="color: black;">根据</span><span style="color: black;">次序</span>尝试<span style="color: black;">创立</span>连接,<span style="color: black;">倘若</span><span style="color: black;">第1</span>个 IP 返回失败,将立即<span style="color: black;">起始</span>连接第二个 IP。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">3)<span style="color: black;">倘若</span><span style="color: black;">第1</span>个 IP 率先成功返回,<span style="color: black;">那样</span>第二个 IP 将被加入连接尝试列表并停止所有尝试连接。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">4)<span style="color: black;">倘若</span><span style="color: black;">第1</span>个 IP 失败,会立刻<span style="color: black;">起始</span>第二个 IP 的连接。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">5)<span style="color: black;">倘若</span><span style="color: black;">第1</span>个 IP <span style="color: black;">处在</span> pending 状态,<span style="color: black;">那样</span>会<span style="color: black;">起步</span>一个<span style="color: black;">按时</span>器,默认延迟 2s 会发起第二个 IP 的连接,<span style="color: black;">倘若</span>是多个 IP 将会递归连接,<span style="color: black;">必须</span><span style="color: black;">尤其</span>说明下,<span style="color: black;">区别</span>的网络制式延迟时间会不<span style="color: black;">同样</span>,<span style="color: black;">这般</span>体验<span style="color: black;">亦</span>会更好。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">问题三:复合连接的目的是什么?</strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">答:复合连接的好处是<span style="color: black;">供给</span>最优的 IP <span style="color: black;">选择</span>机制,但<span style="color: black;">亦</span>会带来服务端的高负载,<span style="color: black;">因此</span><span style="color: black;">运用</span>的时候<span style="color: black;">必须</span>进行综合<span style="color: black;">评定</span>。</p>
<span style="color: black;">3. 连接优化的最佳实践</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度 App <span style="color: black;">日前</span>客户端网络架构<span style="color: black;">因为</span>历史<span style="color: black;">原由</span>还未统一,<span style="color: black;">不外</span><span style="color: black;">咱们</span>正朝着这个<span style="color: black;">目的</span><span style="color: black;">奋斗</span>。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">咱们</span>的中心思想是以系统网络库的 API 调用接口为中心,上层<span style="color: black;">创立</span>网络门面,供<span style="color: black;">外边</span><span style="color: black;">方便</span>调用,底层<span style="color: black;">经过</span>系统机制以 AOP 的方式将 cronet(chromium 的 net 模块)注入进系统网路库,达到双端网络架构统一,能力复用。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">下面着重介绍下连接优化在 Android 和 iOS 网络架构中的位置及实践。</p>
1. 连接优化在 Android 网络架构的位置及实践<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
连接优化在 Android 网络架构的位置 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度 App 的 Android 网络流量<span style="color: black;">日前</span>都在 okhttp 之上,上层进行了网络门面的封装,封装内部的实现细节和对外友好的 API,<span style="color: black;">日前</span><span style="color: black;">咱们</span>正在进行重构,默认采用 Android 标准的网络接口 HttpURLConnection,它的底层由系统<span style="color: black;">供给</span>的 okhttp 的实现。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">订制方面利用 URL Stream Protocol 机制将 HttpURLConnection 底层网络协议栈接管为 cronet,供各个业务和<span style="color: black;">基本</span>模块<span style="color: black;">运用</span>,连接优化的所有内容在 cronet 网络库内部实现。</p>
2. 连接优化在 iOS 网络架构的位置及实践 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
连接优化在 iOS 网络架构的位置 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">百度 App 的 iOS 网络流量<span style="color: black;">日前</span>都在 cronet 之上,上层<span style="color: black;">咱们</span><span style="color: black;">运用</span> iOS 的 URL Loading System 机制将 cronet stack 注入进 URLSession 里,<span style="color: black;">这般</span><span style="color: black;">咱们</span>就<span style="color: black;">能够</span>直接<span style="color: black;">运用</span> URLSession 的 API 进行网络的操作<span style="color: black;">况且</span>更易于系统<span style="color: black;">守护</span>,在上层封装了网络门面,供各个业务和<span style="color: black;">基本</span>模块<span style="color: black;">运用</span>。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">在 cronet 内部实现了预连接(<span style="color: black;">重点</span>针对百度 App 的几个核心域名进行预连和保活),连接重建(针对所有请求),备用连接(针对所有请求),复合连接(iOS 上暂时<span style="color: black;">无</span>开启),Session Resumption(针对所有请求),False Start(针对所有请求)。</p>
<span style="color: black;">4. 收益</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">连接优化的收益<span style="color: black;">重点</span><span style="color: black;">表现</span>在网络时延和网络成功率上,这两点收益<span style="color: black;">必须</span>结合业务<span style="color: black;">来讲</span>,以百度 App Feed 刷新这个典型业务场景为例。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">Feed 刷新文本请求网络时延降低 16%,Feed 刷新<span style="color: black;">照片</span>请求网络时延降低 12%,可谓收益相当<span style="color: black;">显著</span>。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">成功率方面,Feed 刷新文本请求成功率<span style="color: black;">提高</span> 0.29%,Feed 刷新<span style="color: black;">照片</span>请求成功率<span style="color: black;">提高</span> 0.23%,<span style="color: black;">亦</span>是非常不错的收益。</p>
<span style="color: black;"><span style="color: black;">3、</span>结语</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">DNS 优化和连接优化是个<span style="color: black;">连续</span>性的<span style="color: black;">专题</span>,<span style="color: black;">无</span>最优<span style="color: black;">仅有</span>更优。上面介绍的百度 App 的<span style="color: black;">有些</span>经验和做法并不见得完美,但<span style="color: black;">咱们</span>会继续深入的优化下去,<span style="color: black;">连续</span><span style="color: black;">提高</span>百度 App 的网络性能。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">以上优化由百度 App 团队,内核团队,OP 团队共建完成。最后感谢<span style="color: black;">大众</span>的辛苦阅读,<span style="color: black;">期盼</span>对你有所<span style="color: black;">帮忙</span>。</p>
参考资料 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">https://tools.ietf.org/html/rfc7858</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">https://tools.ietf.org/html/rfc6555</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">https://tools.ietf.org/html/rfc8305</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">https://tools.ietf.org/html/rfc7918</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">https://tools.ietf.org/html/rfc5077</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">https://tools.ietf.org/html/rfc7413</p>
作者简介 <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">蔡锐,9 年移动客户端<span style="color: black;">研发</span>经验,在百度先后主导过订制 ROM <span style="color: black;">行业</span>、多屏互动<span style="color: black;">行业</span>、Hybrid 跨平台<span style="color: black;">行业</span>等多个技术<span style="color: black;">行业</span>的<span style="color: black;">研发</span>,<span style="color: black;">日前</span>担任百度 App 的客户端资深工程师,参与<span style="color: black;">基本</span>技术的<span style="color: black;">科研</span>,专攻动态化和网络优化方向。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"> 活动<span style="color: black;">举荐</span></span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">GMTC 全球大前端技术大会上,<span style="color: black;">咱们</span>邀请到了来自 Google、BAT、美团、京东、滴滴、字节跳动等 60+ 一线技术专家与你共话前端<span style="color: black;">哪些</span>事,涵盖小程序、Flutter、前端安全、工程化、性能优化等 20+ 热点技术,不可<span style="color: black;">错失</span>。欢迎点击“<span style="color: black;">阅读原文</span>”<span style="color: black;">认识</span>详情。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><img src="data:image/svg+xml,%3C%3Fxml version=1.0 encoding=UTF-8%3F%3E%3Csvg width=1px height=1px viewBox=0 0 1 1 version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink%3E%3Ctitle%3E%3C/title%3E%3Cg stroke=none stroke-width=1 fill=none fill-rule=evenodd fill-opacity=0%3E%3Cg transform=translate(-249.000000, -126.000000) fill=%23FFFFFF%3E%3Crect x=249 y=126 width=1 height=1%3E%3C/rect%3E%3C/g%3E%3C/g%3E%3C/svg%3E" style="width: 50%; margin-bottom: 20px;"></p>
“NB”(牛×的缩写,表示叹为观止) “BS”(鄙视的缩写) seo常来的论坛,希望我的网站快点收录。
页:
[1]