龙空技术网

程序员应该掌握的基于UDP的传输协议优化、DNS协议优化!

程序员高级码农II 102

前言:

今天兄弟们对“nginxiisip”大约比较着重,小伙伴们都需要剖析一些“nginxiisip”的相关文章。那么小编同时在网上网罗了一些关于“nginxiisip””的相关文章,希望兄弟们能喜欢,同学们快快来了解一下吧!

基于UDP的传输协议优化

在当前的互联网环境中,有大量的NAT网络设备,为了兼容这些NAT网络设备,新的协议必须使用UDP协议来作为承载层。

这是为什么呢?

首先,NAT网络设备是用来完成网络地址转换的,所以NAT网络设备必须能识别和理解对应的协议,否则就无法建立连接和进行通信。

其次,目前无法在IP层定义新协议来解决NAT网络设备的兼容问题,但是UDP和IP层本质上是一致的,均用于提供包发送服务,所以开发者就在UDP报文上来解决这个兼容性问题。NAT网络设备均支持UDP协议的地址转换,因此,新的传输协议均是在UDP协议基础上进一步封装开发的。

所以,对UDP协议的优化对于后面的网络协议开发及使用十分重要,下面通过6.4.1节和6.4.2节进行介绍。6.4.1节会对目前的几种UDP实现的可靠传输协议进行简单介绍。6.4.2节重点介绍优化弱网环境下网络传输的QUIC协议,分别针对其在HTTP2.0上的改进、在负载均衡中的使用及其应用进行详细介绍。

6.4.1 基于UDP的传输协议简介

由于TCP协议在NAT穿透方面的不足,出现了许多基于UDP协议的可靠传输协议。实现UDP协议可靠传输主要是依赖于ARQ(Automatic Repeat reQuest,自 动 重 传 ) 的 确 认 和 重 传 机 制 , 个 别 协 议 会 采 用 FEC ( Forward ErrorCorrection,前向纠错)机制。下面简要介绍6种基于UDP协议实现的传输协议。

1.KCP

KCP是一个快速可靠协议,能通过比TCP多用10%~20%带宽使平均延迟降低30%~40%,且使最大延迟降至TCP最大延迟的三分之一。该协议是利用纯算法实现的,并不负责底层协议(如UDP)的收发,需要使用者自己定义下层数据包的发送方式,以回调函数的方式提供给KCP协议。连时钟都需要从外部传递进来,内部不会有任何一次系统调用。KCP协议的可靠传输是通过ARQ重传机制来实现的,其重传是选择性重传,只重传真正丢失的数据包,与TCP协议从第一个丢包后全部重传相比,KCP协议的性能会好很多。更多KCP协议的设计细节请参考官方资料(见链接[18]),这里不再赘述。

2.uTP

uTP(Micro Transport Protocol,微型传输协议)是一种基于UDP、开放的BT(BitTorrent,比特洪流)点对点文件共享协议,该协议的目的是减轻延迟,同时解决传统的基于TCP协议的BT所遇到的拥塞控制问题,提供可靠的、有序的数据传送。

当BT传输干扰到其他应用时,uTP协议的设计能够自动减少数据包的传送速度。这使得该协议可以支持BT应用和网络浏览器共享ADSL(AsymmetricDigital Subscriber Line,非对称数字用户线路),而不影响正常浏览。

3.FASP

Aspera的FASP(Fast and Secure Protocol,安全快速传输协议)是基于UDP协议实现的,解决了TCP协议传输的延迟问题,能够提供高达5 Gbit/s的带宽。FASP协议主要用于文件高速传输,该协议不需要确保报文顺序,避免了乱序重组时内存开销过大,同时避免了因内存限制而丢弃部分乱序报文,进而减少不必要的重传,提高传输速度。

4.SCTP

SCTP(Stream Control Transmission Protocol,流控制传输协议)并不是一种基于UDP协议的可靠传输协议,SCTP是一种和TCP协议、UDP协议同级的传输协议,该协议集合了TCP和UDP两种协议的优点,能够提供和TCP协议一样可靠有序的数据传输功能,并实现类似UDP协议的面向消息的操作。该协议的主要特征有多宿主、多流、初始化保护、消息分帧、可配置的无序发送及平滑关闭。

