长轮询长轮询详细信息
用简单的书冲洗学生。
转载请注明原始来源。 谢谢你。
读完之后觉得有收获的话,请称赞并关注。
介绍
众所周知,数据交换有两种模式:推送模式和抽取模式。
推送模式是客户端与服务端建立网络长度连接,服务端有相关数据,直接通过长连接通道推送至客户端。 其优点是及时,只要有数据更改,客户端就能立即感知; 另外,对客户端来说在逻辑上很简单,不需要在意有无数据这样的逻辑处理。 缺点是不知道客户端的数据消耗能力,数据可能会聚集在客户端,来不及处理。
抽取类型是客户端主动向服务端发出请求,并抽取相关数据。 好处是,此过程是客户端请求的,所以没有推送模式下的数据积压问题。 缺点是可能不及时。 对客户端来说,需要考虑有关数据抽取的逻辑、何时抽取、如何控制抽取频率等。
详细解答
说到长轮询,必然要提到轮询。 这两种方式都是拉动模式。
轮询是指客户端每隔一段时间请求提取数据(无论是否更新服务端数据),可以返回更新数据,也可以什么都没有。
长轮询原理也很简单,客户端比轮询启动长轮询。 此时,如果服务端没有相关数据,则在服务端有相关数据或超时一定时间之前保持请求。 返回后,客户端将立即再次开始下一次长轮询。 该方案也是拉模式优化,解决了拉模式数据通知的延迟,减少了大量的无效轮询次数。 (所谓的hold居住请求。服务端暂时不响应结果,保存相关请求,不关闭请求连接,准备好相关数据后写入客户端。 )
如果服务端没有所需的相关数据,上述长轮询会挂起请求,直到服务端准备相关数据,或者直到这次请求超时。 为什么要等到服务端的数据准备好才回来呢? 这样可以节省资源,而不需要重新启动下一个长轮询。
主要原因是网络传输层主要走tcp协议,tcp协议是可信的面向连接的协议,通过三次握手建立连接。 但是,所建立的连接是虚拟的,可能在一段时间内网络不通,服务端程序可能无法正常结束,服务端计算机可能无法正常关闭。 在这种情况下,客户端不知道服务器此时无法互操作,而是傻傻地等待服务器发送数据,这通常是很长时间。 当然,在实现上,tcp协议栈由保持活动计时器保证,但需要很长时间才意识到保持活动计时器断开了连接,尤其是在没有配置tcp参数的情况下,通常需要两个小时。 另外,由于这些参数是机器操作系统级别的,所以这样保证保持活跃是不太可靠的,所以Long Polling的实现需要设置超时时间。
实现
长轮询的实现很简单,可分为四个过程:
开始轮询
启动轮询很简单,只是向服务发出请求,而服务端尚未响应,因此客户端和服务端之间始终连接。
推送数据
如果服务器端有相关数据,则服务器端通过以前建立的通道将数据发送回客户端。
轮询结束
轮询的结束情况有三种。
如果服务端返回相关数据,则客户端在收到数据后将关闭请求连接,并终止此轮询过程。
如果客户端等待配置的超时时间,但服务端没有返回数据,则客户端必须主动退出轮询请求。
如果客户端收到网络故障或异常,客户端当然也需要积极停止这次轮询请求。
重新轮询
退出上一次轮询后,客户端必须立即再次启动轮询请求。 这样,就可以保证数据的及时性。
代码实现也很简单,http调用通过执行上述步骤可以很容易地实现长轮询。 以下代码只是简单地演示过程,在具体场景中,根据具体的业务逻辑进行调整。
客户端代码
package com.Andy.example.long polling.client;
import java.io.BufferedReader;
import java.io.IOException;
import Java.io.input streamreader;
import Java.net.httpurl connection;
import java.net.URL;
//*
Created by andy on 17/7/6。
*/
公共类客户端引导程序{
publicstaticfinalstringurl=' http://localhost :8080/long-polling ';
publicstaticvoidmain (字符串[ ] args ) {
int i=0;
while (真)。
System.out.println ('第' (I ) )次
longpolling");HttpURLConnection connection = null;
try {
URL getUrl = new URL(URL);
connection = (HttpURLConnection) getUrl.openConnection();
connection.setReadTimeout(50000);//这就是等待时间,设置为50s
connection.setConnectTimeout(3000);
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept-Charset", "utf-8");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Charset", "UTF-8");
if (200 == connection.getResponseCode()) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
StringBuilder result = new StringBuilder(256);
String line = null;
while ((line = reader.readLine()) != null) {
result.append(line);
}
System.out.println("结果 " + result);
} finally {
if (reader != null) {
reader.close();
}
}
}
} catch (IOException e) {
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}
}
服务端Code
package com.andy.example.longpolling.server;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
Created by andy on 17/7/6.
*/
public class LongPollingServlet extends HttpServlet {
private Random random = new Random();
private AtomicLong sequenceId = new AtomicLong();
private AtomicLong count = new AtomicLong();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("第" + (count.incrementAndGet()) + "次 longpolling");
int sleepSecends = random.nextInt(100);
//随机获取等待时间,来通过sleep模拟服务端是否准备好数据
System.out.println("wait " + sleepSecends + " second");
try {
TimeUnit.SECONDS.sleep(sleepSecends);//sleep
} catch (InterruptedException e) {
}
PrintWriter out = response.getWriter();
long value = sequenceId.getAndIncrement();
out.write(Long.toString(value));
}
}
应用
WebQQ、Comet都用到长轮询技术,另外一些使用Pull模式消费的消息系统,都会使用Long Polling技术进行优化。
补充
针对一些同学的反馈,补充一篇 Long Polling长轮询实现进阶,希望大家对长轮询理解更加深刻。
个人微信公共号 beijing-tmt ,感兴趣的关注下,获取更多技术文章