`
13shu
  • 浏览: 71939 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Spring3.2异步处理http请求

    博客分类:
  • java
 
阅读更多

解决的问题:

目前系统中,有个别的查询比较慢,大概需要几秒才能返回结果。在大量使用此功能时导致系统变慢。

可能会出现的原因:

1、程序问题(包括业务设计、业务逻辑处理、数据库优化等)

2、架构问题

3、其他原因

此处就第二点做一下分析:

用户查询开始到返回结果到页面,此处是一个同步的过程,如果做成异步的能提高系统响应的性能,最近发现servlet3.0有一个新的特性,新增HTTP请求的异步处理,详细请参考

由于项目采用的SpringMVC做的,所以查看了下SpringMVC的资料,发现3.2版本对于异步处理有良好的封装。开始做实验哭

 

  • 配置servlet3.0

     web.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">
 

 

<servlet>
		<servlet-name>test</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>2</load-on-startup>
		<async-supported>true</async-supported>
	</servlet>

	<servlet-mapping>
		<servlet-name>test</servlet-name>
		<url-pattern>/</url-pattern>
    <servlet-mapping>

 

  • Spring控制层代码修改返回类型为Callable模型
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属性来设置线程数的大小),郁闷了叫喊.这个肯定不是我需要的。

 

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,不执行)

 

 

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类似。

 

        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(单位秒)

<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方式配置线程池,异步操作拦截器

	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请求的超时时间
  • 线程池的大小配置
以上问题都需要根据实际业务来做调整。

 

分享到:
评论
2 楼 langzibo112304 2016-07-11  
楼主的qq是多少 有个问题请教
1 楼 lancijk 2015-04-02  
多谢楼主 分享.

相关推荐

    Spring Boot实现异步请求(Servlet 3.0)

    在spring 3.2 及以后版本中增加了对请求的异步处理,这篇文章主要介绍了Spring Boot实现异步请求(Servlet 3.0),感兴趣的小伙伴们可以参考一下。

    详解spring mvc对异步请求的处理

    spring mvc3.2及以上版本增加了对请求的异步处理,是在servlet3的基础上进行封装的,有兴趣的可以了解一下。

    spring-best-practices:Spring异步最佳实践(未维护)

    功能完善,经过测试的功能完备的Spring 3.2参考应用程序,具有JPA持久性,REST Level-3资源,异步处理,作业,安全性,单元,模拟,集成,功能,其余客户端存根,性能测试以及许多最佳实践,聚集了在MVC / Spring / ...

    spring-mvc-chat:使用Spring MVC 3.2的示例代码,基于Servlet的异步支持

    概述使用Spring MVC 3.2(基于Servlet)的异步请求处理的聊天示例。 另请参阅分支以进行分布式聊天。笔记Tomcat中有一个错误会影响此示例。 请使用Tomcat 7.0.32或更高版本。指示Eclipse用户运行mvn eclipse:eclipse...

    达内云笔记项目

    JQuery:处理客户端的逻辑,以及发送异步请求 3)设计思想 采用了MVC分层思想,将代码分为如下: 视图层V:JQuery2.1 控制层C:DispatcherServlet+Controller(SpringMVC) 业务层M:Java 持久层M...

    达内 云笔记 很实用!

    JQuery:处理客户端的逻辑,以及发送异步请求 3)设计思想 采用了MVC分层思想,将代码分为如下: 视图层V:JQuery2.1 控制层C:DispatcherServlet+Controller(SpringMVC) 业务层M:Java 持久层M...

    看透springMvc源代码分析与实践

    22.2.3 WebAsyncTask和Callable类型异步请求的处理过程及用法301 22.2.4 DeferredResult类型异步请求的处理过程及用法303 22.2.5 ListenableFuture类型异步请求的处理过程及用法305 22.3 小结309

    spring security 参考手册中文版

    远程处理 - spring-security-remoting.jar 25 Web - spring-security-web.jar 25 配置 - spring-security-config.jar 26 LDAP - spring-security-ldap.jar 26 ACL - spring-security-acl.jar 26 CAS - spring-...

    Spring中文帮助文档

    2.4.4. 异步的JMS 2.4.5. JDBC 2.5. Web层 2.5.1. Spring MVC合理的默认值 2.5.2. Portlet 框架 2.5.3. 基于Annotation的控制器 2.5.4. Spring MVC的表单标签库 2.5.5. 对Tiles 2 支持 2.5.6. 对JSF 1.2支持...

    Spring.3.x企业应用开发实战(完整版).part2

    2.5.2 处理登录请求 2.5.3 JSP视图页面 2.6 运行Web应用 2.7 小结 第2篇 IoC和AOP 第3章 IoC容器概述 3.1 IoC概述 3.1.1 通过实例理解IoC的概念 3.1.2 IoC的类型 3.1.3 通过容器完成依赖关系的注入 3.2 相关Java...

    Spring API

    2.4.4. 异步的JMS 2.4.5. JDBC 2.5. Web层 2.5.1. Spring MVC合理的默认值 2.5.2. Portlet 框架 2.5.3. 基于Annotation的控制器 2.5.4. Spring MVC的表单标签库 2.5.5. 对Tiles 2 支持 2.5.6. 对JSF 1.2支持...

    Spring3.x企业应用开发实战(完整版) part1

    2.5.2 处理登录请求 2.5.3 JSP视图页面 2.6 运行Web应用 2.7 小结 第2篇 IoC和AOP 第3章 IoC容器概述 3.1 IoC概述 3.1.1 通过实例理解IoC的概念 3.1.2 IoC的类型 3.1.3 通过容器完成依赖关系的注入 3.2 相关Java...

    SpringBoot新手学习手册

    6.2使用AOP统一处理Web请求日志 32 6.3Spring Boot集成lombok让代码更简洁 33 七、 缓存支持 35 7.1注解配置与EhCache使用 35 7.2使用Redis集成缓存 37 八、 热部署 37 8.1 什么是热部署 37 8.2 项目演示案例...

    SpringBoot新手学习手册.pdf

    6.2使用AOP统一处理Web请求日志 9 七、 缓存支持 9 7.1注解配置与EhCache使用 9 使用Redis做集中式缓存 9 八、 其他内容 9 8.1、使用@Scheduled创建定时任务 9 8.2、使用@Async实现异步调用 9 8.3、自定义...

    【分布式事务----LCN】LCN原理及使用方式.docx

    LCN的补偿事务原理是模拟上次失败事务的请求,然后传递给TxClient模块然后再次执行该次请求事务。 模拟场景演示 若存在事务发起方、参与方A、参与方B。调用关系图如下 那么他们正常执行业务的时序图为: ...

    Grails权威指南

     3.2 grails和mvc模式  3.2.1 mvc中的模型(m)  3.2.2 mvc中的视图(v)  3.2.3 mvc中的控制器(c)  3.2.4 除mvc之外的其他部分  3.3 grails支持多种环境  3.4 数据源配置  3.4.1 ...

    DWR.xml配置文件说明书(含源码)

    3.2 Security Creator可以配置类的成员函数的访问权限.creator有授权访问(指明可以被访问的方法)和拒绝访问(指明不允许访问的方法)两种配置方式. 如果要设置除了setWibble方法之外的所有方法都不可访问可以采用下面...

    经典JAVA.EE企业应用实战.基于WEBLOGIC_JBOSS的JSF_EJB3_JPA整合开发.pdf

    6.2.4 PTP消息的异步接收 246 6.3 Pub-Sub类型的JMS 248 6.3.1 配置Pub-Sub模型的JMS 服务器 248 6.3.2 消息的生产、消费 250 6.3.3 可靠的JMS订阅 251 6.4 JMS消息 253 6.4.1 JMS消息类型 253 6.4.2 JMS消息头和...

Global site tag (gtag.js) - Google Analytics