作者回复: 我是这样认为的:
就像你说的,比如可靠性,保序性,拥塞控制等等,完全是可以放到应用层来做的。
但是,对应用层来说,它们关心的内容,不需要,也不应该包括这些传输方面的基本特性,或者说,已经有传输层的TCP把这些事做好了。而协议的设计者,关心他/她设计的协议想解决的问题,这些传输方面的特性,拿来用就可以了,而不需要再实现一遍。
往大了说,软件分层的目的最大目的,就是解耦。解耦之后“谁”做“什么事”就是每一层的职责,7层/5层模型里的传输层也一样。
作者回复: 其实基于 UDP 的应用层协议也有一些,常听说的比如有 DHCP。之所以我们平时接触的不多,主要还是由它们的特点所决定的,你可以看一下 https://en.wikipedia.org/wiki/User_Datagram_Protocol#Comparison_of_UDP_and_TCP 比如其中提到的可靠性和有序性就是我们一般应用层的基本通信要求。
另外一个,HTTP/3 也会使用 UDP 代替 TCP,主要原因还是在物理设备改进,传输质量提升了以后,出于性能的考虑。可以参见:https://thenewstack.io/http-3-replaces-tcp-with-udp-to-boost-network-speed-reliability/
作者回复: 最佳实践那一章的最后一篇特别放送,我会讲一讲程序员怎么学英语。可以期待一下。
作者回复: 这是你打开 https://www.websocket.org/echo.html 的时候,它已经默认帮你填好的内容
作者回复: 好问题,是这样的,HTTP/1.1 Comet 的一个局限性就在这里,也就是说,传输是单工的,从协议的角度来说,服务端的响应分多次返回,因此这个过程中,返回并没有传输完,那么对于该次 HTTP 连接来说,客户端也就无法发送新的请求。
当然,除了升级到 HTTP/2 或者采用其他技术以外,对于这个问题本身,还是有一些改进办法的,你可以思考一下。:)
作者回复: 👍,你也可以看看其他回答
作者回复: 感谢回复。
1. 正确。
2. 不一定。而且即便客户端需要保持服务端状态,也比服务端保持客户端状态,要简单得多(数量上是 1 和 N 的关系,在横向扩展的时候,这个 N 就可能成为瓶颈)
你说的这个 push-pull 结合的方式很好,其实它是一种非常常见的实现方式,你可以想想互联网有哪些应用。:)
作者回复: 你可以自己尝试实现一个最简单的 Comet:
客户端,你甚至都不用写 JavaScript,一个普通的 get 请求即可——浏览器直接访问某一个服务端暴露的接口(URL)。
服务端则是往响应中写数据,使用按块传输,返回类型设置为 plain/text,随便写一点数据并 flush,然后 sleep 10 秒,再写一点并 flush,再 sleep 10 秒,如是多次,这样可以模拟多次返回。
这样,你会看到浏览器一直在加载,页面十秒十秒地新增新的内容。
作者回复: HTTP/2 确实有了专门的 server push,但这个最主要用来解决资源的预加载问题。因此,如果要完全替代 Comet,实现任意时刻的推送,可以使用 SSE 等技术,或者是将 server push 和 SSE 等技术结合起来(即让浏览器端预留一个回调来响应服务端的推送)。
作者回复: 对于大多数分布式的系统来说,Pull 确实要比 Push 实现起来方便一些,但是也不绝对。至于你说的“是否引入新的框架”这个描述比较模糊,两种方式和是否引入框架没有必然联系。
作者回复: 非常好的问题!
是的,完全正确。你可以考虑这样几点:
1. 客户端和服务端,还有网关等中间节点,都要商定好一个最大的超时时间。并且,业务端配置的超时时间要稍短于其它,以留足余量。
2. 服务端即便没有消息需要推送,也要定期返回一个无业务意义的消息,以避免超时发生。
3. 客户端如果在若干个推送周期后,可以结束连接,休息一段时间,再重新连接。
作者回复: 感谢分享!
对于 Push 模型,你说的内容都是正确的,就提一点,对于你提到的数据包堆积和流控的问题,可以引入第三方的消息队列服务来协调消息发布者和订阅者,来缓解你说的问题。
对于 Pull 模型,“传输失败不用重试”这个不对,因为是不是重试并非 Push 或 Pull 的区别,二者都可以重试,也都可以不重试,取决于设计。其它正确。
当然,这是一个开放性问题,还有其它的优缺点,你也可以看看其他人的答复。
作者回复: 谢谢。
很多技术都是相通的,学习全栈技术都是可以用类比的方式进行的。
作者回复: 感谢分享!
关于第 2) 个答复:解包和封包其实并不是 push 和 poll 的本质区别,poll 也可以由解包封包,也可以定义一套消息结构。