Servlet只有同步模型是怎样的?

异步处理是Servlet3.0版本的重要功能之一,分析异步处理模型之前,先看看同步处理的过程是怎样的:

  • 客户端发起HTTP请求一个动态Servlet API,请求到达服务器端后经过静态服务器过滤后转交给Servlet容器,
  • 容器从主线程池获取一个线程,开始执行Servlet程序,执行结束后得到了完整的响应内容并将其返回给调用方
  • 然后将该线程还回线程池,整个过程都是由同一个主线程在执行。

上述模型存在什么问题呢?

  • 作为服务器要想提高并发性能,就只能通过提高线程池的最大线程数。即吞吐量瓶颈受线程池约束。
  • 更严重的问题是:Servlet执行期间始终占用了该线程池线程,假如某个高频且耗时的Servlet被请求,那就会占用大量甚至全部的线程池线程,进而导致没有线程来处理其它请求。

什么是异步处理模式?

相对于前面的同步处理,异步处理的核心本质就是提供了一个突破点:

让Servlet程序能够将这些慢操作分配给新线程来执行,同时尽快将该Servlet所占用的线程归还到容器线程池。

先来看看异步模式的Servlet程序是怎么样的:

import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletResponse;
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.Date; @WebServlet(urlPatterns = "/asyncapi", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/html;charset=GBK");
PrintWriter printWriter = response.getWriter();
printWriter.println("<title>异步Servlet示例</title>");
printWriter.println("进入Servlet的时间:" + new Date() + "<br/>");
printWriter.println("执行Servlet的线程ID:" + Thread.currentThread().getId() + " Name=" + Thread.currentThread().getName() + "<br/>"); // 创建AsyncContext,开始异步调用
final AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(50 * 1000);// 设置异步调用的超时时长,限制HTTP响应的最大耗时
asyncContext.start(
new Runnable() {//创建新线程继续执行
public void run() {
try {
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
printWriter.println("执行业务处理的线程ID:" + Thread.currentThread().getId() + " Name=" + Thread.currentThread().getName() + "<br/>");
ServletResponse response = asyncContext.getResponse();
/* ... print to the response ... */
printWriter.println("请求处理结束:" + new Date() + "<br/>");
asyncContext.complete();
}
}
);
printWriter.println("退出Servlet的时间:" + new Date() + "<br/>");
}
}

请求该API,根据返回值可以看到现象:

  • Servlet内部模拟耗时了1秒,postman监测到实际耗时也是1秒
  • 处理该次HTTP请求期间,共使用了两个线程
  • 进入和离开Servlet的时间一致,说明该线程占用很短暂
  • 执行业务处理的耗时操作占用了另外一个线程,且执行完成后才返回HTTP响应

至此,我们通过演示程序确实看到使用异步模式将Servet部分和耗时操作部分分配到了不同的线程执行。那么耗时操作用到的新线程来资源哪里?

异步处理模式的实现原理

根据文档描述,start()方法是启用新线程的关键,它是怎么实现的呢?扒拉源码看看吧

这需要我们查看Servlet容器的具体实现来验证,此处以Tomcat 9.0源码为例进行分析。

下载好源码后,首选找到接口AsyncContext及实现类AsyncContextImpl的源码:

上图看到tomcat的状态机进行调度后续的处理。

我们根据状态值检索到后续入口:

最终看到是通过executor来执行runnable程序,而executor即tomcat中可配置的连接池。

当然也不是只能使用start()来启用新线程,我们甚至可以手工new线程来执行耗时操作,演示如下:

import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletResponse;
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.Date; @WebServlet(urlPatterns = "/asyncapi_newthread", asyncSupported = true)
public class AsyncServlet_NewThread extends HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/html;charset=GBK");
PrintWriter printWriter = response.getWriter();
printWriter.println("<title>异步Servlet示例</title>");
printWriter.println("进入Servlet的时间:" + new Date() + "<br/>");
printWriter.println("执行Servlet的线程ID:" + Thread.currentThread().getId() + " Name=" + Thread.currentThread().getName() + "<br/>"); // 创建AsyncContext,开始异步调用
final AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(50 * 1000);// 设置异步调用的超时时长,限制HTTP响应的最大耗时 new Thread(() -> {
try {
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
printWriter.println("执行业务处理的线程ID:" + Thread.currentThread().getId() + " Name=" + Thread.currentThread().getName() + "<br/>");
ServletResponse aResponse = asyncContext.getResponse();
/* ... print to the response ... */
printWriter.println("请求处理结束:" + new Date() + "<br/>");
asyncContext.complete();
}).start();
printWriter.println("退出Servlet的时间:" + new Date() + "<br/>");
}
}

异步模式带来的好处是什么?

从编程方面提供了异步能力,在编程环节就可以提前将耗时较高的Servlet启用异步模式。

异步模式具备拆分线程池解耦的能力,配置异步模式从专有work线程池取线程,而不是与原Servlet线程池共用线程,可以显著提高Servlet容器的并发量,减小对其它Serlvet请求的影响。

同时要看到:异步模式只能说让Tomcat有机会接收更多请求,并不能提升特定服务的吞吐量。

参考资料:

Java EE7官方文档目录:https://docs.oracle.com/javaee/7/tutorial/index.html

Servlet的异步处理:https://docs.oracle.com/javaee/7/tutorial/servlets012.htm

Servlet的非阻塞IO:https://docs.oracle.com/javaee/7/tutorial/servlets013.htm

