无标题
Posted LiveVideoStack_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了无标题相关的知识,希望对你有一定的参考价值。
翻译、编辑:Alex
技术审校:刘连响
本文来自Smashing Magazine,原文链接:
https://www.smashingmagazine.com/2021/08/http3-performance-improvements-part2/
Robin讲HTTP/3 #004#
连接迁移
连接迁移是QUIC的第三个性能特性,它通过保持现有连接不变,使QUIC在网络间传输时更快。这确实有效,但这种网络变化并不经常发生,连接仍然需要重新设置它们的发送速率。
我们在第一部分讨论过,QUIC的CID允许它在切换网络时进行连接迁移。我们通过“客户端在下载大文件时从Wi-Fi切换到4G网络”说明了这一特性。在TCP上,此类下载很可能会中止,但使用QUIC便有可能继续下载。
不过,先要考虑这种情况的实际发生频率。你也许认为,当你在一个建筑物内多个Wi-Fi接入点或者马路上各个信号塔间移动时也会发生连接迁移。但在这些场景中,你的设备IP通常不会发生变化,因为无线基站之间的转变在更加底层的协议中完成。因此,只有在完全不同的网络间移动时才会发生连接迁移,所以我说这种情况并不常见。
其次,除了下载大文件、实时视频会议和传输视频流之外,我们可以问问连接迁移是否还适用于其他应用场景。如果你在加载网页时正切换网络,你也许确实需要重新请求一些(后来的)资源。
不过,加载网页通常只需几秒,所以刚巧碰上切换网络的情况并不常见。除此之外,对于迫切需要解决这一问题的应用场景来说,通常已有其他缓解措施。比如,提供大文件下载的服务器支持HTTP范围请求[1]以允许可恢复下载。
网络1断开与网络2连接之间通常存在一些重叠时间(overlap time),所以视频应用可以打开多个连接(每个网络一个),并在旧网络完全消失之前同步它们。用户仍将注意到网络切换,但是视频并不会完全断开。
再次,无法确保新、旧网络的可用带宽一样。因此,即使是概念上的连接完好,QUIC服务器也无法一直保持高速发送数据。而为了避免新的网络过载,它需要重置(或者至少降低)发送速率,并在拥塞控制器的慢启动阶段[2]重新开始。
因为初始发送速率通常太低,无法真正支持如视频流这样的数据,所以即使在QUIC上,也会出现质量损失(quality loss)或者其他暂时性的小问题。某种程度上,连接迁移的目的更多是防止连接上下文混乱以及服务器上的开销,而不是提升性能。
“你知道吗?
注意,正如前文对0-RTT的讨论,我们可以开发一些高级技术来改进连接迁移。比如,我们可以尝试记住上次特定网络上有多少可用带宽,并在新的连接迁移时努力提升到这一水平。除此之外,我们也可以想象不仅仅切换网络,也可以同时使用两个网络。这个概念被称为“多路径(multipath)”,我们在下文会详细介绍[3]。
到目前为止,我们已经讨论了主动连接迁移(active connection migration),其中用户在不同的网络间移动。但也存在被动连接迁移(passive connection migration)[4]的情况,其中特定网络本身会更改参数。NAT[5] rebinding是一个很好的例子,(虽然本文不会全面讨论NAT)它意味着连接的端口号可以在任何特定时间更改,而不发出警告。在大多数路由器中,UDP比TCP更经常出现这种情况。
一旦发生端口号改变,QUIC将不会更改CID,且大多数实现也将假设用户依然在同一物理网络上,从而不会重新设置拥塞窗口或者其他参数。PING[6]和timeout indicator[7]等一些QUIC中的特性也会阻止CID的更改,因为这通常会导致长闲置连接(long-idle connection)的发生。
我们在第一部分曾讨论过:出于安全考虑,QUIC不使用单一CID,而是在执行主动迁移时更改CID。实际情况更加复杂,因为客户端和服务器都有单独的CID列表(在QUIC RFC中被称为source and destination CID[8])。参见下图5。
图 5:QUIC使用了不同的客户端和服务器CID
这种做法使每个端都可以选择自身的CID格式和内容,而这对于实现高级路由和负载均衡至关重要。通过连接迁移,负载均衡器不再查看四元组来识别连接并将其发送到正确的后端服务器。不过,如果所有的QUIC连接都将使用随机CID,这将在很大程度上增加负载均衡器的内存需求,因为它需要存储CID到后端服务器的映射。此外,这种做法也不适用于连接迁移,因为CID会更改为新的随机值。
因此,部署在均衡负载器后面的QUIC后端服务器拥有属于自己的CID可预测格式(predictable format)非常重要,这样负载均衡器才能从CID中获取正确的后端服务器(即使在迁移之后)。IETF提案文件[9]中描述了用于执行这一操作的一些选项。为了实现这一切,服务器需要能够选择自己的CID,可是如果连接发起者(对于QUIC来说,始终是客户端)已经选择了CID,服务器就无法再这么做了。这就是QUIC中客户端和服务器CID存在分歧的地方。
这一切意味着什么?
因此,连接迁移是一种情景特性(situational feature)。谷歌的最初测试[10]显示其应用场景的改进比例较低。很多QUIC实现还没有实现这一特性。即使那些已经实现连接迁移的场景也被限制为移动(而非桌面)客户端和应用。有些人认为这一特性是多余的,因为在大多数情况下,使用0-RTT打开新的连接本该具备同样的性能属性。
不过,应用场景或者用户画像的差异也可能带来很大影响。如果你的网站或者应用经常在移动情景下使用(如Uber或Google Maps),那么你也许会比使用桌面程序的用户受益更多。同样,如果你专注于持续互动(如视频聊天、协同编辑或者游戏),那么与新闻网站相比,连接迁移会为你遇到的最坏情况带来更多改进。
队头阻塞消除
QUIC的第四个特性目的是通过缓解队头阻塞使QUIC在丢包发生率较高的网络上更快。虽然理论上正确,但实际中,它将很可能只为网页加载带来很小的性能优势。
为了理解这点,我们需要先了解流优先级和多路复用。
| 流优先级(Stream Prioritization)
我们在第一部分曾讨论过,单个TCP丢包会导致多个传输中资源数据的延迟,这是因为TCP的字节流认为所有数据都属于单一文件。而QUIC非常清楚存在多个并发字节流,并能在每个字节流的基础上处理丢包。然而,正如我们所看到的,这些字节流并没有真的在并行传输数据,而是流数据被多路复用到单一连接上。这种多路复用可以有多种方式实现。
比如,对数据流A、B和C来说,我们可能会看到ABCABCABCABCABCABCABCABC这样的数据包序列,其中我们可以更改每个数据包中的活跃数据流[我们称之为轮询(round-robin)]。
然而,我们还可能看到相反模式:AAAAAAAABBBBBBBBCCCCCCCC,其中每个数据流在下一个数据流开始前全部完成[我们称之为顺序(sequential)]。当然在这两个极端之间,还存在很多其他可能的选项(如AAAABBCAAAAABBC…、AABBCCAABBCC…和ABABABCCCC…等)。多路复用的方案是动态的,并由HTTP级的特性流优先级驱动(我们将在下文讨论)。
事实证明,你选择的多路复用方案会对网站加载性能产生巨大影响。参见下文中由Cloudflare[11]提供的视频,每个浏览器都使用了不同的多路复用器。其中的原因相当复杂,我已经写了几篇[12]相关的学术论文[13],并在一个会议上讨论过[14]。以Webpagetest[15]闻名的Patrick Meenan甚至制作了相关主题的三小时课程[16]。
数据流多路复用的差异对于不同浏览器中的网站加载影响很大
幸好我们可以相对轻松地解释基础知识。如你所知,一些资源会被渲染阻塞(render blocking)[17]。CSS文件和html head元素中的javascript会遇到这种情况。这些文件在加载的时候,浏览器不能渲染它们(或者如执行新的JavaScript)。
更重要的是,CSS和JavaScript文件需要完整下载才能使用(虽然它们经常以增量方式被解析和编译)。因此,这些资源需要尽快被下载(以最高优先级)。让我们思考一下,如果A、B和C都是渲染阻塞资源会发生什么。
图6:数据流多路复用方法影响(渲染阻止)资源完成时间
如果我们使用了一个轮询多路复用器( round-robin multiplexer ,图6中上面一行),实际上将延迟每个资源的全部完成时间,因为它们都需要与其他资源共享带宽。由于我们在资源全部下载完成时才能使用它们,因此引发了很严重的延迟。然而,如果我们将它们按顺序多路复用(参见图6中下面一行),会看到A和B更早完成(并被浏览器使用),同时(实际上)并没有延迟C的完成时间。
不过这并不意味着顺序多路复用总是最佳方法,因为一些(大部分非渲染阻塞)资源(比如HTML和progressive JPEG)实际会以增量方式处理和使用。在那些(以及其他)情况中,使用第一个轮询多路复用(或者至少是二者之间的选项)很合理。
但对于大部分网页资源来说,顺序多路复用(sequential multiplexing)是最佳选择。比如,上文视频中Google Chrome示例,而Internet Explorer使用的是最坏情况下的轮询多路复用器。
丢包恢复能力(Packet Loss Resilience)
既然我们知道所有数据流不会一直同时处于活跃状态,同时它们可以以多种方式被多路复用,我们可以想想出现丢包时会发生什么。第一部分曾解释过,如果一个QUIC流出现丢包,依然可以使用其他活跃的数据流(而在TCP中,所有数据流都会暂停)。
不过,正如我们刚刚看到的,同时拥有多个并发活跃的数据流通常并不利于网络性能,因为会延迟一些关键(渲染阻塞)资源,即使在没有丢包的情况下!我们宁可同时对一或两个活跃数据流使用顺序多路复用器。然而,这将减少QUIC队头阻塞消除所带来的影响。
比如,想象一下,发送方可以在特定时间内传输12个数据包(参见图7)——记住这受到了拥塞控制器[18]的限制。如果我们用A(因为它优先级高且渲染阻塞,比如main.js)的数据填充所有 12个数据包,那么在这 12个数据包的窗口中我们将只有一个活跃数据流。
如果其中一个数据包丢失,那么QUIC最终仍将发生完全队头阻塞,因为除了A之外,它没有其他数据流可以处理:所有数据都用于A,所有数据都仍需等待(我们没有B或C数据处理),这与TCP很相似。
图7 丢包影响取决于使用的多路复用器(注意我们假设每个数据流所发送的数据都比之前类似的图片中的多)
我们看到现在存在矛盾的地方:顺序多路复用(AAAABBBBCCCC)通常更有利于网络性能,但它不允许我们充分利用QUIC队头阻塞消除的优势。轮询多路复用(ABCABCABCABC)在处理队头阻塞方面具有优势,但是却不利于网络性能。因此可以说,一个最佳实践或优化会抵消掉另一个。
然后情况会变得更加糟糕。到现在为止,我们已经假设单一数据包每次丢失一个。但并不总是这样,因为互联网上的丢包经常是“突发”的[19],意味着通常会同时发生多个丢包。
我们在前文讨论过,丢包的一个重要原因是网络因数据过多而过载,不得不丢掉多余的数据包。这就是拥塞控制器开始发送缓慢的原因。不过它会不断增加发送速率直到出现丢包!
换言之,本来目的是防止网络过载的机制实际上导致了网络过载(虽然以一种控制的方式)。在大部分网络上,网络过载通常在很长一段时间后发生:当发送速率增加到每个往返发送数百个数据包时。当达到网络极限时,几个数据包会同时丢失,从而导致突发的丢包模式。
“你知道吗?
这就是我们转而使用HTTP2的单一(TCP)连接,而非HTTP1.1的6~30个连接的原因。因为每个单一连接都会以几乎相同的方式增加发送速率,HTTP1.1在最开始的时候会获得很快的加速,但多个连接实际上导致了网络过载,因此很可能已开始引发彼此大规模丢包。
那时,Chromium的开发者推测[20]这种行为会引发互联网上大部分可见丢包,这也是BBR成为最常用的拥塞控制算法的原因之一,因为它使用观察到的RTT波动(而非丢包)来评估可用带宽。
“
你知道吗?
其他导致丢包的原因也会引起较少或者单一的数据包丢失,尤其是在无线网络上。不过这些丢包通常在更底层的协议层被发现并在两个本地实体间(比如智能手机和4G网络)得到解决,而不是通过客户端和服务器间的重传。这些原因不会导致真正的端到端丢包,但会表现为包延迟(或“抖动”)以及包的乱序到达。
所以,让我们假设正在通过一个per-packet轮询多路复用器(ABCABCABCABCABCABCABCABC…)充分消除队头阻塞,并发生4个突发丢包。我们发现这将一直影响全部三个数据流(参见图8中间那一行)!在这种情况下,QUIC的队头阻塞消除没有带来任何好处,因为所有数据流都必须等待重传。
图8:根据所使用的多路复用器和丢包模式,数据流或多或少都会受到影响
为了降低突发丢包对于多个流的影响,我们需要为每个流连接更多数据。比如,AABBCCAABBCCAABBCCAABBCC…是一个小的改进,而AAAABBBBCCCCAAAABBBBCCCC…(参见图8最下面一行)则获得更多改进。你可以再次看到偏顺序的方法更好,即使它减少了我们拥有多路并发活跃流的可能。
最后,很难预测QUIC队头阻塞消除的实际影响,它取决于流的数量、突发丢包的大小和频率,以及流数据实际使用方式等。但此时大多数结果[21]表明队头阻塞消除不会对网页加载应用场景有太大帮助,因为加载网页时我们通常需要更少的并发流。
如果你希望了解这个主题更多细节信息或只是一些具体的示例,请阅读我写的关于HTTP队头阻塞的深度文章:
https://calendar.perfplanet.com/2020/head-of-line-blocking-in-quic-and-http-3-the-details/
“你知道吗?
与前面部分一样,这里我们可以使用一些高级技术。比如,现代拥塞控制器使用数据包平滑发送( packet pacing)[22]。这表示它们不会在单一一次突发中发送100个数据包,而是将数据包分散在一整个RTT中。这在概念上降低了网络过载的发生,QUIC Recovery RFC强烈建议使用数据包平滑发送这一技术[23]。作为补充,BBR[24]等拥塞控制算法不会一直增加发送速率直到引发丢包,而是在此之前回退(通常查看RTT波动,因为网络变得过载时RTT也会上升)。
虽然这些方法降低了总体丢包的发生,但并不一定降低数据包突发。
这一切意味着什么?
理论上,虽然QUIC队头阻塞消除意味着QUIC(和HTTP/3)应该在丢包网络上表现更好,但实际上会受到很多因素的影响。由于网页加载应用场景更倾向于偏顺序的多路复用设置,而且丢包无法预测,所以队头阻塞消除这一特性可能会主要影响到那些最慢的1%用户。目前这仍是一个活跃的研究领域,时间将会证明一切。
然而,有些应用场景也许会得到更多改进。这些场景大部分都不属于常见的网页加载。比如,当资源没有渲染阻塞时;当资源以增量方式被处理时;当数据流完全独立时;或者同时发送更少的数据时。
这些例子包括对于缓存良好的网页的重复访问、单一页面应用中的background download和API调用。比如,Facebook使用HTTP/3在原生应用中下载数据时,已经从队头阻塞消除特性中获益。
UDP和TLS性能
QUIC和HTTP/3的第五个性能特性是关于如何高效且高性能地在网络上创建和发送数据包。我们将看到,QUIC使用UDP和重度加密会让它变得比TCP慢一点(但情况正在改善)。
首先,我们已经在前文讨论过QUIC使用UDP更多是为了灵活性和可部署性,而不是为了提升性能。直到最近,在UDP上发送QUIC数据包比发送TCP数据包慢得多的事实更加证明了这一点。这在一定程度上是由实现这些协议的位置和方式造成的(参见下图9)。
图9:TCP和QUIC之间的实现区别
正如上文所述,TCP和UDP通常直接在操作系统较快的内核中实现,而TLS和QUIC通常在较慢的用户层实现(注意QUIC并不是真的需要在用户层实现,而主要是因为这么做会让它变得更加灵活)。这时QUIC就已经比TCP慢一点了。
此外,当从用户层软件(如浏览器和网络服务器)发送数据时,我们需要将数据传递给操作系统内核,接着使用TCP或者UDP将数据放在网络上。使用内核API(系统调用)完成了这些数据的传递,而每个API都包含一定量的开销。对于TCP来说,这些开销曾比使用UDP低得多。
这主要是因为,TCP在过去的使用要比UDP多得多。因此,随着时间的推移,许多优化被添加到TCP实现和内核API中,目的是为了将数据包发送和接收开销降到最低。许多网络接口控制器(NIC,network interface controller)甚至具备用于TCP的内置硬件卸载特性。不过UDP就没有这么幸运了,使用上的限制使它无法添加更多优化。在过去五年中,幸好情况发生了改变,大多数操作系统也已为UDP添加了优化选项[25]。
其次,QUIC单独加密每个数据包带来了很多开销。而TLS over TCP要比QUIC快,因为其中数据包以块(chunk)的形式加密[26](一次最多大约16kb或者11个数据包),从而更加高效。这是QUIC中特意做出的一次权衡,因为批量加密会导致其自身形式的队头阻塞[27]。
和第一点[其中添加额外的API使UDP(和QUIC)变得更快]不同的是,与TCP+TLS相比,这是QUIC的一个固有劣势。但这在实际中很容易处理,比如,使用优化加密库[28]和批量加密QUIC数据包头的巧妙方法。
因此,虽然TCP+TLS比谷歌早期的QUIC曾经快上两倍[29],但QUIC已经得到很大改进。比如,在最近的测试中,微软高度优化的QUIC栈[30]已经能够达到7.85 Gbps,而在同一系统上的TCP+TLS为11.85 Gbps(所以这里QUIC的速度约为TCP+TLS的66%)。
这与最近的Windows更新有关,这次更新使UDP变得更快(全面对比下,系统上UDP的吞吐量为19.5 Gbps)。谷歌的QUIC协议栈最优化版本目前约比TCP+TLS慢20%[31]。Fastly在不太高级的系统上曾使用一些技术做过早期测试[32],并声称可以获得相同的性能(约450 Mbps),这表明在不同的应用场景,QUIC绝对可以与TCP竞争。
不过即使QUIC要比TCP+TLS慢两倍,情况也没有那么糟糕。首先,由于需要执行其他逻辑(比如HTTP、缓存和代理等),QUIC 和 TCP + TLS 处理通常并不是造成服务器压力最大的操作。因此,实际上你并不需要两倍的服务器来运行QUIC(不过,还不清楚它将对真实的数据中心所造成的影响,因为还没有一家大公司发布这方面的数据)。
其次,仍有很多机会优化QUIC实现。比如,逐渐地,一些QUIC实现将(部分)迁移到操作系统内核中(与TCP非常相似)或避开它(有些实现已经这样做了,如MsQuic[33]和Quant[34])。我们同样可以期待使用QUIC的硬件[35]出现。
尽管如此,仍可能存在一些将TCP+TLS作为首选的应用场景。比如Netflix曾表示它可能不会太快迁移到QUIC上,因为它已经在FreeBSD的定制化[37](以通过TCP+TLS传输视频)上进行了大量投资。
类似地,Facebook也曾表示,由于其较大的开销,QUIC将有可能主要用于终端用户和CDN边缘之间,而不是数据中心或边缘节点和源站之间。一般来说,非常高的带宽场景将更有可能继续使用TCP+TLS,尤其是在接下来几年。
“你知道吗?
优化网络栈是一个非常深入且复杂的技术难题,上文的内容只触及了浅显的知识(并遗漏了很多细微差别)。如果你足够勇敢或者你想知道GRO/GSO, SO_TXTIME、内核旁路(kernel bypass)、sendmmsg() 和 recvmmsg() 等术语的意思 ,我可以向你推荐一些由Cloudflare[37]和Fastly[38]创作的优秀的优化QUIC文章、微软的关于QUIC实现的演讲[39]以及思科的深度演讲[40]。最后还有谷歌工程师带来的关于优化QUIC实现的有趣主题演讲[41]。
这一切意味着什么?
QUIC使用UDP和TLS协议曾使它比TCP+TLS慢得多。然而,一些已经实现的改进(将持续改进)逐渐缩小了它们之间的差距。不过,在网页加载的典型应用场景中,你很可能不会注意到这些差异,但如果你在维护大量服务器,那么它们也许会给你带来很大麻烦。
注释:
[1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests
[2] https://www.smashingmagazine.com/2021/08/http3-performance-improvements-part2/#congestion-control
[3] https://www.smashingmagazine.com/2021/08/http3-performance-improvements-part2/#future-developments-to-look-out-for
[4] https://www.rfc-editor.org/rfc/rfc9000.html#name-connection-migration
[5] https://computer.howstuffworks.com/nat.htm
[6] https://www.rfc-editor.org/rfc/rfc9000.html#frame-ping
[7] https://www.rfc-editor.org/rfc/rfc9000.html#idle-timeout
[8] https://www.rfc-editor.org/rfc/rfc9000.html#name-connection-id
[9] https://datatracker.ietf.org/doc/html/draft-ietf-quic-load-balancers-06
[10] https://github.com/quicwg/wg-materials/blob/main/ietf104/IETF_104_QUIC_Connection_Migration.pdf
[11] https://blog.cloudflare.com/better-http-2-prioritization-for-a-faster-web/
[12] https://speeder.edm.uhasselt.be/www18/files/h2priorities_mwijnants_www2018.pdf
[13] https://h3.edm.uhasselt.be/files/ResourceMultiplexing_H2andH3_Marx2020.pdf
[14] https://www.youtube.com/watch?v=nH4iRpFnf1c
[15] https://www.webpagetest.org/
[16] https://www.youtube.com/watch?v=ct5MvtmL1NM
[17] https://web.dev/render-blocking-resources/
[18] https://www.smashingmagazine.com/2021/08/http3-performance-improvements-part2/#congestion-control
[19] https://huitema.wordpress.com/2020/07/12/parsing-quic-logs-and-assessing-packet-losses/
[20] https://a77db9aa-a-7b23c8ea-s-sites.googlegroups.com/a/chromium.org/dev/spdy/An_Argument_For_Changing_TCP_Slow_Start.pdf?attachauth=ANoY7crnhU249H7J5qnPlmYtYjztjaJPDTFxJkAw5PsT28a710erbMoDc9PCddnVBSR3kE3wYYryZJ4fDLxKAzxvqjPbKqUF5xBo-4lqVPiGXZCkHhDM7Hlx8ablVaHvjDoq2s2jXO9u93x5_GZCxghtaWTF70wF87yXWV97Y8LKGwDtyDuB3NXvqqTMn9TFfoX2AZGqMdoimDFEYwl43VMMnjko00U9Rdhlakc2-jY7675Q6UTbFqk0xvPvtEvM4KFZsa9cuzww&attredirects=0
[21] https://h3.edm.uhasselt.be/files/ResourceMultiplexing_H2andH3_Marx2020.pdf
[22] https://homes.cs.washington.edu/~tom/pubs/pacing.pdf
[23] https://www.rfc-editor.org/rfc/rfc9002.html#name-pacing
[24] https://blog.apnic.net/2017/05/09/bbr-new-kid-tcp-block/
[25] https://blog.cloudflare.com/how-to-receive-a-million-packets/
[26] https://blog.cloudflare.com/optimizing-tls-over-tcp-to-reduce-latency/
[27] https://www.igvita.com/2013/10/24/optimizing-tls-record-size-and-buffering-latency/
[28] https://github.com/h2o/picotls/pull/310
[29] https://rjshade.com/work/files/papers/pdf/langley_et_al_sigcomm2017_quic.pdf
[30] https://github.com/microsoft/msquic
[31] https://youtu.be/xxN4FfwaANk?t=3161
[32] https://www.fastly.com/blog/measuring-quic-vs-tcp-computational-efficiency
[33] https://github.com/microsoft/msquic
[34] https://github.com/NTAP/quant
[35] https://datatracker.ietf.org/meeting/104/materials/slides-104-quic-offloading-quic-00
[36] https://www.youtube.com/watch?v=8NSzkYSX5nY
[37] https://blog.cloudflare.com/accelerating-udp-packet-transmission-for-quic/
[38] https://www.fastly.com/blog/measuring-quic-vs-tcp-computational-efficiency
[39] https://www.youtube.com/watch?v=Icskyw17Dgw
[40] https://archive.fosdem.org/2020/schedule/event/fast_quic_sockets_for_cloud_networking/
[41] https://www.youtube.com/watch?v=xxN4FfwaANk
作者简介:
Robin Marx: IETF贡献者、HTTP/3和QUIC工作组成员。2015年,作为PhD的一部分,Robin开始研究HTTP/2的性能,这使他后来有机会在IETF中参与HTTP/3和QUIC的设计。在研究这些协议的过程中,Robin开发了QUIC和HTTP/3的调试工具(被称为qlog和qvis),目前这些工具已经使来自世界各地的许多工程师受益。
致谢:
本文已获得Smashing Magazine和作者Robin Marx的授权翻译和发布,特此感谢。
以上是关于无标题的主要内容,如果未能解决你的问题,请参考以下文章
如何让 Try except 输出更多细节?显示它到底发生在哪里? - Python [重复]