java 实现http长轮询,Long Polling长轮询实现进阶「建议收藏」

java 实现http长轮询,Long Polling长轮询实现进阶「建议收藏」LongPolling长轮询实现进阶简书涤生。转载请注明原创出处,谢谢!如果读完觉得有收获的话,欢迎点赞加关注。介绍由于LongPolling长轮询详解这篇文章中的code实现较为简单,尤其是服务端处理较为粗暴,有一些同学反馈希望服务端处理阻塞这块内容进行更深入讨论等等,所以这里专门补一篇实现进阶,让大家对长轮询有更加深刻的理解。疑问对上篇文章,同学反馈有两个疑问。服务端实现使用的是同…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

Long Polling长轮询实现进阶

简书 涤生。

转载请注明原创出处,谢谢!

如果读完觉得有收获的话,欢迎点赞加关注。

介绍

由于Long Polling长轮询详解 这篇文章中的code实现较为简单,尤其是服务端处理较为粗暴,有一些同学反馈希望服务端处理阻塞这块内容进行更深入讨论等等,所以这里专门补一篇实现进阶,让大家对长轮询有更加深刻的理解。

疑问

对上篇文章,同学反馈有两个疑问。

服务端实现使用的是同步servlet,性能比较差,能支撑的连接数比较少?

同步servlet来hold请求,确实会导致后续请求得不到及时处理,servlet3.0开始支持异步处理,可以更高效的处理请求。

服务端如何去hold住请求,sleep好吗?

同步servlet hold住请求的处理逻辑必须在servlet的doGet方法中,一般先fetch数据,准备好了,就返回,没准备好,就sleep片刻,再来重复。

异步servlet hold住请求比较简单,只要开启异步,执行完doGet方法后,不会自动返回此次请求,需要等到请求的context被complete,这样很巧妙的请求就自动hold住了。

实现

客户端实现

客户端实现基本和上篇差不多,没什么改变。

package com.andy.example.longpolling.client;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.net.HttpURLConnection;

import java.net.URL;

import java.util.concurrent.atomic.AtomicLong;

/**

* Created by andy on 17/7/8.

*/

public class AbstractBootstrap {

//同步URL

protected static final String URL = “http://localhost:8080/long-polling”;

//异步URL

protected static final String ASYNC_URL = “http://localhost:8080/long-polling-async”;

private final AtomicLong sequence = new AtomicLong();

protected void poll() {

//循环执行,保证每次longpolling结束,再次发起longpolling

while (!Thread.interrupted()) {

doPoll();

}

}

protected void doPoll() {

System.out.println(“第” + (sequence.incrementAndGet()) + “次 longpolling”);

long startMillis = System.currentTimeMillis();

HttpURLConnection connection = null;

try {

URL getUrl = new URL(URL);

connection = (HttpURLConnection) getUrl.openConnection();

//50s作为长轮询超时时间

connection.setReadTimeout(50000);

connection.setConnectTimeout(3000);

connection.setRequestMethod(“GET”);

connection.setUseCaches(false);

connection.setDoOutput(true);

connection.setDoInput(true);

connection.setRequestProperty(“Content-Type”, “application/json;charset=UTF-8”);

connection.setRequestProperty(“Accept-Charset”, “application/json;charset=UTF-8”);

connection.connect();

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) {

System.out.println(“request failed”);

} finally {

long elapsed = (System.currentTimeMillis() – startMillis) / 1000;

System.out.println(“connection close” + ” ” + “elapse ” + elapsed + “s”);

if (connection != null) {

connection.disconnect();

}

System.out.println();

}

}

}

package com.andy.example.longpolling.client;

import java.io.IOException;

/**

* Created by andy on 17/7/6.

*/

public class ClientBootstrap extends AbstractBootstrap {

public static void main(String[] args) throws IOException {

ClientBootstrap bootstrap = new ClientBootstrap();

//发起longpolling

bootstrap.poll();

System.in.read();

}

}