5.UDT

UDT(UDP-based Data Transfer Protocol,基于UDP的数据传输协议)是基于UDP协议的数据传输协议。UDT协议主要是为了解决“TCP协议在高带宽、长距离网络上性能很差”的问题,希望全面支持高速广域网上的海量数据传输。该协议是面向连接的双向的应用层协议,引入了新的拥塞控制和数据可靠性控制机制。它同时支持可靠的数据流传输和部分可靠的数据报传输。因为UDT协议是完全基于UDP协议实现的,因此它也可以被应用到点对点技术(Peer-to-Perr,P2P)、防火墙穿透等领域。

6.QUICQUIC(Quick UDP Internet Connection,快速UDP互联网连接协议)是Google实现的一种可靠UDP传输协议,其主要目标是减小网络传输延迟。它的主要特点如下。

● 内建安全性:QUIC协议集成了TLS协议,在建立连接时和TLS协议协商合并,减少了往返请求次数,提高了连接速度。

● 避免前序包阻塞:对TCP协议来说,即便与其他数据包无关的前序包阻塞也会使传输流程受阻。而UDP协议对数据包顺序没限制,所以基于UDP协议实现的QUIC协议能够避免前序包阻塞的问题。

● 改进的拥塞控制:QUIC协议默认使用TCP协议的Cubic拥塞控制算法,但在此基础上做了很大的改进,主要的改进有支持可插拔、采用数据包序号递增来避免重传歧义,以及通过ACK包携带延时来精确计算RTT。

● 连接迁移:QUIC协议通过连接ID来唯一标识客户端和服务器的逻辑连接,这个设计可以在用户切换网络使得IP地址或端口改变时仍保持连接,无须重连。

6.4.2 QUIC协议优化

QUIC是一种基于UDP协议实现的多路复用、安全传输的应用层协议。QUIC协议最早由Google提出。为了扩大影响力及应用范围,Google于2016年向IETF组织提交了审议申请,试图将QUIC协议作为下一代HTTP协议(即HTTP2.0 overQUIC,该名称为IETF内部早期对HTTP3.0的称呼)进行标准化规范推广。

Google提交的HTTP2.0 over QUIC的原始实现包含了传输层和应用层的功能。经过IETF组委会的多次优化修订,负责连接及数据处理的部分被剥离出来,并作为单一功能的传输层协议发布,从而使得QUIC协议可以在其他应用层上使用。

1.QUIC协议在HTTP2.0上的改进

在6.3节中,我们已经了解到HTTP2.0规范,该协议规范针对HTTP1.1的许多性能问题进行了相应的改进。但是,本质上HTTP2.0还是基于TCP协议的,依然有一些问题无法解决。比如,在HTTP2.0连接网络中有一个数据包丢失,或者任何一方的网络中断,整个TCP连接就会停止。因为TCP协议是一个按序传输的链条,所以如果其中一个点丢失了,那么链路之后的内容都需要等待。这种单 数 据 包 造 成 的 阻 塞 , 就 是 TCP 协 议 上 的 队 头 阻 塞 ( head of lineblocking)。随着丢包率的增加(弱网条件),HTTP2.0的表现会越来越差。

目前,在基于TCP协议规范上解决这个问题十分困难,所以就衍生出了新的基

于UDP的协议规范——QUIC协议。

QUIC协议结合HTTP2.0、TLS及TCP协议的设计经验,在其传输方面进行了多路复用,以及流量控制的传输优化;在安全通信方面,为通信双方提供等效于TLS协议的安全机制;在可靠性方面,提供类似TCP协议的包重传、拥塞机制等特性来保证传输的可靠性。

下面对于QUIC协议的实现特点进行进一步讨论。

(1)QUIC协议的数据流。

