Java SpringMVC异步处理详解

目录
  • 1、本篇内容
  • 2、看段代码,分析问题
  • 3、springmvc 中异步处理
  • 4、模拟非超时请求
  • 5、模拟超时请求
  • 6、总结

1、本篇内容

本文让大家掌握 springmvc 中异步处理请求,特别牛逼的一个功能,大家一定要掌握。

2、看段代码,分析问题

@ResponseBody
@RequestMapping("/async/m1.do")
public String m1() throws InterruptedException {
    long st = System.currentTimeMillis();
    System.out.println("主线程:" + Thread.currentThread() + "," + st + ",开始");
    //休眠3秒,模拟耗时的业务操作
    TimeUnit.SECONDS.sleep(3);
    long et = System.currentTimeMillis();
    System.out.println("主线程:" + Thread.currentThread() + "," + st + ",结束,耗时(ms):" + (et - st));
    return "ok";
}

这段代码很简单

这段代码是 springmvc 提供的一个接口

内部休眠了 3 秒钟,用来模拟耗时的操作

方法内部有 2 条日志(日志中包含了当前线程、开始时间、结束时间、耗时)

浏览器中访问下这个接口,效果如下,可以看到接口耗时 3s 左右。

控制台输出

主线程:Thread[http-nio-8080-exec-1,5,main],1624889293055,开始
主线程:Thread[http-nio-8080-exec-1,5,main],1624889293055,结束,耗时(ms):3002

从输出中,我们可以看出,这个接口从开始到结束都是由 tomcat 中的线程来处理用户请求的,也就是说,3 秒这段时间内,tomcat 中的一个线程会被当前请求一直占用了则,tomcat 线程是有最大值的,默认情况下好像是 75,那么问题来了。

当 3 秒之内,来的请求数量超过了 tomcat 最大线程数的时候,其他请求就无法处理了,而此时 tomcat 中这些线程都处理 sleep 3s 的休眠状态,cpu 此时没活干,此时就会造成机器没活干,但是呢又不能处理新的请求,这就是坑啊,浪费资源,怎么办呢?

遇到这种场景的,也就是说接口内部比价耗时,但是又不能充分利用 cpu 的,我们可以采用异步的方式来处理请求,过程如下:

tomcat 线程,将请求转发给我们自定义的子线程去处理这个请求,然后 tomcat 就可以继续去接受新的请求了。

3、springmvc 中异步处理

主要有 3 个大的步骤。

step1:servlet 开启异步处理支持

web.xml 中开启 servlet 异步支持

step2:Filter 中添加异步支持

如果我们的异步请求需要经过 Filter 的,那么需要在 web.xml 对这个 Filter 添加异步支持.

step3:接口返回值为 DeferredResult

这个步骤中细节比较多,当需要异步响应请求的时候,返回值需要为 DeferredResult,具体参考下面案例代码,详细信息都在注释中了,大家注意看注释。

第 1 步:创建 DeferredResult<返回值类型>(超时时间[毫秒],超时回调的代码)

第 2 步:在子线程中异步处理业务,调用 DeferredResult 的 setResult 方法,设置最终返回到客户端的结果,此方法调用以后,客户端将接收到返回值,然后响应过程请求就结束了

第 3 步:将 DefaultResult 作为方法返回值

/**
 * 使用springmvc的异步功能,业务处理放在异步线程中执行
 *
 * @param timeout 异步处理超时时间(毫秒)
 * @return
 */
@ResponseBody
@RequestMapping("/async/m2/{timeout}.do")
public DeferredResult m2(@PathVariable("timeout") long timeout) {
    long st = System.currentTimeMillis();
    System.out.println("主线程:" + Thread.currentThread() + "," + st + ",开始");
    /**
     * 1、创建DeferredResult<返回值类型>(超时时间[毫秒],超时回调的代码)
     */
    DeferredResult result = new DeferredResult(timeout, () -> {
        System.out.println("超时了");
        return "timeout";
    });
    //2、异步处理业务,
    new Thread(() -> {
        //开启一个异步线程,在异步线程中进行业务处理操作
        try {
            TimeUnit.SECONDS.sleep(3);
            //3、调用DeferredResult的setResult方法,设置最终返回到客户端的结果,此方法调用以后,客户端将接收到返回值
            result.setResult("ok");
        } catch (InterruptedException e) {
            result.setResult("发生异常了:" + e.getMessage());
        }
    }).start();
    long et = System.currentTimeMillis();
    System.out.println("主线程:" + Thread.currentThread() + "," + st + ",结束,耗时(ms):" + (et - st));
    //3、将DefaultResult作为方法返回值
    return result;
}

