HTTP协议
每个人都熟悉HTTP协议。在开始本文之前,我们将简要回顾一下HTTP协议。
HTTP协议是基于TCP协议的应用层协议。该协议的本质是请求-响应:
也就是说,对于HTTP协议来说,整个请求在服务器给出响应后结束,这是HTTP请求最大的特点,也正因为这个特点,HTTP请求做不到的是服务器主动将数据推送给客户端。
然而,由于HTTP协议的广泛应用,我们确实希望使用HTTP协议来实现实时数据采集。这个时候我们该怎么办?首先,介绍了几种基于HTTP协议的实时数据采集方法。
短轮询
轮询是基于HTTP协议获取实时数据最常见的方式。轮询分为短轮询和长轮询。短轮询非常简单。让我们用一张图片展示一下:
客户端向服务器请求数据,服务器立即将数据返回给客户端。如果客户端没有得到想要的数据(例如,返回结果告诉客户端,数据正在处理中),客户端继续发送请求,服务器继续立即重复响应。
这种实时数据采集的方法比较粗糙,优点是编程简单,客户端发送请求,服务器实时响应。主要有两个缺点:
无效请求很多,每一个无效请求都浪费了服务器的带宽和计算资源,给服务器带来了很大的压力。如果请求定期发送,并发性高,服务器可能会瞬间收到数千个请求,这很容易导致服务器宕机,甚至导致停机。短轮询适合什么样的使用场景?根据我的理解,如果数据变化频繁或者可以预期数据在短时间内变化一次,可以使用短轮询,例如:
用户在PC端购买了一些东西来唤醒网页端。因为PC端和网页端断开,我们预计用户应该很快就能完成支付。此时,为了开发简单的短轮询,可以使用它。直接服务器端提供接口告诉客户端订单状态,客户端每5秒钟可以请求一次,不需要请求结果。
使用短轮询时,注意控制请求数量的上限。比如请求100次后仍未检测到付款,可以弹出‘付款完成后请到我的订单页面查询’窗口,这样就不需要请求了。
长轮询
长轮询是实时获取数据的另一种方式。看看这个过程:
本质上没有变化,但是客户端一直向服务器发送请求,而没有收到它想要的数据。不同的是,服务器在收到请求时并不直接响应请求,而是挂起请求,定期判断数据变化,如果有变化就立即返回给客户端,一直等到超时。
可以清楚地看到,长轮询的优点是客户端请求少很多,避免不必要的客户端请求,缺点是服务器会挂起大量请求,增加资源消耗,并且服务器对并发HTTP请求的数量有限制。
微信网页版的登录就是长轮询的典型例子:
从图中可以看出,客户端一直向服务器发送请求,但是服务器在第一时间没有给出任何响应,所以客户端会等待,并在超时的情况下继续发送请求。
一般来说,我理解长轮询更常用,而短轮询更注重简单编程,适合小应用。就像微信网页登录一样,成千上万的用户同时登录,每隔一段时间,服务器就会收获成千上万的请求来处理,终究不是解决问题的办法。
WebSocket
上面介绍了两种轮询方法,但是两种方法结合起来都有明显的缺点。综上所述,有以下几点:
伪实时,即以上两种方法都不是实时的。无论短轮询的客户端轮询时间有多短,长轮询的服务器轮询时间有多短,都会有一定程度的延迟。只要没有需要返回的数据,所有的轮询都是对计算资源的浪费。HTTP协议本身就是一个重协议,每次都必须有一个HTTP头。其实我们需要的只是HTTP Body,冗余数据就是浪费带宽。因此,我们能做的最好的事情就是客户端和服务器之间有一个通道,当服务器数据发生变化时,服务器可以主动推送给客户端。WebSocket是HTML5之后诞生的协议。虽然是新协议,但也是基于HTTP协议。
看看WebSocket的原理,很简单:
WebSocket客户端首先通过HTTP协议向服务器发送几个特殊的头,告诉服务器我现在正在发起HTTP请求,但是我会升级到WebSocket:
Upgrade:websocket连接:升级Sec-websocket-key : XXX Sec-WebSocket-Protocol : chat,Super chat Sec-web socket-version : xx只要服务器支持web socket协议(Tomcat7和Jetty7之后支持web socket),那么在接收到请求并成功建立连接之后,服务器会向客户端返回两个头,Sec-WebSocket-Accept和Sec-WebSocket-Protocol,Http Status为101,表示协议
先说说WebSocket相对于长轮询和短轮询的带宽节省。有一个测试,假设HTTP Header是871字节。WebSocket在帧传输方面效率更高,因为数据传输是基于帧的。与轮询相比,2字节可以替代871字节的Header。测试结果如下:
在每秒客户端轮询次数相同的情况下,当次数高达10W/s时,轮询需要消耗665Mbps,而WebSocket只消耗1.526Mbps,接近435倍。
WebSocket实现了实时性,节省了大量的带宽资源,但我理解它也有自己的问题,那就是开发成本比较高。这里的开发成本并不意味着要自己实现WebSocket,而是直接在Java语言层面上使用Netty-Socketio。该应用编程接口非常简单,提供了一个完整的网络套接字实现。真正的开发成本在于分布式环境下的数据同步。
比如在线聊天系统,10W个人同时在线。此时,用户发送1K语音消息。单台机器保持10W连接是可以的(这不是HTTP请求,所以不受连接池数量的影响)。问题在于带宽。单台机器同时向10W用户推送1K条语音消息,至少需要10M的带宽。这只是单纯的数据推送,没有考虑数据进来的场景,在实际操作过程中会需要更多的带宽,这对企业来说是一个非常大的成本。
因此,在大量连接的情况下,将进行集群(事实上,即使没有大量连接,也会为了高可用性而进行),5台机器将以10W分布,每台机器平均有2W连接。考虑群集下将出现的问题:
客户端1向服务器1发送数据,所有连接到服务器1的客户端都可以推送语音,但问题在于:
从服务器2到服务器5的所有客户端如何获取数据?一种简单的方法是使用消息队列,并通过消息队列将数据发送到所有订阅的服务器。如果传输的是1M图片,数据太大,无法使用消息队列,可以先存储数据,消息队列只发送id,接收消息的服务器再根据id获取真实数据并推送。如果您依赖消息队列,您不仅需要开发应用程序代码。还需要在消息服务器上进行分布式集群和压力测试,以确保高可用性的2W连接通常能够发送1K条消息。但万一用户发送1M图片,导致远超预估带宽,则没有列出超过XXX的数据能否在业务上发送,或者技术上需要考虑的其他问题太多。总之,在使用WebSocket的大量请求和高并发的场景下,代码开发成本非常高。但是由于WebSocket可以将实时数据从服务器推送到客户端,节省了大量的带宽资源,所以很多应用如IM、音视频、弹幕等都会使用WebSocket。
摘要
以上就是本文的全部内容。希望本文的内容对大家的学习或工作有一定的参考价值。有问题可以留言交流。谢谢你的支持。