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

涤生-微信公共号

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • WPF 教程(wpf架构)

    WPF介绍了一个非常方便的概念:把数据储存为一种资源,无论是本地控件、本地窗口还是全局应用。数据可以是任何你想要的东西,从实际的信息到WPF控件的层次结构都行。这非常有用,你可以把数据放在一个地方,然后在其他地方调用它。这个概念被广泛用在样式和模版,我们后面会详细讲到。也可以用在很多别的地方,就像本章要说明的地方,例子如下:

  • 来自科学网博主的问候

    来自科学网博主的问候

  • 条件运算符嵌套_条件运算符(?:)的运算过程是什么

    条件运算符嵌套_条件运算符(?:)的运算过程是什么条件运算符(?:)都知道就不细说了,最简单也是最常用的例子:n=(a>b)?a:b;    在C++primerplus第六版180页看到了一个例子,如下所示:    constcharx[2][20]={“Jason”,”atyourservices\n”}    constchar*y=”Quillstone”;

  • Word域代码:TOA,自动生成目录

    Word域代码:TOA,自动生成目录 示例:TOC/o”1-3″/h/z/u———————————————————————–域代码:TOA(引文目录)域域代码:TOA(引文目录)域{TOC[Switches]}建立一个目录。TOC域根据标题级别、指定样式或由TC(目录项)域指定的项目来收集目录项。在使用“插入”

  • AssetManager asset的使用

    AssetManager asset的使用

  • MGN网络详解以及代码分析「建议收藏」

    MGN网络详解以及代码分析「建议收藏」MGN网络详解以及代码分析最近阅读了云从科技最新的关于REID的论文以及相关的博客和代码,算法是基于MGN,关于网络的部分,这里记录一些自己的学习笔记。以下是我参考的博客和代码的网址博客:https://blog.csdn.net/Gavinmiaoc/article/details/80840193代码:https://github.com/Gavin666Github/reid-m…

发表回复

您的电子邮箱地址不会被公开。

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