服务端实现

长轮询服务端同步servlet处理

服务端同步servlet和上篇差不多,没什么改动,增加了相关注释。

package com.andy.example.longpolling.server;

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 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.

*/

@WebServlet(urlPatterns = “/long-polling”)

public class LongPollingServlet extends HttpServlet {

private final Random random = new Random();

private final AtomicLong sequence = new AtomicLong();

private final AtomicLong value = new AtomicLong();

@Override

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

System.out.println();

final long currentSequence = sequence.incrementAndGet();

System.out.println(“第” + (currentSequence) + “次 longpolling”);

//由于客户端设置的超时时间是50s,

//为了更好的展示长轮询,这边random 100,模拟服务端hold住大于50和小于50的情况。

//再具体场景中,这块在具体实现上,

//对于同步servlet,首先这里必须阻塞,因为一旦doGet方法走完,容器就认为可以结束这次请求,返回结果给客户端。

//所以一般实现如下:

// while(结束){ //结束条件,超时或者拿到数据

// data = fetchData();

// if(data == null){

// sleep();

// }

// }

int sleepSecends = random.nextInt(100);

System.out.println(currentSequence + ” wait ” + sleepSecends + ” second”);

try {

TimeUnit.SECONDS.sleep(sleepSecends);

} catch (InterruptedException e) {

}

PrintWriter out = response.getWriter();

long result = value.getAndIncrement();

out.write(Long.toString(result));

out.flush();

}

}

长轮询服务端异步servlet处理

由于同步servlet,性能较差,所有的请求操作必须在doGet方法中完成,包括等待数据,占用了容器的处理线程,会导致后续的请求阻塞,来不及处理。servlet 3.0支持异步处理,使用异步处理doGet方法执行完成后,结果也不会返回到客户端,会等到请求的context被complete才会写回客户端,这样一来,容器的处理线程不会受阻,请求结果可由另外的业务线程进行写回,也就轻松实现了hold操作。

package com.andy.example.longpolling.server;

import javax.servlet.*;

import javax.servlet.annotation.WebServlet;

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.*;

import java.util.concurrent.atomic.AtomicLong;

/**

* Created by andy on 17/7/7.

*/

/**

* 开启异步servlet,asyncSupported = true

*/

@WebServlet(urlPatterns = “/long-polling-async”, asyncSupported = true)

public class LongPollingAsyncServlet extends HttpServlet {

private Random random = new Random();

private final AtomicLong sequence = new AtomicLong();

private final AtomicLong value = new AtomicLong();

private static ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L,

TimeUnit.MILLISECONDS, new ArrayBlockingQueue(100));

@Override

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

System.out.println();

final long currentSequence = sequence.incrementAndGet();

System.out.println(“第” + (currentSequence) + “次 longpolling async”);

//设置request异步处理

AsyncContext asyncContext = request.startAsync();

//异步处理超时时间,这里需要注意,jetty容器默认的这个值设置的是30s,

//如果超时,异步处理没有完成(通过是否asyncContext.complete()来进行判断),将会重试(会再次调用doGet方法)。

//这里由于客户端long polling设置的是50s,所以这里如果小于50,会导致重试。

asyncContext.setTimeout(51000);

asyncContext.addListener(new AsyncListener() {

@Override

public void onComplete(AsyncEvent event) throws IOException {

}

//超时处理,注意asyncContext.complete();,表示请求处理完成

@Override

public void onTimeout(AsyncEvent event) throws IOException {

AsyncContext asyncContext = event.getAsyncContext();

asyncContext.complete();

}

@Override

public void onError(AsyncEvent event) throws IOException {

}

@Override

public void onStartAsync(AsyncEvent event) throws IOException {

}

});

//提交线程池异步写会结果

//具体场景中可以有具体的策略进行操作

executor.submit(new HandlePollingTask(currentSequence, asyncContext));

}