类似于SCTP、HTTP2.0,QUIC协议可以在同一物理连接上有多个独立的逻辑数据流。这些数据流在同一个连接上并行传输,互不影响。相比于HTTP2.0在HTTP层面上处理数据流,QUIC协议可以在传输层协议(包括TCP协议或UDP协议)上实现数据流处理。连接在两端点之间经过类似TCP连接的方式协商建立。QUIC连接基于UDP端口和IP地址建立,而一旦建立,连接就可以通过其“连接ID”(Connection ID)进行关联。在已建立的连接上,双方均可通过此连接进行数据传输。同时,QUIC协议可以对连接和数据流分别进行流量控制。

(2)全TLS加密传输。

TCP协议头部没有经过任何加密和认证处理,所以很容易在传输过程中被网络设备篡改、注入和窃听。而QUIC协议是全TLS加密传输(QUIC使用的加密传输协议为TLS 1.3),没有非加密版本,相比于HTTP2.0(TLS仅标记为可选)或HTTPS协议更加安全可靠。其中,使用TLS 1.3的最主要原因是其握手所花费的往返次数更低,从而能降低协议的延迟。

(3)RTT快速会话恢复。

QUIC协议在和客户端进行第一次连接时,QUIC协议仅需要1RTT即可建立安全可靠的连接,相比于基于TCP的协议规范(如HTTP2.0与HTTPS协议,它们需要1~3RTT才可以建立安全可靠的连接)要更加快捷。然后,客户端可以在本地缓存添加加密认证信息,以便再次与服务器端建立连接时可以实现0RTT协议的连接建立延迟。与HTTP2.0相比,这种使用0RTT协议建立连接的特点可以说是QUIC协议最大的性能优势,这个特点也使得QUIC协议在弱网环境下有出色的表现。

(4)没有队头阻塞的多路复用。

多路复用是HTTP2.0的最大特性。多路复用可以使多条请求在一个TCP连接中同时发送出去,但也使得队头阻塞的问题变得更加严重。QUIC协议沿用了多路复用这一特点,在一条QUIC连接上并行发送多个HTTP请求(将并行的HTTP请求流称为stream)。但是,因为QUIC协议在一个连接上的多个stream之间没有依赖,所以即使某个stream丢失了一个UDP数据包,也只会影响本stream的处理,不会影响其他stream的处理。这种特性在很大程度上缓解甚至消除了队头阻塞的问题。

在互联网上有很多关于QUIC协议的具体技术细节分析,本节不再做过多分析。下面就对QUIC协议在负载均衡系统应用过程中遇到的问题及解决思路进行讨论。

2.QUIC协议在负载均衡器中的使用

在QUIC协议出现以前,HTTP使用的底层协议一直是TCP。在传统单一有线网络上,借助TCP完善的拥塞控制、超时重传等可靠连接传输机制进行通信是网络交互的普遍做法。

移动互联网的快速发展使得TCP可靠连接变得不再那么美好。TCP使用4元组<SIP,SPORT,DIP,DPORT>确定一个连接的方式在有线与移动网络的切换场景下变得不稳定了。在弱网环境下,频繁重连不仅会影响用户体验,也会进一步消耗服务资源。

QUIC协议建立在UDP协议的基础上,并会随机为每个QUIC连接指定一个64位的身份标识——连接ID,表示一个连接。这个QUIC连接ID使连接迁移得到了实现,在4元组客户端IP地址、端口变化的情况下,不需要重连验证即可进行断点传输。

在集群化的服务场景中,四层负载均衡器通常使用VIP为集群化服务提供代理。所以,来自客户端的请求报文可以被集群化负载均衡器中的任意一台负载均衡器所处理。在常见的轮询、最小连接等调度方式中,集群化负载均衡器中的任意一台负载均衡器都会根据4元组的不同,将报文转发到特定的后端服务器。但是,对于同一个QUIC连接ID,4元组不同的QUIC服务,常见的轮询、最小连接等调度方式已经无法根据4元组(不同4元组的QUIC连接ID可能相同)来将同一个QUIC连接调度到同一个后端服务器。所以,我们需要提供一种一致性的算法,使得属于同一个QUIC连接的报文能够在任意时刻被集群化负载均衡器中的任意一台负载均衡器正确地转发出去。

QUIC协议的帧结构如下:

