1. 程式人生 > >Servlet3.0的非同步支援-AsyncContext

Servlet3.0的非同步支援-AsyncContext

Servlet3是Tomcat7出現的新特性,所以使用的tomcat版本不能低於7.0.

由於每個request請求進來之後都被分配了一個執行緒處理,如果當前處理的業務方法非常耗時,將會在某段時間出現非常多的執行緒,而其他請求無法被分配到執行緒執行而被拒絕,所以Servlet3.0引入了非同步支援,請求進來後交給另外一個工作執行緒去執行,釋放當前接入請求的執行緒,這樣就可以接入越來越多的請求,提高併發量。

if (request.isAsyncSupported()) {
			
			//用於啟動非同步工作執行緒,進入非同步模式,呼叫業務處理執行緒進行業務處理
			request.startAsync(request, response);
			if (request.isAsyncStarted()) {
				/**
				 * 1 獲取AsyncContext,對非同步執行的上下文提供支援,可以透過AsyncContext的getRequest() 、 getResponse()方法取得Request、Response物件
				 * 2  客戶端的響應將暫緩至,呼叫AsyncContext的complete()方法或dispatch()為止,前者表示迴應完成,後者表示將響應調派給指定的URL
				 * 3 使用非同步處理方式,web容器的請求處理執行緒釋放了,可以服務其他的請求處理。但是該Request的處理並沒有結束,
				 *   在使用AsyncContext的complete或者dispatch完成後,這個request的處理才結束。
				 * 
				 */
				final AsyncContext asyncContext = request.getAsyncContext();
				asyncContext.setTimeout(SysConfig.getInstance().getPropertyInt("async_timeout"));
				// Servlet不會被阻塞,而是直接往下執行
				asyncContext.start(
				// 開啟http執行緒
				new Runnable() {
					@Override
					public void run() {

						PrintWriter printWriter = null;
						try {
							logger.info("休眠3秒鐘...");
							Thread.sleep(3000);
							String responseMsgContent = "";
							String ws_callback = SysConfig.getInstance().getProperty("ws_callback");
							String result = HttpUtils.postHttpAndHttps(ws_callback, "desc="+System.currentTimeMillis()+"-"+RandomUtils.nextInt(0, 9999));
							logger.info("執行緒Name:{},result:{}",Thread.currentThread().getName(),result);
							response.setCharacterEncoding("UTF-8");
							response.setHeader("Content-type","application/json;charset=UTF-8");
							printWriter = response.getWriter();
							printWriter.write(responseMsgContent);
							logger.info(responseMsgContent);
						} catch (Exception e) {
							e.printStackTrace();
						} finally {
							if (printWriter != null) {
								printWriter.flush();
								printWriter.close();
							}
							//告訴啟動非同步處理的Servlet非同步處理已完成,Servlet就會提交請求響應
							asyncContext.complete();
						}
					}

				});
				logger.info("繼續執行....");
			}
		} else { // 不支援非同步
			logger.info("當前servlet容器不支援非同步....");
		}

以上的asyncContext.start(new Runnable(){});這個方法是將任務提交給了主執行緒池,這樣的話感覺不過還是使用的主執行緒池的執行緒數,可能效果不是很好。

我們也可以使用自定義執行緒池,將需要非同步處理的任務提交給執行緒池管理,靈活呼叫。

package com.jeff.nio.controller;

import java.io.PrintWriter;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.jeff.nio.server.common.JsonDto;


/**
 * 登入
 *
 */
@Controller
public class TestController {

	private static final Logger logger = LoggerFactory.getLogger(TestController.class);

	private static ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));

	@GetMapping(value="/servlet3.0/asyncContext")
	public @ResponseBody void startServer(HttpServletRequest request, HttpServletResponse response) {
		JsonDto jsonDto = new JsonDto();
		try {
			logger.info("start server...................");
			if (request.isAsyncSupported()) {
				//用於啟動非同步工作執行緒,進入非同步模式,呼叫業務處理執行緒進行業務處理
				request.startAsync(request, response);
				if (request.isAsyncStarted()) {
					/**
					 * 1 獲取AsyncContext,對非同步執行的上下文提供支援,可以透過AsyncContext的getRequest() 、 getResponse()方法取得Request、Response物件
					 * 2  客戶端的響應將暫緩至,呼叫AsyncContext的complete()方法或dispatch()為止,前者表示迴應完成,後者表示將響應調派給指定的URL
					 * 3 使用非同步處理方式,web容器的請求處理執行緒釋放了,可以服務其他的請求處理。但是該Request的處理並沒有結束,
					 *   在使用AsyncContext的complete或者dispatch完成後,這個request的處理才結束。
					 * 
					 */
					final AsyncContext asyncContext = request.getAsyncContext();
					asyncContext.setTimeout(5000);
					// Servlet不會被阻塞,而是直接往下執行
					executor.execute(new Runnable() {
						@Override
						public void run() {
							PrintWriter printWriter = null;
							try {
								logger.info("休眠3秒鐘...");
								Thread.sleep(3000);
								response.setCharacterEncoding("UTF-8");
								response.setHeader("Content-type","application/json;charset=UTF-8");
								printWriter = response.getWriter();
								printWriter.write(jsonDto.toString());
							} catch (Exception e) {
								e.printStackTrace();
							} finally {
								if (printWriter != null) {
									printWriter.flush();
									printWriter.close();
								}
								//告訴啟動非同步處理的Servlet非同步處理已完成,Servlet就會提交請求響應
								asyncContext.complete();
							}
						}
					});
					logger.info("繼續執行....");
				}
			} else { // 不支援非同步
				logger.info("當前servlet容器不支援非同步....");
			}
		} catch (Exception e) {
			e.printStackTrace();
			jsonDto.setCode("-1");
			jsonDto.setMsg("執行失敗");
		}
	}
}