Spring3.2异步处理http请求
2015-10-28 10:03
525 查看
解决的问题:
目前系统中,有个别的查询比较慢,大概需要几秒才能返回结果。在大量使用此功能时导致系统变慢。
可能会出现的原因:
1、程序问题(包括业务设计、业务逻辑处理、数据库优化等)
2、架构问题
3、其他原因
此处就第二点做一下分析:
用户查询开始到返回结果到页面,此处是一个同步的过程,如果做成异步的能提高系统响应的性能,最近发现servlet3.0有一个新的特性,新增HTTP请求的异步处理,详细请参考。
由于项目采用的SpringMVC做的,所以查看了下SpringMVC的资料,发现3.2版本对于异步处理有良好的封装。开始做实验
配置servlet3.0
web.xml中配置
Xml代码
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
Xml代码
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
<strong><async-supported>true</async-supported></strong>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/</url-pattern>
<servlet-mapping>
Spring控制层代码修改返回类型为Callable模型
Java代码
public Callable<String> processUpload(HttpServletRequest request,
final HttpServletResponse response) {
System.out.println("线程名称:"+Thread.currentThread().getName());
return new Callable<String>() {
public String call() throws Exception {
try {
System.out.println("线程名称:"+Thread.currentThread().getName());
response.setContentType("text/plain;charset=utf-8");
response.getWriter().write("nihao");
response.getWriter().close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
};
}
控制台输出内容:
13:46:59,875 INFO [stdout] (http-localhost-127.0.0.1-8080-2) 线程名称:http-localhost-127.0.0.1-8080-2
13:48:57,609 INFO [stdout] (abc-2) 线程名称:abc-2
Callable内部与外部输出了当前线程的名字,发现线程被切换了,request请求的线程被释放到了web容器中(如:tomcat)。
问题貌似解决了
,离下班时间还早于是乎想看下这个神奇的过程是怎么发生的,看了下Spring的源代码,不看不知道,一看吓一跳。
Spring返回的Callable被RequestMappingHandlerAdapter拦截处理了,结果发现Callable被SimpleAsyncTaskExecutor线程池处理了,经过细心的查看代码发现SimpleAsyncTaskExecutor,每当任务被提交到此“线程池(这里就交线程池了)”时,线程池产生一个新的线程去执行Callable中的代码,每次都产生新的线程而且没有上线(默认没有上线,可以设置concurrencyLimit属性来设置线程数的大小),郁闷了
.这个肯定不是我需要的。
Java代码
public void execute(Runnable task, long startTimeout) {
Assert.notNull(task, "Runnable must not be null");
if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {
this.concurrencyThrottle.beforeAccess();
doExecute(new ConcurrencyThrottlingRunnable(task));
}
else {
doExecute(task);
}
}
执行任务前会先判断有没有空余的线程空间(concurrencyLimit默认为-1,不执行)
Java代码
protected void beforeAccess() {
if (this.concurrencyLimit == NO_CONCURRENCY) {
throw new IllegalStateException(
"Currently no invocations allowed - concurrency limit set to NO_CONCURRENCY");
}
if (this.concurrencyLimit > 0) {
boolean debug = logger.isDebugEnabled();
synchronized (this.monitor) {
boolean interrupted = false;
while (this.concurrencyCount >= this.concurrencyLimit) {
if (interrupted) {
throw new IllegalStateException("Thread was interrupted while waiting for invocation access, " +
"but concurrency limit still does not allow for entering");
}
if (debug) {
logger.debug("Concurrency count " + this.concurrencyCount +
" has reached limit " + this.concurrencyLimit + " - blocking");
}
try {
this.monitor.wait();
}
catch (InterruptedException ex) {
// Re-interrupt current thread, to allow other threads to react.
Thread.currentThread().interrupt();
interrupted = true;
}
}
if (debug) {
logger.debug("Entering throttle at concurrency count " + this.concurrencyCount);
}
this.concurrencyCount++;
}
}
}
如果没有超过concurrencyLimit,就不会this.monitor.wait();产生新的线程执行任务。
Spring中应该有其他线程池来支持此功能。最终选择ThreadPoolTaskExecutor,看起来与ThreadPoolExecutor类似。
Java代码
private int corePoolSize = 1;
private int maxPoolSize = Integer.MAX_VALUE;
private int keepAliveSeconds = 60;
private boolean allowCoreThreadTimeOut = false;
private int queueCapacity = Integer.MAX_VALUE;
private ThreadPoolExecutor threadPoolExecutor;
具备线程池的功能,可以设置线程的大小,有缓冲队列,空闲线程存活时间等。
默认corePoolSize=1,maxPoolSize、queueCapacity为整型最大值,keepAliveSeconds=60(单位秒)
Xml代码
<bean id="myThreadPool"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" /><!--最小线程数 -->
<property name="maxPoolSize" value="10" /><!--最大线程数 -->
<property name="queueCapacity" value="50" /><!--缓冲队列大小 -->
<property name="threadNamePrefix" value="abc-" /><!--线程池中产生的线程名字前缀 -->
<property name="keepAliveSeconds" value="30" /><!--线程池中空闲线程的存活时间单位秒 -->
</bean>
<mvc:annotation-driven>
<mvc:async-support task-executor="myThreadPool"
default-timeout="600">
<mvc:callable-interceptors>
<bean class="com.zhongyu.ABC" />
</mvc:callable-interceptors>
</mvc:async-support>
</mvc:annotation-driven>
采用annotation-driven方式配置线程池,异步操作拦截器
Java代码
public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task)
throws Exception {
HttpServletResponse servletResponse = request.getNativeResponse(HttpServletResponse.class);
if (!servletResponse.isCommitted()) {
servletResponse.setContentType("text/plain;charset=utf-8");
servletResponse.getWriter().write("超时了");
servletResponse.getWriter().close();
}
return null;
}
请求设定default-timeout超时后,会调用拦截器中handleTimeout进行逻辑处理
还需要优化的地方:
由于ThreadPoolTaskExecutor内部缓冲队列采用的是阻塞队列LinkedBlockingDeque,如果队列满了,外部线程会继续等待,需要设置HTTP请求的超时时间
线程池的大小配置
以上问题都需要根据实际业务来做调整。
下载次数: 175
目前系统中,有个别的查询比较慢,大概需要几秒才能返回结果。在大量使用此功能时导致系统变慢。
可能会出现的原因:
1、程序问题(包括业务设计、业务逻辑处理、数据库优化等)
2、架构问题
3、其他原因
此处就第二点做一下分析:
用户查询开始到返回结果到页面,此处是一个同步的过程,如果做成异步的能提高系统响应的性能,最近发现servlet3.0有一个新的特性,新增HTTP请求的异步处理,详细请参考。
由于项目采用的SpringMVC做的,所以查看了下SpringMVC的资料,发现3.2版本对于异步处理有良好的封装。开始做实验
配置servlet3.0
web.xml中配置
Xml代码
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
Xml代码
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
<strong><async-supported>true</async-supported></strong>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/</url-pattern>
<servlet-mapping>
Spring控制层代码修改返回类型为Callable模型
Java代码
public Callable<String> processUpload(HttpServletRequest request,
final HttpServletResponse response) {
System.out.println("线程名称:"+Thread.currentThread().getName());
return new Callable<String>() {
public String call() throws Exception {
try {
System.out.println("线程名称:"+Thread.currentThread().getName());
response.setContentType("text/plain;charset=utf-8");
response.getWriter().write("nihao");
response.getWriter().close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
};
}
控制台输出内容:
13:46:59,875 INFO [stdout] (http-localhost-127.0.0.1-8080-2) 线程名称:http-localhost-127.0.0.1-8080-2
13:48:57,609 INFO [stdout] (abc-2) 线程名称:abc-2
Callable内部与外部输出了当前线程的名字,发现线程被切换了,request请求的线程被释放到了web容器中(如:tomcat)。
问题貌似解决了
,离下班时间还早于是乎想看下这个神奇的过程是怎么发生的,看了下Spring的源代码,不看不知道,一看吓一跳。
Spring返回的Callable被RequestMappingHandlerAdapter拦截处理了,结果发现Callable被SimpleAsyncTaskExecutor线程池处理了,经过细心的查看代码发现SimpleAsyncTaskExecutor,每当任务被提交到此“线程池(这里就交线程池了)”时,线程池产生一个新的线程去执行Callable中的代码,每次都产生新的线程而且没有上线(默认没有上线,可以设置concurrencyLimit属性来设置线程数的大小),郁闷了
.这个肯定不是我需要的。
Java代码
public void execute(Runnable task, long startTimeout) {
Assert.notNull(task, "Runnable must not be null");
if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {
this.concurrencyThrottle.beforeAccess();
doExecute(new ConcurrencyThrottlingRunnable(task));
}
else {
doExecute(task);
}
}
执行任务前会先判断有没有空余的线程空间(concurrencyLimit默认为-1,不执行)
Java代码
protected void beforeAccess() {
if (this.concurrencyLimit == NO_CONCURRENCY) {
throw new IllegalStateException(
"Currently no invocations allowed - concurrency limit set to NO_CONCURRENCY");
}
if (this.concurrencyLimit > 0) {
boolean debug = logger.isDebugEnabled();
synchronized (this.monitor) {
boolean interrupted = false;
while (this.concurrencyCount >= this.concurrencyLimit) {
if (interrupted) {
throw new IllegalStateException("Thread was interrupted while waiting for invocation access, " +
"but concurrency limit still does not allow for entering");
}
if (debug) {
logger.debug("Concurrency count " + this.concurrencyCount +
" has reached limit " + this.concurrencyLimit + " - blocking");
}
try {
this.monitor.wait();
}
catch (InterruptedException ex) {
// Re-interrupt current thread, to allow other threads to react.
Thread.currentThread().interrupt();
interrupted = true;
}
}
if (debug) {
logger.debug("Entering throttle at concurrency count " + this.concurrencyCount);
}
this.concurrencyCount++;
}
}
}
如果没有超过concurrencyLimit,就不会this.monitor.wait();产生新的线程执行任务。
Spring中应该有其他线程池来支持此功能。最终选择ThreadPoolTaskExecutor,看起来与ThreadPoolExecutor类似。
Java代码
private int corePoolSize = 1;
private int maxPoolSize = Integer.MAX_VALUE;
private int keepAliveSeconds = 60;
private boolean allowCoreThreadTimeOut = false;
private int queueCapacity = Integer.MAX_VALUE;
private ThreadPoolExecutor threadPoolExecutor;
具备线程池的功能,可以设置线程的大小,有缓冲队列,空闲线程存活时间等。
默认corePoolSize=1,maxPoolSize、queueCapacity为整型最大值,keepAliveSeconds=60(单位秒)
Xml代码
<bean id="myThreadPool"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" /><!--最小线程数 -->
<property name="maxPoolSize" value="10" /><!--最大线程数 -->
<property name="queueCapacity" value="50" /><!--缓冲队列大小 -->
<property name="threadNamePrefix" value="abc-" /><!--线程池中产生的线程名字前缀 -->
<property name="keepAliveSeconds" value="30" /><!--线程池中空闲线程的存活时间单位秒 -->
</bean>
<mvc:annotation-driven>
<mvc:async-support task-executor="myThreadPool"
default-timeout="600">
<mvc:callable-interceptors>
<bean class="com.zhongyu.ABC" />
</mvc:callable-interceptors>
</mvc:async-support>
</mvc:annotation-driven>
采用annotation-driven方式配置线程池,异步操作拦截器
Java代码
public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task)
throws Exception {
HttpServletResponse servletResponse = request.getNativeResponse(HttpServletResponse.class);
if (!servletResponse.isCommitted()) {
servletResponse.setContentType("text/plain;charset=utf-8");
servletResponse.getWriter().write("超时了");
servletResponse.getWriter().close();
}
return null;
}
请求设定default-timeout超时后,会调用拦截器中handleTimeout进行逻辑处理
还需要优化的地方:
由于ThreadPoolTaskExecutor内部缓冲队列采用的是阻塞队列LinkedBlockingDeque,如果队列满了,外部线程会继续等待,需要设置HTTP请求的超时时间
线程池的大小配置
以上问题都需要根据实际业务来做调整。
TestWeb.rar (13.8 KB)
下载次数: 175
相关文章推荐
- 配置Tomcat使用https协议(配置SSL协议)
- 【Java TCP/IP Socket】构建和解析自定义协议消息(含代码)
- MTK平台下用Socket如何实现HTTP请求
- 计算机网络-http简介
- IDEA maven web 问题 -- maven包由于网络问题不能自动下载--本地包无法识别-解决
- https原理及tomcat配置https方法
- HTTP协议中的8种方法:Options,Head,Get,Post,put,Delete,Trace,Connect
- http的post传送文件原理
- ashx一般处理程序和HttpHandler
- 【Android测试技巧】02. 测试手机客户端HTTPS 你应该知道的事
- 机器学习、统计分析、数据挖掘、神经网络、人工智能、模式识别之间的关系是什么?
- TCP/IP,http,socket,长连接,短连接
- 四种常见的 POST 提交数据方式
- 修复Windows XP/2003 系统TCP/IP网络协议
- Tomcat处理HTTP请求源码分析--包含jsp内置对象的实例过程
- tmcat和appach http 服务器分别是用什么语言编写的
- tomcat之jsp/servlet容器与tomcat与专业的http服务器的区别
- python实现socket网络通信
- 承接游戏UI美术外包【厦门巨游网络科技有限公司】
- Codeforces Round #327 (Div. 1) E. Birthday【AC自动机+网络流】