Servlet特性研究之异步模式的更多相关文章

  1. 高性能的关键:Spring MVC的异步模式

    我承认有些标题党了,不过话说这样其实也没错,关于“异步”处理的文章已经不少,代码例子也能找到很多,但我还是打算发表这篇我写了好长一段时间,却一直没发表的文章,以一个更简单的视角,把异步模式讲清楚. 什 ...

  2. 基于Task的异步模式的定义

    返回该系列目录<基于Task的异步模式--全面介绍> 命名,参数和返回类型 在TAP(Task-based Asynchronous Pattern)中的异步操作的启动和完成是通过一个单独 ...

  3. 基于Task的异步模式--全面介绍

    今天是国庆长假第一天,也是今天十月的开始.每到这个时候都是看海的季节-一个看"人海"的季节.反正我是不想在这样一个尴尬期出去放松自己,于是不如在家写写博客,长点本领呢.今天就来给大 ...

  4. Spring MVC的异步模式

    高性能的关键:Spring MVC的异步模式   我承认有些标题党了,不过话说这样其实也没错,关于“异步”处理的文章已经不少,代码例子也能找到很多,但我还是打算发表这篇我写了好长一段时间,却一直没发表 ...

  5. Ansible系列(七):执行过程分析、异步模式和速度优化

    本文目录:1.1 ansible执行过程分析1.2 ansible并发和异步1.3 ansible的-t选项妙用1.4 优化ansible速度 1.4.1 设置ansible开启ssh长连接 1.4. ...

  6. Controller异步模式

    转载: https://blog.csdn.net/yingxiake/article/details/51193319 因为服务器请求处理线程的总数是有限的,如果类似的请求多了,所有的处理线程处于阻 ...

  7. Spring MVC的异步模式DefferedResult

    原文:http://www.importnew.com/21051.html 什么是异步模式 要知道什么是异步模式,就先要知道什么是同步模式,先看最典型的同步模式: (图1) 浏览器发起请求,Web服 ...

  8. 三、基于任务的异步模式(TAP),推荐使用

    一.引言 在上两个专题中我为大家介绍.NET 1.0中的APM和.NET 2.0中的EAP,在使用前面两种模式进行异步编程的时候,大家多多少少肯定会感觉到实现起来比较麻烦, 首先我个人觉得,当使用AP ...

  9. C11 标准特性研究

    前言 - 需要点开头 C11标准是C语言标准的第三版(2011年由ISO/IEC发布),前一个标准版本是C99标准. 相比C99,C11有哪些变化呢!!所有的测试全部基于能够和标准贴合的特性平台. 但 ...

随机推荐

  1. 《C Primer Plus》第六版笔记--1~3章

    目录 第一章 初识C语言 1 使用C语言的7个步骤 1.1 定义程序目标 1.2 设计程序(功能实现) 1.3 编写代码 1.4 编译 1.5 运行程序 1.6 测试和调试程序 1.7 维护和修改代码 ...

  2. DYOJ 【20220317模拟赛】瞬间移动 题解

    瞬间移动 题意 三维空间中从 \((0,0,0)\) 开始,每次移动 1,问刚好走 \(N\) 次能到 \((X,Y,Z)\) 的方案数 \(N\le10^7\),答案模 \(998244353\) ...

  3. 第6章 字符串(下)——C++字符串

    6.5 C++ strings(C++字符串) C风格字符串常见错误:试图去访问数组范围以外的元素:没有使用函数strcpy( )来实现字符串之间的复制:没有使用函数strcmp( )来比较两个字符串 ...

  4. 基于SqlSugar的开发框架循序渐进介绍(7)-- 在文件上传模块中采用选项模式【Options】处理常规上传和FTP文件上传

    在基于SqlSugar的开发框架的服务层中处理文件上传的时候,我们一般有两种处理方式,一种是常规的把文件存储在本地文件系统中,一种是通过FTP方式存储到指定的FTP服务器上.这种处理应该由程序进行配置 ...

  5. 开发工具-PowerShell下载地址

    更新日志 2022年6月10日 初始化链接. https://github.com/PowerShell/PowerShell/releases/

  6. Ribbon的ServerStats引起内存泄露问题总结

    问题描述 服务运行一段时间之后,出现页面卡顿加载慢的问题,使用top命令查看了服务器的使用情况,发现CPU飙高,接着查看了该进程中每个线程的占用情况,发现导致CPU高的线程是JVM垃圾回收的线程,然后 ...

  7. VR技术赋能五大领域,不止高级,更高效!

    除了VR游戏.VR影视作品,究竟还有哪些产业领域会应用到VR技术并为生活带来改变呢?今天就帮大家好好梳理一下~ VR赋能交通,不只是高级 最近在网上看到了VR考驾照的新闻,网友都赞叹,现在学车都这么高 ...

  8. word-制作三线表

    找一个表格或插入一个表格, 找到 [设计] [新建表格样式] [将格式应用于: 整个表格] 点击"框线设置"按钮,在弹出的下拉菜单中分别选择 [上框线] 和 [下框线],然后分别设 ...

  9. 关于使用 koa路由与mysql模块, ctx.body获取不到值的问题

    var Koa = require('koa');var Router = require('koa-router' );var bodyParser = require('koa-bodyparse ...

  10. Maven的安装 和idea的配置

    Maven的安装 和idea的配置 工欲善其事 必先利其器 1 下载maven 官网 下滑 找到Files tar.gz 是linux系统的 .zip window系统 2 maven安装和配置到环境 ...