springMvc异步的DeferredResult long polling应用示例解析

目录
  • 1.了解servlet以及springmvc中的异步
  • 2.简述polling和longpolling的区别
  • 3.因为DeferredResult,所以longpolling
  • 4.简单的测试用例
    • 1.定义异步接口
    • 2.定义接口访问实例,使用fegin
    • 3.测试用例

1.了解servlet以及spring mvc中的异步

Spring MVC 3.2开始引入了基于Servlet 3的异步请求处理。相比以前,控制器方法已经不一定需要返回一个值,而是可以返回一个java.util.concurrent.Callable的对象,并通过Spring MVC所管理的线程来产生返回值。与此同时,Servlet容器的主线程则可以退出并释放其资源了,同时也允许容器去处理其他的请求。通过一个TaskExecutor,Spring MVC可以在另外的线程中调用Callable。当Callable返回时,请求再携带Callable返回的值,再次被分配到Servlet容器中恢复处理流程。以下代码给出了一个这样的控制器方法作为例子:

@RequestMapping(method=RequestMethod.POST)
public CallableprocessUpload(final MultipartFile file) {
    return new Callable() {
        public String call() throws Exception {
            // ...
            return "someView";
        }
    };
}

另一个选择,是让控制器方法返回一个DeferredResult的实例。这种场景下,返回值可以由任何一个线程产生,也包括那些不是由Spring MVC管理的线程。举个例子,返回值可能是为了响应某些外部事件所产生的,比如一条JMS的消息,一个计划任务,等等。以下代码给出了一个这样的控制器作为例子:

@RequestMapping("/quotes")
@ResponseBody
public DeferredResultquotes() {
    DeferredResultdeferredResult = new DeferredResult();
    // Save the deferredResult somewhere..
    return deferredResult;
}
    // In some other thread...
    deferredResult.setResult(data);

如果对Servlet 3.0的异步请求处理特性没有了解,理解这个特性可能会有点困难。因此,阅读一下前者的文档将会很有帮助。

以下给出了这个机制运作背后的一些原理:

一个servlet请求ServletRequest可以通过调用request.startAsync()方法而进入异步模式。这样做的主要结果就是该servlet以及所有的过滤器都可以结束,但其响应(response)会留待异步处理结束后再返回调用request.startAsync()方法会返回一个AsyncContext对象,可用它对异步处理进行进一步的控制和操作。比如说它也提供了一个与转向(forward)很相似的dispatch方法,只不过它允许应用恢复Servlet容器的请求处理进程ServletRequest提供了获取当前DispatherType的方式,后者可以用来区别当前处理的是原始请求、异步分发请求、转向,或是其他类型的请求分发类型。

有了上面的知识,下面可以来看一下Callable的异步请求被处理时所依次发生的事件:

  • 控制器先返回一个Callable对象
  • Spring MVC开始进行异步处理,并把该Callable对象提交给另一个独立线程的执行器TaskExecutor处理
  • DispatcherServlet和所有过滤器都退出Servlet容器线程,但此时方法的响应对象仍未返回
  • Callable对象最终产生一个返回结果,此时Spring MVC会重新把请求分派回Servlet容器,恢复处理
  • DispatcherServlet再次被调用,恢复对Callable异步处理所返回结果的处理
  • 对DeferredResult异步请求的处理顺序也非常类似,区别仅在于应用可以通过任何线程来计算返回一个结果:
  • 控制器先返回一个DeferredResult对象,并把它存取在内存(队列或列表等)中以便存取
  • Spring MVC开始进行异步处理
  • DispatcherServlet和所有过滤器都退出Servlet容器线程,但此时方法的响应对象仍未返回
  • 由处理该请求的线程对 DeferredResult进行设值,然后Spring MVC会重新把请求分派回Servlet容器,恢复处理
  • DispatcherServlet再次被调用,恢复对该异步返回结果的处理

2.简述polling和long polling的区别

这里暂抛开某些场景webSocket的解决方案。

