HTTP轮询模型

长短轮询

http协议是一种client-server模型的应用层协议,这种c-s的模式虽然大多数情况都能满足需求,但是某些场景也需要服务端能够将一些信息实时的推送到客户端,即实现服务器向客户端推消息的功能。

比如:

配置管理中心服务端需要将更新的配置推送到client端

消息推送服务器需要将一些消息推送到Android、iOS客户端

利用Http协议实现服务器推送有两种常见的思路:

短轮询拉

客户端不停的去向服务器发送轮询请求,如果有数据更新,客户端能也能尽快(取决于轮询间隔)获取最新的数据,这种方式被称为Http短轮询。

这种方式有如下缺点:

因为是短轮询,因此一定时间t内需要进行轮询的次数就更多,而Http的连接是需要tcp三次握手等资源开销的。

由图可以看出,但是服务端数据发生更新时,客户端并不是立刻收到更新的数据(除去网络传输仍然还需要时间),而只能是在下一次轮询的时候才能感知到数据的变更。

长轮询推

长轮询的思路是这样的:尽量减少轮询的次数,从而减少资源开销。为了减少轮询次数,那么每次轮询的时间跨度就需要比较长,因此成为长轮询,同时也希望长轮询模型的每一次轮询效率要高于短轮询。

长轮询模型有这么几个特征:

每次轮询的间隔不固定

服务器对每次轮询做出响应的条件是:超时或者数据更新

长轮询模型中,客户端能实时感知到服务器端数据更新

由于很多服务器都具有异步处理连接的能力,因此图中的阻塞消耗的资源比较小。

异步Servlet

下面是利用Servlet规范中提供的异步Servlet作为服务端的Http长轮询模型,实现了客户端能实时获取服务端某个配置文件内容。

异步Servlet是Servlet3.0出来的新特性,对于需要异步处理的连接,Servlet引擎会将处理该请求的工作线程回收进工作线程池,而不是阻塞在该请求上。

package httplongconnection;

import java.io.File;

import java.io.FileFilter;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.InputStream;

import java.io.PrintWriter;

import javax.servlet.AsyncContext;

import javax.servlet.AsyncEvent;

import javax.servlet.AsyncListener;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.digest.DigestUtils;

import org.apache.commons.io.monitor.FileAlterationListener;

import org.apache.commons.io.monitor.FileAlterationMonitor;

import org.apache.commons.io.monitor.FileAlterationObserver;

@WebServlet(urlPatterns = "/long", asyncSupported = true)

