任何倒闭的建筑设计都是耍流氓。网页接收消息时,是push还是pull?什么业务场景?对于登录网页的用户A来说,发送者,也就是消息的来源,有几个方面:系统给A发送的“系统通知”,可能对实时性要求不是那么高;用户发给A的“聊天信息”对实时性要求比较高,而且越实时越好。消息的处理者,即系统端,通俗的说:有服务对消息进行逻辑处理;有一个数据库来实现数据;有一个缓存来加速数据。撇开这些技术细节不谈,我们假设服务器有一个针对每个用户的“待接收消息”队列,其中存储了所有需要发送给该用户的消息。如果消息的接收者,即用户A在网页上登录,由于HTTP协议是“请求-响应”,服务器和网页之间没有消息通道,需要这种类型的“收到消息”是如何处理的?方案一:轮询拉取轮询拉取是最容易想到的实现方式:发送方先发送消息,先进入队列;网页启动定时器,每隔一段时间(比如10秒),发起轮询请求拉取队列中的消息;如果队列中有消息,则返回该消息;如果队列中没有消息,则在10秒后再次轮询。这种方法的优点是实现简单,直观易懂。互联网出现的时候,人少的聊天室就是这么玩的。画外音:成立于1996年的老牌互联网网站碧海隐沙,曾经是中国最火爆的聊天室,已于2017年9月27日停止运营。缺点也很明显:实时性差:最坏情况下,1条消息后进入队列,10s后接收;低效率:发送消息是一个低频动作。如果10次轮询后收到1条消息,则请求有效性只有10%,浪费大量服务器资源。更何况,在这个方案中,实时性和效率是不可调和的矛盾:如果轮询周期设置为1/10,则延时缩短为1秒,也就是说100次轮询收到1条消息,请求有效期为减少到1%。方案二:建立长连接如果要兼顾实时性和效率,长连接是最好的选择,PC端的聊天软件基本上都是使用长连接。网页持久连接的实现有两种常见的方式:WebSocketFlashSocket这两种方案的细节就不展开了,它们都有一定的局限性。一种更通用的方法是“长轮询”。长轮询通过组装HTTP短连接达到长连接的效果,保证100%实时消息,最大化系统效率。方案三:HTTP长轮询HTTP长轮询的核心是在浏览器和服务器之间建立一个“通知连接”。它的特点是:这是一个从浏览器发送到网络服务器的HTTP连接;该连接仅用于接收推送通知;与普通的“请求-响应”HTTP请求不同,此HTTP将被服务器阻止,直到推送通知到达或超过约定的时间。画外音:对于HTTP请求,为了提高效率,一般来说浏览器和web-server都会有一些设置。如果一个HTTP请求长时间(比如150秒)没有数据,就会断开连接。为了不被浏览器和web-server粗暴断开连接,“通知连接”一般会设置一个约定的阈值(比如小于150秒),系统返回一个空的消息表示“优雅返回”。更具体地说,你如何玩“坚持”和“只收到推送通知”的“通知连接”?场景1,发起通知连接时,队列中有消息,则:发起通知连接,恰好队列中有消息;队列中的消息实时带回来;并立即启动通知连接。场景2,发起通知连接时,队列中没有消息,则:发起通知连接时,队列中没有消息;等待“时间阈值”被触发,没有消息返回;立即再次发起通知连接。场景三:当有新消息到来时,有通知连接,则:当有新消息到来时,有通知连接;通知连接会实时带回消息;立即发起通知连接。以上三种场景的最终状态都是“一定,永远,浏览器和服务器之间会有一个通知连接”,这样可以保证消息的实时性。当然有人会说HTTP的返回和重新发起会有时间差。如果这个时差刚好有新消息过来呢?场景4,当有新消息到来时,连接没有被通知,则:当新消息到来时,连接没有被通知;将新消息放入队列。最后一种场景出现的概率很小,但是也保证了在“HTTP返回和重新发起的时间差”内消息不会丢失,连接后可以实时返回消息被通知。总结一下,网页接收消息是push还是pull?最简单的思路就是pull,但是实时性和效率是不可调和的矛盾;最好的方式是push,但是WebSocket和FlashSocket都有自己的局限性;最常见的方法是长轮询,通过HTTP短连接组装长连接,具体是通过“tampingdown”和“只接收推送通知”的“通知连接”,可以实现消息的实时到达。【本文为专栏作者《58神剑》原创稿件,转载请联系原作者】点此阅读更多该作者好文