DPVS负载均衡器将包含在UDP报文数据段中的QUIC连接ID取出作为一致性哈希键值,通过完全一致的哈希算法,使属于同一个QUIC连接上的UDP报文得到相同转发规则。也就是,使QUIC请求经过DPVS负载均衡器集群内任意一台服务器均能够被同一台后端QUIC服务器处理。

QUIC连接ID的一致性调度方法仅解决了连接能够被正确处理的问题。在商业化的服务中,客户行为分析尤为重要,QUIC连接ID无差别的生成规则,不适合进行相关行为分析。客户端IP地址的区域性、运营商隔离等性质,在用户行为分析中是重要的参考信息。所以,实现QUIC服务器采集经过负载均衡器的用户真实IP地址,也是一个需要解决的问题。

负载均衡器可以通过将客户端信息(如客户端IP地址、端口等)插入TCP的Options扩展字段,将这部分信息透传到后端服务器。UDP报文中无类似区域的Options扩展字段,所以将客户端IP地址、端口等数据透传、下沉到网络层IP报文的Options扩展字段中是一个可行方案。

DPVS负载均衡技术实现了在自定义私有协议及IP协议的Options扩展字段中插入客户端信息的数据插入方式,用于客户端信息的透传,这在技术上被称为UOA。由于不同三层交换机厂商对扩展IP协议Options的支持态度不一致,因此在默认情况下使用私有协议透传客户端信息。

在IP报文上携带客户端信息时需要考虑以下3个问题。

● IP报文的Options空间是否充足。

● 新增了UOA Data后的IP报文是否超过了传输网络的MTU。

● IP报文Options的IPOPT_END是否会覆盖UOA Data。

在DPVS的实现中,IP报文Options空间不足,或者插入UOA Data后超过链路MTU,均会构建独立的仅携带UOA Data的报文,先于真实用户数据包进行发送。

对于IPOPT_END覆盖问题,事实上IPOPT_END不是必需的,Linux内核中也没有强制要求将Options扩展字段的属性设置为IPOPT_END。

解决了上述3个问题,下面分别看一下如何使用私有协议和IP协议携带UOAData。

(1)使用私有协议携带UOA Data。

DPVS私有协议——UOA传输协议将会在IP报文头与IP报文Payload之间(IP报文头和UDP报文之间)插入一段携带用户信息的数据。为了使RS能够正确识别这个信息段,还为该数据段增加了协议头。

UOA传输协议的定义如下:

● Ver:version=0x1表示IPv4,version=0x2表示IPv6。

● Rsvd:保留字段,置0。

● Protocol:表示紧随其后的报文协议类型,目前该私有协议仅做UDP的

UOA信息传输,因此该值现阶段仅可能为IPPROTO_UDP。

● Length:Options长度信息,用于对客户端地址类型进行解析。

● Options:UOA Data.

完整的UOA传输协议IP报文结构如下:

(2)通过IP协议携带UOA Data。

通过IP协议携带UOA Data,需要在IP报文的IP Options字段中插入UOAData。插入UOA Data后的IP报文结构如下:

最后,在后端服务器的UOA内核模块中,调用Netfilter的LOCAL_IN()钩子函数,分别对两种报文形式进行相关解析即可还原客户端IP地址。

3.QUIC协议的应用

如何从HTTP1.x转向QUIC协议?

启用QUIC协议的方案有如下两种。

方案一:客户端基于支持gQUIC的cronet进行开发,服务器端选择基于Chromium项目下的gQUIC进行相关服务开发。长期看来,gQUIC有向HTTP3.0合并的趋势,后续需要维护两个QUIC分支;其优点是Google在这方面提供了成熟的使用案例以便大家参考,因此可以实现快速开发。

方案二:客户端基于HTTP3.0的Libcurl进行开发,服务器端按照HTTP3.0进行相关开发。长期看来,HTTP3.0会成为行业标准,客户端无须再进行改动,即使对HTTP3.0进行协议降级后客户端也无须改动;其缺点是由于HTTP3.0发布的时间还较短,可以参考的资料有限。