public class HttpLongConnectionServlet extends HttpServlet {

/**

*

*/

private static final long serialVersionUID = 1L;

static FileAlterationObserver observer;

static {

FileAlterationMonitor monitor = new FileAlterationMonitor(1000L);// 每隔1000毫秒扫描一次

// 需要监听的文件目录

observer = new FileAlterationObserver(new File("E:/J2EE_workspace/httplongconnection/src/main/resources"), new FileFilter() {

public boolean accept(File pathname) {

// TODO Auto-generated method stub

return true;

}

});

System.out.println("observer");

monitor.addObserver(observer);

try {

monitor.start();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

// TODO Auto-generated method stub

final AsyncContext ctx = req.startAsync();

ctx.addListener(new AsyncListener() {

public void onTimeout(AsyncEvent event) throws IOException {

// TODO Auto-generated method stub

ctx.complete();

observer.removeListener((FileAlterationListener)ctx.getRequest().getAttribute("fileListener"));

}

public void onStartAsync(AsyncEvent event) throws IOException {

// TODO Auto-generated method stub

}

public void onError(AsyncEvent event) throws IOException {

// TODO Auto-generated method stub

}

public void onComplete(AsyncEvent event) throws IOException {

// TODO Auto-generated method stub

observer.removeListener((FileAlterationListener)ctx.getRequest().getAttribute("fuck"));

}

});

ctx.setTimeout(50 * 1000);

new Thread(new BizProcessor(ctx)).start();

}

class BizProcessor implements Runnable {

private String checkSum;

private AsyncContext asyncContext;

private boolean checkSumEqual(InputStream is, String originalCheckSum) {

try {

String digest = DigestUtils.md5Hex(is);

if (digest.equals(originalCheckSum)) {

return true;

}

this.checkSum = digest;

System.out.println(

"File has changed. new md5 is " + this.checkSum + ", old checsum is " + originalCheckSum);

return false;

} catch (FileNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return false;

}

public BizProcessor(AsyncContext asyncContext) {

super();

this.asyncContext = asyncContext;

}

public void run() {

// TODO Auto-generated method stub

// sleep

HttpServletRequest req = (HttpServletRequest) asyncContext.getRequest();

String cSum = String.valueOf(req.getParameter("checkSum"));

// 文件update

InputStream is = null;

try {

is = new FileInputStream(new File("E:/J2EE_workspace/httplongconnection/src/main/resources/config.txt"));

} catch (FileNotFoundException e1) {

// TODO Auto-generated catch block

e1.printStackTrace();

}

if (is != null && !checkSumEqual(is, cSum)) {

try {

is.close();

is = this.getClass().getClassLoader().getResourceAsStream("config.txt");

PrintWriter out = asyncContext.getResponse().getWriter();

String content = org.apache.commons.io.IOUtils.toString(is, "UTF-8");

System.out.println(content);

out.write(checkSum + "\002" + content);

out.flush();

out.close();

is.close();

} catch (IOException e) {

e.printStackTrace();

}

asyncContext.complete();

}

System.out.println("register");

register(asyncContext);

// 没有发生文件更新,则等待超时发生

}

private void register(AsyncContext ctx) {

// TODO Auto-generated method stub

FileListerAdapter listner = new FileListerAdapter(ctx);

ctx.getRequest().setAttribute("fileListener", listner);

observer.addListener(listner);

}

}

}

Hello World!

poll();

function poll() {

$.ajax({

url: "/httplongconnection/long",

data : {"checkSum" : cSum},

success: function(response) {

if (response == null || response.length == 0) {

poll();

return;

}

var msg = response.split("\002");

var checkSum = md5(msg[1]), content = msg[1];

if (checkSum != cSum) {

cSum = checkSum;

$("#show").val(content);

}

poll();

}});

}

package httplongconnection;

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.PrintWriter;

import javax.servlet.AsyncContext;

import org.apache.commons.codec.digest.DigestUtils;

import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;

public class FileListerAdapter extends FileAlterationListenerAdaptor {

AsyncContext ctx;

public FileListerAdapter(AsyncContext ctx) {

super();

this.ctx = ctx;

}

@Override

public void onFileChange(File file) {

if (!file.exists() || !file.canRead()) {

System.out.println("The file " + file + " is not exists or is not readable!");

return;

}

try {

InputStream is = new FileInputStream(file);

if (ctx.getResponse().isCommitted()) {

return;

}

PrintWriter out = ctx.getResponse().getWriter();

String content = org.apache.commons.io.IOUtils.toString(is, "UTF-8");

System.out.println(content);

String digest = DigestUtils.md5Hex(is);

out.write(digest + "\002" + content);

System.out.println("yy");

System.out.println(content);

is.close();

out.flush();

out.close();

} catch (IOException e) {

e.printStackTrace();

}

ctx.complete();

//TODO 读取操作

super.onFileChange(file);

}

@Override

public void onFileCreate(File file) {

//TODO 读取操作

super.onFileCreate(file);

}

@Override

public void onFileDelete(File file) {

super.onFileDelete(file);

}

@Override

public void onDirectoryChange(File directory) {

System.out.println("----The directory " + directory + " has changed.");

super.onDirectoryChange(directory);

}

@Override

public void onDirectoryCreate(File directory) {

super.onDirectoryCreate(directory);

}

@Override

public void onDirectoryDelete(File directory) {

super.onDirectoryDelete(directory);

}

}

java 轮询http_HTTP轮询模型相关推荐

  1. java ajax轮询_ajax轮询(ajax轮询实现聊天)

    最近一直在研究ajax长轮询连实现即时通信,但是到底是个怎么轮询法?难道. 一般最原始的作法就是在客户端搞个定时器一直向后台请求,而ajax的长轮询与一般的http连接不一样,它发送的是长连接,比如说 ...

  2. 多线程下的生产者消费者(一个初始值为0的变量,两个线程一个加1一个减1,轮询5轮)

    在使用Lock之前,我们使用的最多的同步方式应该是synchronized关键字来实现同步方式了.配合Object的wait().notify()系列方法可以实现等待/通知模式.Condition接口 ...

  3. HTTP - 长连接 短连接 长轮询 短轮询 心跳机制

    错觉与突然的察觉 大多数人都知道HTTP1.0不支持长连接,知道HTTP1.1支持长连接. 这是业界的一个常识. 然而这样的描述导致了一些不做网络底层开发的开发者都下意识的认为HTTP1.1是一个可以 ...

  4. 计算机网络之数据链路层:13、令牌传递协议、轮询协议-轮询访问介质访问控制

    数据链路层:13.令牌传递协议.轮询协议-轮询访问介质访问控制 思维导图: 轮询协议: 令牌传递协议: 思维导图: 轮询协议: 主节点轮流向从节点发送一个较短的数据帧,询问从节点是否要向我发送数据: ...

  5. ajax使用频率,AJAX轮询频率 - 要长期轮询还是不轮询长轮询?

    我正在构建一个需要相对不变的数据库轮询的网页组件.我可以看到两种不同的方法,我想知道他们中的一个是否比其他人好,或者如果我错过了第三个选择.AJAX轮询频率 - 要长期轮询还是不轮询长轮询? 1)发送 ...