举一个生活中的列子来说明长轮询比轮询好在哪里:电商云集的时代,大家肯定都有查询快递的经历,怎么最快知道快递的进度呢?polling和long polling的方式分别如下:

  • polling:如果我想在两分钟内看到快递的变化,那么,轮询会每隔两分钟去像服务器发起一次快递变更的查询请求,如果快递其实是一个小时变更一次,那么polling的方式在获取一次真实有效信息时需要发起30次
  • long polling:首先发起查询请求,服务端没有更新的话就不回复,直到一个小时变更时才将结果返回给客户,然后客户发起下次查询请求。长轮询保证了每次发起的查询请求都是有效的,极大的减少了与服务端的交互,基于web异步处理技术,大大的提升了服务性能

如果在发散的触类旁通一下,long polling的方式和发布订阅的模式有点类似之处,只是每次拿到了发布的结果之后需要再次发起消息订阅

3.因为DeferredResult,所以long polling

因为DeferredResult技术,所以使得long polling不会一直占用容器资源,使得长轮询成为可能。长轮询的应用有很多,简述下就是:需要及时知道某些消息的变更的场景都可以用长轮询来解决,当然,你可能又想起了发布订阅了,哈哈

  • 比如:在线聊天?一个服务端,多个客户端,服务端管理所有的人的消息,客户端向服务端发起给自己的消息的请求,服务端处理后给返回,然后客户端再次发起?
  • 在比如类发布订阅的例子:配置中心服务,当配置中心的配置变更好,相关的客户端程序需要及时更新最新的配置。disconf就是基于zookeeper的发布订阅来做的,apollo就是采用的DeferredResult的long polling来做的,客户端发起长轮询,配置中心监听器监听到配置变更后,将结果响应给客户端。

apollo的具体做法可见

  • 服务端:com/ctrip/framework/apollo/configservice/controller/NotificationControllerV2.java
  • 客户端:com/ctrip/framework/apollo/internals/RemoteConfigLongPollService.java

4.简单的测试用例

多个请求的结果,使用另一个请求控制他的响应返回。本实例构建在spring boot 1.5.7上。

1.定义异步接口

/**
 * Created by kl on 2017/9/27.
 * Content :
 */
@RestController
@RequestMapping("/async")
public class AsyncController {
    final Map deferredResultMap=new ConcurrentReferenceHashMap<>();
    @GetMapping("/longPolling")
    public DeferredResultlongPolling(){
        DeferredResultdeferredResult=new DeferredResult(0L);
        deferredResultMap.put(deferredResult.hashCode(),deferredResult);
        deferredResult.onCompletion(()->{
            deferredResultMap.remove(deferredResult.hashCode());
            System.err.println("还剩"+deferredResultMap.size()+"个deferredResult未响应");
        });
        return deferredResult;
    }
    @GetMapping("/returnLongPollingValue")
    public void returnLongPollingValue(){
        for (Map.Entry entry:deferredResultMap.entrySet()){
            entry.getValue().setResult("kl");
        }
    }
}

2.定义接口访问实例,使用fegin

/**
 * Created by kl on 2017/9/27.
 * Content :
 */
@FeignClient(url = "localhost:8976",name = "async")
public interface AsyncFeginService {
    @GetMapping("/async/longPolling")
    String longPolling();
    @GetMapping("/async/returnLongPollingValue")
     void returnLongPollingValue();
}

3.测试用例

@RunWith(SpringRunner.class)
@SpringBootTest
public class LongPollingdemoApplicationTests {
	@Autowired
	AsyncFeginService asyncFeginService;
	/**
	 * 模拟多个浏览器客户端发起长轮询请求,等待testLongPolling测试用例请求通知服务端返回各浏览器的请求结果
	 * @throws Exception
	 */
	@Test
	public void contextLoads() throws Exception{
		ExecutorService executorService=Executors.newFixedThreadPool(4);
		for (int i=0;i<=3;i++){
			executorService.execute(()->{
				String kl=asyncFeginService.longPolling();
				System.err.println("收到响应:"+kl);
			});
		}
		System.in.read();
	}
	/**
	 * 通知服务端返回上个测试的长轮询结果
	 */
	@Test
	public void testLongPolling(){
		asyncFeginService.returnLongPollingValue();
	}
}

