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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • kafka队列模式_redis消息队列和mq

    kafka队列模式_redis消息队列和mq一、消息队列概述消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。目前使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ二、消息队列应用场景以下介绍消息队列在实际应用中常用的使用场景。异步处理,应用解耦,流量削锋和消息通讯四个场景。2.1异步处理场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种1.串行的方式;2.并行方式a、串行方式:将注册

    2022年10月9日
    3
  • 校验json格式的工具_校验码计算工具

    校验json格式的工具_校验码计算工具在线JSON格式化校验工具

    2025年7月7日
    6
  • idea入门与实战(实战训练)

    工作中多人使用版本控制软件协作开发,常见的应用场景归纳如下:假设小组中有两个人,组长小张,组员小袁场景一:小张创建项目并提交到远程Git仓库场景二:小袁从远程Git仓库上获取项目源码场景三:小袁修改了部分源码,提交到远程仓库场景四:小张从远程仓库获取小袁的提交场景五:小袁接受了一个新功能的任务,创建了一个分支并在分支上开发场景六:小袁把分支提交到远程Git仓库场景七…

    2022年4月11日
    51
  • 公众号微信平台开发_订阅号 小程序

    公众号微信平台开发_订阅号 小程序微信公众号应用开发

    2022年8月21日
    7
  • leetcode-1840. 最高建筑高度

    leetcode-1840. 最高建筑高度在一座城市里,你需要建 n 栋新的建筑。这些新的建筑会从 1 到 n 编号排成一列。这座城市对这些新建筑有一些规定:每栋建筑的高度必须是一个非负整数。第一栋建筑的高度 必须 是 0 。任意两栋相邻建筑的高度差 不能超过 1 。除此以外,某些建筑还有额外的最高高度限制。这些限制会以二维整数数组 restrictions 的形式给出,其中 restrictions[i] = [idi, maxHeighti] ,表示建筑 idi 的高度 不能超过 maxHeighti 。题目保证每栋建筑在 res

    2022年8月9日
    6
  • matlab的meshgrid函数详解

    matlab的meshgrid函数详解函数形式[C,R]=meshgrid(c,r)初步解释首先需要明确的是参数c,r都是行向量,该函数将行向量c,r指定的域变换为数组C,R,这2个数组能用来指示有2个变量的函数和三维的图。输出数组C的每一行都是行向量c,输出数组R的每一列都是行向量r。例如我们需要形成一个二维函数,其元素是由坐标变量x和y的值的平方和。也就是f(x,y)=x^2+y^2这样的形式…

    2022年6月7日
    69

发表回复

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

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