  6. php http长轮询,http长轮询短轮询

    http 协议介绍: http 协议是请求/响应范式的, 每一个 http 响应都是由一个对应的 http 请求产生的; http 协议是无状态的, 多个 http 请求之间是没有关系的. http ...

  7. mysql长轮询_【系列一】ajax长轮询、轮询应用和介绍

    前言 本文是系列文章,主要介绍客户端浏览器和服务器端的通信,当然,客户端和服务器端通信有很多方式.本系列文章主要是讲不间断通信方式!不间断通信就是通信没有停止,一直进行.系列一文章主要是讲轮询和长轮询 ...

  8. 扛过字节Java研发岗4轮面试,收到sp offer(月薪35k)!揭秘字节面试流程及考题(附带答案)

    3 轮技术面 + 1 轮 HR 面,他最终拿到了 35k*16薪 的 Offer. 第一轮主要考察 Java 基础,二.三轮注重对应技术的掌握,以及对过往项目的业务理解.之所以令他印象深刻,是因为每轮 ...

  9. 300小时成为java程序员_直击面试现场: Java程序员3轮6小时面试, 成功拿到阿里offer!...

    原标题:直击面试现场: Java程序员3轮6小时面试, 成功拿到阿里offer! 今天给大家分享一位Java程序员小伙去阿里应聘的经历! 从Java开发要掌握的技术来讲,前面已经说得差不多了.我主要想 ...

最新文章

  1. 有规律格式化文本文件插入数据库
  2. SSH: 使用ssh推送github代码
  3. php注入类,简单实用的PHP防注入类实例
  4. C语言a+++b的问题
  5. 单引号内的双引号内的双引号怎么写
  6. html5 filereader读取文件,H5的FileReader分布读取文件应该如何使用以及其方法简介...
  7. 跌落测试显示:iPhone 13/13 Pro耐用性和前代几乎相同
  8. Android SDK大连东软镜像地址及地址列表
  9. mac下的mysql报错:ERROR 1045(28000)和ERROR 2002 (HY000)的解决办法
  10. Python GUI程序整理
  11. C++描述 LeetCode 480. 滑动窗口中位数
  12. Balanced Multimodal Learning via On-the-fly Gradient Modulation论文笔记
  13. Arduino 学习思考与记录
  14. linux批量对文件改名,在Linux中对文件进行批量重命名文件的方法
  15. Docker安装PHP-FPM5.6 (自带redis扩展,Mysql扩展,GD库扩展(支持JEPG))
  16. can和could的用法_can 和 could 用法异同
  17. idea的Terminal的git提示密码,修改git配置文件配置
  18. 一分钟带你了解最真实的服务器
  19. W3C 推出去中心化标识符作为 Web 标准
  20. Android MVP框架MVPro的使用和源码分析

热门文章

  1. 【python教程】(4)python中的模块
  2. 终端零售行业为什么要选择会员管理系统
  3. 关于高校项目出差时的一些记录日志
  4. Attentional Graph Convolutional Networks for Knowledge Concept Recommendation in MOOCs in a Heter---
  5. 1.JAVA猜数字游戏: 一个类A有两个成员变量v、num,v有一个初值100。 定义一个方法guess,对A类的成员变量v,用num进行猜。 *如果num比v大则提示大了,反之则提示小了.
  6. java创建文件夹的4种方法及其优缺点(io基础)
  7. Ubuntu换Kali源
  8. 信息系统管理师、高级项目经理与计算机系统集成资质之间的关系
  9. 成功解决 Failed to load ApplicationContext No qualifying bean of type 问题
  10. 如何构建内部开发者门户:企业参考指南