上面的 m2 方法个 timeout 参数,调用者通过这个参数来指定接口的超时时间,未超时的情况下,也就是说 timeout 大于 3 秒的时候,此时会输出 ok,否则将出现超时,此时会将 DeferredResult 构造器第 2 个参数的执行结果作为最终的响应结果,即会向客户端输出 timeout。

使用建议:案例开启了一个新的子线程来执行业务操作,生产环境中,建议大家采用线程池的方式,效率更高。

下面我们来通过 2 个 case 来模拟下这个接口超时和正常的结果。

4、模拟非超时请求

当 timeout 大于 3 秒时,才不会出现超时,此时我们传递 4000 毫秒来试试

控制台输出如下,可以看到主线程瞬间就结束了。

主线程:Thread[http-nio-8080-exec-6,5,main],1624891886020,开始
主线程:Thread[http-nio-8080-exec-6,5,main],1624891886020,结束,耗时(ms):0

5、模拟超时请求

当 timeout 小于 3 秒会出现超时,此时我们传递 1000 毫秒来试试

控制台输出如下,输出了超时信息,且通过前两行输出看出主线程瞬间就结束了,不会被请求阻塞。

主线程:Thread[http-nio-8080-exec-1,5,main],1624892109695,开始
主线程:Thread[http-nio-8080-exec-1,5,main],1624892109695,结束,耗时(ms):0
超时了

6、总结