目前,基于gQUIC完成度较高的开源项目有Caddy(见链接[19])。Caddy是一个采用Golang开发的WebServer,对QUIC协议的支持主要由quic-go(见链接[20])库提供。quic-go项目通过QUIC标准趋势分析预测IETF标准的QUIC协议将在下一个HTTP标准中胜出,已经停止对gQUIC分支进行更新。

基于HTTP3.0的开源项目有liteSpeed(见链接[21]),以及CloudFlare开源的quiche(见链接[22])的Nginx Module。

由于HTTP1.x的服务存量庞大,因此HTTP1.x的服务器端要支持QUIC协议并不是一件简单的事,各端重新开发支持QUIC协议的服务器端是一个效率极低的方式。在QUIC协议的客户端及HTTP1.x的服务器端之间引入代理层,将QUIC请求解析为HTTP1.x请求在私有网络中转发是最为高效的支持QUIC协议的方式。HTTP1.x通过QUIC代理层支持QUIC协议的架构如图6-16所示。

图6-16 HTTP1.x通过QUIC代理层支持QUIC协议的架构

上文提到,Caddy支持从QUIC到HTTP1.x的反向代理功能。在生产环境中,我们也需要四层负载均衡器后面的Caddy服务器来记录日志及客户端IP地址。

但是,原生的Golang网络库中没有导出连接FD的方法,进而无法获取用户IP地址。因此,需要在Golang网络库的UDP Conn(见链接[23])接口中新增一个UdpFd()方法用于返回文件描述符FD,代码如下:

然后,Caddy依赖的quic-go库的UDP包处理函数handlePacket(见链接[24])就可以从UDPConn中还原客户端IP地址。

如果对Caddy的性能有疑虑,则可以考虑使Nginx支持QUIC模块。目前,IETF的HTTP3.0也只是以草案方式提出,还在快速迭代中。相较之下,gQUIC在Google内部的使用时间更长,可参考的实现更多,并且如上文分析,Google的gQUIC还包含了应用层HTTP的实现,借助Chromium框架实现Nginx的QUIC模块支持是一个比较稳妥的方法。

QUIC->HTTP方向的流程比较简单,Nginx的每次读事件都要将读到的QUIC帧交给Chromium框架的QuicFramer进行解析。

HTTP->QUIC方向则需要借助filter框架,将请求通过Chromium框架发送回去。

6.5 DNS协议优化

DNS即域名解析协议,客户端向服务器发起通信时,会经过DNS解析查找,将域名转换成IP地址。我们先了解一下DNS解析流程,再讨论如何进行DNS协议的优化。

DNS解析流程主要包括5个步骤:①查找浏览器缓存;②查找系统缓存;③查找路由器缓存;④查找ISP DNS缓存;⑤迭代查询。

通过DNS解析流程我们可以知道,DNS协议的优化可以从以下两个方面进行:减少DNS请求数量和缩短DNS请求时间。

减少DNS请求数量可以通过减少域名数量和增加DNS缓存避免重定向来实现。我们可以使用浏览器、服务器和计算机DNS缓存防止DNS迭代查询,并利用HTTP协议的keep-alive特性保持TCP连接以降低DNS查找频率。现在,我们还可以采用DNS预解析,在用户打开页面之前进行域名解析,减少用户等待时间,提高用户体验。

此外,对于内网服务,我们可以集成服务发现机制,定期将域名解析同步到本地。爱奇艺设计了一个内网调度中心服务,其架构如图6-17所示,业务方将域名服务注册到consul集群,在客户端安装改进的dnsmasq,并将要访问的域名添加到配置文件中,这样客户端的dnsmasq进程就会定期到consul集群中获取域名对应的IP地址,并将其缓存到本地。当客户端访问该域名时,DNS解析请求会被dnsmasq劫持,直接返回本地缓存域名对应的IP地址。这个DNS解析过程是在本地完成的,基本无延迟。

图6-17 爱奇艺内网调度中心服务架构

本文给大家讲解的内容是网络协议优化-基于UDP的传输协议优化、DNS协议优化下篇文章给大家讲解是性能优化-性能挑战与分析

标签: #nginxiisip