class HandlePollingTask implements Runnable {

private AsyncContext asyncContext;

private long sequense;

public HandlePollingTask(long sequense, AsyncContext asyncContext) {

this.sequense = sequense;

this.asyncContext = asyncContext;

}

@Override

public void run() {

try {

//通过asyncContext拿到response

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

int sleepSecends = random.nextInt(100);

System.out.println(sequense + ” wait ” + sleepSecends + ” second”);

try {

TimeUnit.SECONDS.sleep(sleepSecends);

} catch (InterruptedException e) {

}

long result = value.getAndIncrement();

out.write(Long.toString(result));

} catch (Exception e) {

System.out.println(sequense + “handle polling failed”);

} finally {

//数据写回客户端

asyncContext.complete();

}

}

}

}

结果

同步servlet实现结果

6e90c2f2e463

同步servlet客户端结果

6e90c2f2e463

同步servlet服务端结果

异步servlet实现结果

6e90c2f2e463

异步servlet客户端结果

6e90c2f2e463

异步servlet服务端结果

个人微信公共号,感兴趣的关注下,获取更多技术文章

6e90c2f2e463

涤生-微信公共号

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/183558.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • ASP.NET MVC商城网站后台管理系统

    ASP.NET MVC商城网站后台管理系统本项目使用了大量的插件,所有的商品数据皆为动态加载,全部从数据库中读取呈现在界面上,具备商品管理、用户管理等功能,还可查看商品的相关数据汇总。本项目对应的网上商城在上一篇文章中。界面展示(部分)代码太多了,就不进行部分展示了。…

    2022年9月27日
    5
  • navicat在线生成激活码【最新永久激活】

    (navicat在线生成激活码)最近有小伙伴私信我,问我这边有没有免费的intellijIdea的激活码,然后我将全栈君台教程分享给他了。激活成功之后他一直表示感谢,哈哈~https://javaforall.net/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~83PV…

    2022年3月27日
    328
  • 【罗盘时钟—使用html,js,css编写。附源代码及效果】

    【罗盘时钟—使用html,js,css编写。附源代码及效果】代码罗盘效果.html<!DOCTYPEhtml><htmllang=”en”><head><metacharset=”UTF-8″><metaname=”viewport”content=”width=device-width,initial-scale=1.0″><metahttp-equiv=”X-UA-Compatible”content=”ie=edge”><title>js罗盘时钟

    2022年6月28日
    33
  • platform device和driver之间的关系

    platform device和driver之间的关系[c-sharp] viewplaincopy内核中的platform driver机制需要将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform device提供的标准接口进行申请并使用。这样可以提高驱动和资源管理的独立性。本文的目的就是希望弄清楚platform device和driver之间的关系。  1.1

    2022年7月24日
    7
  • oracle基本面试题_mongodb面试题

    oracle基本面试题_mongodb面试题1.Oracle跟SQLServer2005的区别?宏观上:1).最大的区别在于平台,oracle可以运行在不同的平台上,sqlserver只能运行在windows平台上,由于windows平台的稳定性和安全性影响了sqlserver的稳定性和安全性2).oracle使用的脚本语言为PL-SQL,而sqlserver使用的脚本为T-SQL微观上:从数据类型,数据库…

    2022年8月29日
    3
  • mysql-创建用户报错ERROR 1396 (HY000): Operation CREATE USER failed for ‘XXXX’@’XXXX’

    mysql-创建用户报错ERROR 1396 (HY000): Operation CREATE USER failed for ‘XXXX’@’XXXX’创建用户:createuser‘test’@’%’identifiedby‘test’;显示ERROR1396(HY000):OperationCREATEUSERfailedfor‘test’@’%’查看是不是存在这个用户selectuserfromuser;发现没有这个用户。记得上次有删除过这个用户。可能没有刷新权限flushpriv

    2022年8月12日
    8

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号