测试时,先启动contextLoads会发起四个异步请求,一直等待请求结果响应,直到testLongPolling通知服务端返回deferredResult的值。

以上就是springMvc中DeferredResult的long polling应用示例解析内容,更多关于springMvc DeferredResult的long polling应用的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java 利用DeferredResult实现http轮询实时返回数据接口

    今天这篇文章呢,不难,其实是解答我一直以来心里的一个疑问.是这样的,之前看五八技术委员会主席沈剑老师的公众号架构师之路的一篇文章:http 如何像 tcp 一样实时的收消息,里面其中的一个方案是用 http 短连接轮询的方式实现"伪长连接".但是对于轮询,我们的第一反应肯定是有延时,但是标题不是说的是实时吗?当然我们可以把轮询的时长缩短一些,先不说这样大部分时间的轮询调用,可能都没消息返回,造成服务器资源浪费,轮询时间再短也是有延时啊,所以难道是伪实时?反正一般消息延时个三五秒,甚至十

  • 详解Spring DeferredResult异步操作使用场景

    目录 为什么使用DeferredResult? 场景 Demo代码 测试 1. 超时 2. 进行setResult 总结: 为什么使用DeferredResult? API接口需要在指定时间内将异步操作的结果同步返回给前端时: Controller处理耗时任务,并且需要耗时任务的返回结果时: 当一个请求到达API接口,如果该API接口的return返回值是DeferredResult,在没有超时或者DeferredResult对象设置setResult时,接口不会返回,但是Servlet容器线程

  • SpringBoot的DeferredResult案例:DeferredResult的超时处理方式

    DeferredResult的超时处理,采用委托机制,也就是在实例DeferredResult时给予一个超时时长(毫秒),同时在onTimeout中委托(传入)一个新的处理线程(我们可以认为是超时线程):当超时时间到来,DeferredResult启动超时线程,超时线程处理业务,封装返回数据,给DeferredResult赋值(正确返回的或错误返回的). 这个实例可以对上一个实例的代码稍作改动即可. 一.增加超时处理任务TimeOutWork package com.example; impor

  • SpringMVC异步处理操作(Callable和DeferredResult)

    官方文档中说DeferredResult和Callable都是为了异步生成返回值提供基本的支持.简单来说就是一个请求进来,如果你使用了DeferredResult或者Callable,在没有得到返回数据之前,DispatcherServlet和所有Filter就会退出Servlet容器线程,但响应保持打开状态,一旦返回数据有了,这个DispatcherServlet就会被再次调用并且处理,以异步产生的方式,向请求端返回值. 这么做的好处就是请求不会长时间占用服务连接池,提高服务器的吞吐量. Ca

  • springMvc异步的DeferredResult long polling应用示例解析

    目录 1.了解servlet以及springmvc中的异步 2.简述polling和longpolling的区别 3.因为DeferredResult,所以longpolling 4.简单的测试用例 1.定义异步接口 2.定义接口访问实例,使用fegin 3.测试用例 1.了解servlet以及spring mvc中的异步 Spring MVC 3.2开始引入了基于Servlet 3的异步请求处理.相比以前,控制器方法已经不一定需要返回一个值,而是可以返回一个java.util.concurre

  • Java +Tomcat + SpringMVC实现页面访问示例解析

    window7下Java环境安装记录:  一.安装Tomcat 1.下载tomcat 7.0,解压,无需安装,放置到目录:D:\apache-tomcat-7.0.90. 2.配置系统环境变量,CATALINA_BASE=D:\apache-tomcat-7.0.90,CATALINA_HOME=D:\apache-tomcat-7.0.90,在Path中新增"%CATALINA_HOME%\lib;%CATALINA_HOME%\bin"的环境变量. 3.进入D:\apache-to

  • springMVC拦截器HandlerInterceptor用法代码示例

    摘要:很多时候我们都会去修改其他同事的bug,甚至是已经离职的同事的bug,有时候我们点击页面去不着到后台对应的是哪个controller,针对这个问题,其实我们可以通过sprngmvc的拦截器来拦击用户的请求从而知道页面请求的是哪个class的哪个方法,当然这些打印日志信息肯能并不适合放在生产环境,或者这个拦截器也是非必要的.... 一.HandlerInterceptor用法 第一步:注册拦截器 <!-- 注册拦截器 --> <mvc:interceptors> <bea

  • 基于JavaScript的数据结构队列动画实现示例解析

    ###一 摘要 今天给大家介绍一个基于数据结构中的队列的一个动画,在实现这个动画之前呢,还是给大家讲讲,在JavaScript中我们如何实现一个队列. ###二 队列 队列是一种列表,不同的是队列只能在末尾插入元素,在队首删除元素.队列用于存储按顺序排列的数据.先进先出.这点和栈不一样,在栈中,最后入栈的元素反被优先处理.可以将队列想象成银行排队办理业务的人,排队在第一个的人先办理业务,其它人只能排着,直到轮到他们为止. 队列是一种先进先出(FIFO)的数据结构.队列被用在很多地方.比如提交操作

  • Java正则表达式的语法及示例解析

    1匹配验证-验证Email是否正确 Java | 复制 public static void main(String[] args) { // 要验证的字符串 String str = "service@xsoftlab.net"; // 邮箱验证规则 String regEx = "[a-zA-Z_]{1,}[0-9]{0,}@(([a-zA-z0-9]-*){1,}\\.){1,3}[a-zA-z\\-]{1,}"; // 编译正则表达式 Pattern pat

  • Oracle PL/SQL中异常高级特性示例解析

    PL/SQL(Procedural Language/SQL,过程语言/SQL)是结合了Oracel过程语言和结构化查询语言(SQL)的一种扩展语言. 优点: (1)PL/SQL具有编程语言的特点,它能把一组SQL语句放到一个模块中,使其更具模块化种序的特点. (2)PL/SQL可以采用过程性语言控制程序的结构. (3)PL/SQL有自动处理的异常处理机制. (4)PL/SQL程序块具有更好的可移植性,可移植到另一个Oracle数据库中. (5)PL/SQL程序减少了网络的交互,有助于提高程序性

  • java eclipse 中文件的上传和下载示例解析

    文件的上传与下载(一) 在实现文件上传和下载之前我们需要做一些准备工作,在Apache官网去下载文件上传下载的两个组件,下载链接这里给出:common-fileupload组件下载:http://commons.apache.org/proper/commons-fileupload/ common-io组件下载:http://commons.apache.org/proper/commons-io/根据自己需求下载对应版本 一.创建工程 将所需要的两个开发包导入到工程项目中如图: 二.代码编写

  • java基于spring boot本地上传图片示例解析

    前几天项目中刚好需要上传图片的需求,当时想的是用七牛云,因为我用七牛云也用了好几次,就是把图片上传到七牛云空间里面,数据库里面保存的是这张上传图片的url地址 那么页面访问也就很方便,考虑到项目部署的环境我就用了本地上传,不牵涉数据库的操作.我就花了半个小时写了个本地上传图片的小demo.非常的简单. 下面是需要的依赖 pom.xml文件: <?xml version="1.0" encoding="UTF-8"?> <project xmlns=

  • c++ vector 常用函数示例解析

    c++ vector 常用函数 Just like arrays, vectors use contiguous storage locations for their elements, which means that their elements can also be accessed using offsets on regular pointers to its elements, and just as efficiently as in arrays. But unlike ar

  • Java 在PDF中添加骑缝章示例解析

    骑缝章是用于往来业务合同,以确保合同真实.有效的印章加盖方法,是一种防范风险的重要方式.在Java程序中,可以通过使用工具来辅助加盖这种骑缝章. 工具:Free Spire.PDF for Java (免费版) 工具获取及jar文件导入: 方式1:通过官网下载jar包,并解压,手动导入lib文件夹下的Spire.Pdf.jar文件. 方式2:通过创建Maven程序,在pom.xml中配置maven仓库路径并指定Free Spire.PDF for Java 的依赖,配置完成后,在IDEA中,点击

随机推荐