当接口中有大量的耗时的操作,且这些耗时的操作让线程处于等待状态时,此时为了提升系统的性能,可以将接口调整为异步处理的方式。

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • Java SpringMVC 集成静态资源的方式你了解吗

    目录 1.本文内容 2.静态资源 & jsp 是被谁处理的? 3.如何让 springmvc 处理所有请求? 4.问题来了:静态资源 404 了 5.方案 1:使用 6.方案 2:使用 7.总结 1.本文内容 静态资源 & jsp 是被谁处理的? 如何让 springmvc 处理所有请求? springmvc 如何处理静态资源? springmvc 处理静态资源的 2 种方案.区别.原理详解 2.静态资源 & jsp 是被谁处理的? 不知大家是否注意了一个问题,我们将 web 项目

  • 一篇文章带你了解Java SpringMVC返回null

    目录 1.回顾一下 2.思考一个问题 3.springmvc 的处理流程 4.使用场景 5.总结 1.回顾一下 大家有没有注意到,目前讲到的所有 controller 中的方法接收到请求之后,都是有返回值的,返回值主要有 2 种类型: 1. 输出的是页面:也就是视图(会向客户端输出页面),此时方法的返回值可以是 String(视图名称).ModelAndView(页面中有数据的情况) 2.输出的是 json 格式的数据:需要用到@ResponseBody 注解 这 2 种情况中,都是 sprin

  • Java SpringMVC异常处理机制详解

    目录 异常处理的思路 测试环境准备 异常处理两种方式 方式一:简单异常处理器 方式二:自定义异常处理器 总结 异常处理的思路 测试环境准备 首先写一个DemoController控制层的类作为测试访问的控制器 package com.itheima.controller; import com.itheima.exception.MyException; import com.itheima.service.DemoService; import org.springframework.bean

  • Java SpringMVC自学自讲

    目录 SpringMVC 参考博客 MVC SpringMVC 概述 SpringMVC优点 创建maven工程 配置web.xml 创建请求控制器 创建springMVC的配置文件 测试HelloWorld 总结 @RequestMapping注解 @RequestMapping注解的params属性(了解) @RequestMapping注解的headers属性(了解) SpringMVC支持路径中的占位符(重点) @RequestParam @RequestHeader @CookieVa

  • Java Spring MVC获取请求数据详解操作

    目录 1. 获得请求参数 2. 获得基本类型参数 3. 获得POJO类型参数 4. 获得数组类型参数 5. 获得集合类型参数 6. 请求数据乱码问题 7. 参数绑定注解 @requestParam 8. 获得Restful风格的参数 9. 自定义类型转换器 1.定义转换器类实现Converter接口 2.在配置文件中声明转换器 3.在<annotation-driven>中引用转换器 10. 获得Servlet相关API 11. 获得请求头 11.1 @RequestHeader 11.2 @

  • Java SpringMVC 异常处理SimpleMappingExceptionResolver类详解

    Spring3.0 对异常的处理方式总共有两种: 一种是使用 HandlerExceptionResolver 接口,并且 Spring 已经提供默认的实现类 SimpleMappingExceptionResolver. 第二种方法是在 Controller 内部实现,灵活性更高. 从目前的调查结果来看,这两种方式不能共存.我们一般在项目中使用第一种方法. 下面分别描述一下这两种使用方式: 一.基于 HandlerExceptionResolver 接口的方式 使用这种方式只需要实现 reso

  • Java SpringMVC拦截器与异常处理机制详解分析

    目录 拦截器(interceptor)的作用 拦截器快速入门 案例:用户登录权限控制 拦截器方法说明 SpringMVC异常处理 异常处理的思路 异常处理两种方式 拦截器(interceptor)的作用 Spring MVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理. 将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain).在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用.拦截器也是AOP思

  • Java SpringMVC异步处理详解

    目录 1.本篇内容 2.看段代码,分析问题 3.springmvc 中异步处理 4.模拟非超时请求 5.模拟超时请求 6.总结 1.本篇内容 本文让大家掌握 springmvc 中异步处理请求,特别牛逼的一个功能,大家一定要掌握. 2.看段代码,分析问题 @ResponseBody @RequestMapping("/async/m1.do") public String m1() throws InterruptedException { long st = System.curre

  • java SpringMVC学习使用详解

    Controller学习(Spring官网) SpringMVC 标记 某些java代码使其成为Controller,从而这些代码具有和前端网页 交互数据的功能了. 在学习controller之前先把SpringMVC大致捋一下: 一.简介 在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示.在SpringMVC 中提供

  • java异步编程详解

    很多时候我们都希望能够最大的利用资源,比如在进行IO操作的时候尽可能的避免同步阻塞的等待,因为这会浪费CPU的资源.如果在有可读的数据的时候能够通知程序执行读操作甚至由操作系统内核帮助我们完成数据的拷贝,这再好不过了.从NIO到CompletableFuture.Lambda.Fork/Join,java一直在努力让程序尽可能变的异步甚至拥有更高的并行度,这一点一些函数式语言做的比较好,因此java也或多或少的借鉴了某些特性.下面介绍一种非常常用的实现异步操作的方式. 考虑有一个耗时的操作,操作

  • SpringMVC自定义属性编辑器详解及实例

    SpringMVC自定义属性编辑器详解及实例 自定义springMVC的属性编辑器主要有两种方式,一种是使用@InitBinder标签在运行期注册一个属性编辑器,这种编辑器只在当前Controller里面有效:还有一种是实现自己的 WebBindingInitializer,然后定义一个AnnotationMethodHandlerAdapter的bean,在此bean里面进行注册 ,这种属性编辑器是全局的. 第一种方式: import java.beans.PropertyEditorSupp

  • java回调机制实例详解

    java回调机制实例详解 以前不理解什么叫回调,天天听人家说加一个回调方法啥的,心里想我草,什么叫回调方法啊?然后自己就在网上找啊找啊找,找了很多也不是很明白,现在知道了,所谓回调:就是A类中调用B类中的某个方法C,然后B类中反过来调用A类中的方法D,D这个方法就叫回调方法,这样子说你是不是有点晕晕的,其实我刚开始也是这样不理解,看了人家说比较经典的回调方式: Class A实现接口CallBack callback--背景1 class A中包含一个class B的引用b --背景2 clas

  • Java基础 Servlet监听器详解

    Java基础 Servlet监听器详解 1 概念:Servlet监听器,用来监听web容器的一些对象状态的变化,主要是ServletContext.HttpSession.HttpServletRequestl三类对象状态.Servlet的监听器 2  Servlet2.4和JSP2.0规范中一共定义了有八个接口类和六种事件. 3 web.xml中定义Servlet的url-pattern时如果url-pattern的值的"/",则说明该Servlet是该项目的默认Servlet,当请

  • java多线程中断代码详解

    一.java中终止线程主要有三种方法: ①线程正常退出,即run()方法执行完毕了 ②使用Thread类中的stop()(已过期不推荐使用)方法强行终止线程. ③使用中断机制 t.stop()调用时,终止线程,会导致该线程所持有的锁被强制释放,从而被其他线程所持有,因此有可能导致与预期结果不一致.下面使用中断信号量中断非阻塞状态的线程中: public class TestStopThread { public static void main(String[] args) throws Int

  • Spring中@Async注解实现异步调详解

    异步调用 在解释异步调用之前,我们先来看同步调用的定义:同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果. 异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕,继续执行下面的流程.例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法:如他们都是同步调用,则需要将他们都顺序执行完毕之后,过程才执行完毕: 如B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是执行开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了. 概述说明 Sp

  • java调用回调机制详解

    调用和回调机制 在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种: 1.同步调用 同步调用是最基本并且最简单的一种调用方式, 类A的方法a()调用类B的方法b(), 一直等待b()方法执行完毕, a()方法继续往下走. 这种调用方式适用于方法b()执行时间不长的情况, 因为b()方法执行时间一长或者直接阻塞的话, a()方法的余下代码是无法执行下去的, 这样会造成整个流程的阻塞. 2.异步调用 异步调用是为了解决同步调用可能出现阻塞, 导致整个流程卡住而产

随机推荐