说明:此博客源自我在python-cn邮件群组中对网友关于Comet的问题的回答,特整理下供更多需要了解的人参考。
- 轮询(Polling)是指不管服务器端有没有更新,客户端(通常是指浏览器)都定时的发送请求进行查询,轮询的结果可能是服务器端有新的更新过来,也可能什么也没有,只是返回个空的信息。不管结果如何,客户端处理完后到下一个定时时间点将继续下一轮的轮询。
- 推送或叫长连接(Long-Polling)的服务其客户端是不做轮询的,客户端在发起一次请求后立即挂起,一直到服务器端有更新的时候,服务器才会主动推送信息到客户端。 在服务器端有更新并推送信息过来之前这个周期内,客户端不会有新的多余的请求发生,服务器端对此客户端也啥都不用干,只保留最基本的连接信息,一旦服务器有更新将推送给客户端,客户端将相应的做出处理,处理完后再重新发起下一轮请求。
举个例子说明下就很清楚了:
- 轮询模式,假设是客户端每2秒轮询一次,那么客户端每2秒就会发送一次请求,相应的服务器端每2秒就要响应这个客户端的一次请求。而实际上服务器端可能1秒钟后就有更新,也可能1分钟后才有更新。对于1秒钟就有更新的,客户端至少会有1秒钟的延时;而1分钟后才有更新的,只有最后一次查询有意义,这一分钟内的轮询其实都是没有必要的,服务器端和客户端均有资源的浪费。
- 推送模式,客户端发送一次请求后马上挂起等待服务器端响应,可能1秒,也可能10秒钟,也可能1分钟。如果服务器端是1秒就有更新,那么到1秒钟时客户端马上就收到更新了,如果是1分钟才有更新,那么整个一分钟客户端也只请求一次,服务器也只会相应一次,这个跟轮询的区别是不是已经很清楚了。
这两天在公司的项目里用了 Long polling,了解了它的实现原理,其实不像它的名字那么玄乎,只是 Ajax 和 HTTP 的类似小妙招的办法。
先解释一下 Long polling 是什么:
首先得说到传统的 Polling,Polling 是 Ajax 隔一段时间去抓取服务器上的数据,检查数据是否更新,但这样有很大问题,首先是每次请求会实用一个 HTTP request,对应的服务器就得建一个新的线程来处理这个 HTTP request,消耗网络流量、服务器资源不说,绝大多数情况下,数据短时间内是不会更新的,也就是说绝大多数的 Ajax 请求都只能无功而返。
而 Long polling 就是用来解决这个问题的。
它的核心是:
1. 做一个超时非常长的 Ajax 请求,并且在错误捕获代码里不断执行自己。2. CGI 部分接收到请求后在限定的时间内(Ajax 超时时间内)每隔一段时间(例如一秒)对数据库进行查询,可以使用 sleep 类似的方法。
3. 如果有新的数据则返回,如果即将到 Ajax 超时的时间则返回一个错误值,比如 404,这样那个非常长的 Ajax 请求会再发一个过来,继续查询。
这个办法的最大价值是有效减少了 HTTP 请求数,对服务器而言就不用开启新的线程去处理它,旧的线程如果不是因为超时,则只会在数据已经更新的情况下返回数据。可以节约大量资源,而且实时性更高。
目前,人人网的消息提示、Web 版阿里旺旺、新浪微博的新微博提示应该都是使用这种方法做的。
但这个办法对开发有一定困扰,Django 内置的 Web service 是单线程的,一个非常长的 Ajax 请求会占用那唯一一个线程,导致别的请求无法响应。
所以最好做好两套配置,一套用于产品环境的多线程环境,一个用于开发的单线程环境。
我依然对那位在现有架构下想出这种办法的人表示钦佩。
这儿有范例代码,简单又实用:http://stackoverflow.com/questions/333664/simple-long-polling-example-code