servlet異步請求的實現(xiàn)
1、什么是servlet異步請求
Servlet 3.0 之前,一個普通 Servlet 的主要工作流程大致如下:
(1)、Servlet 接收到請求之后,可能需要對請求攜帶的數(shù)據(jù)進行一些預處理;
(2)、調用業(yè)務接口的某些方法,以完成業(yè)務處理;
(3)、根據(jù)處理的結果提交響應,Servlet 線程結束。
其中第二步處理業(yè)務邏輯時候很可以碰到比較耗時的任務,此時servlet主線程會阻塞等待完成業(yè)務處理,對于并發(fā)比較大的請求可能會產生性能瓶頸,則servlet3.0之后再此處做了調整,引入了異步的概念。
(1)、Servlet 接收到請求之后,可能需要對請求攜帶的數(shù)據(jù)進行一些預處理;
(2)、調用業(yè)務接口的某些方法過程中request.startAsync()請求,獲取一個AsyncContext
(3)、緊接著servlet線程退出(回收到線程池),但是響應response對象仍舊保持打開狀態(tài),新增線程會使用AsyncContext處理并響應結果。
(4)、AsyncContext處理完成觸發(fā)某些監(jiān)聽通知結果
2、Servlet異步請求示例
2.1、示例準備
本示例采用web.xml配置的形式,模擬場景為:筆者所在的it公司每周的工作內容,首先研發(fā)總監(jiān)分配給產品、研發(fā)、測試相關的任務,布置完任務就出差(模擬請求響應),余下的各個小組進行自己任務操作(模擬的耗時操作),最終出周報完成任務(異步任務處理完成的通知)
git地址:https://github.com/liushangzaibeijing/spsm.git 分支:dev_async
2.2、實現(xiàn)自定義的Servlet
/**
* @ClassName AsyncServlet
* @Desc 自定義異步Servlet處理器
* @Author xieqx
* @Date 2020/12/9 15:38
**/
//通過注解的形式開始異步
@WebServlet(urlPatterns = "*.async",asyncSupported = true)
public class AsyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//開啟異步支持
//異步管理上下文
resp.setCharacterEncoding("GBK");
PrintWriter writer = resp.getWriter();
writer.println("周工作任務布置開始");
AsyncContext asyncContext = req.startAsync();
asyncContext.start(new WeekTask(asyncContext));
//添加監(jiān)聽器 處理完成監(jiān)聽
asyncContext.addListener(new AsyncListener() {
@Override
public void onComplete(AsyncEvent asyncEvent) throws IOException {
System.out.println("工作在"+new Date()+"處理完成");
}
@Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
System.out.println("工作在"+new Date()+"處理超時");
}
@Override
public void onError(AsyncEvent asyncEvent) throws IOException {
System.out.println("工作在"+new Date()+"處理出錯");
}
@Override
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
System.out.println("工作在"+new Date()+"處理開始");
}
});
writer.println("周工作任務布置完成");
writer.flush();
}
}開啟異步支持(默認異步支持不開啟)有兩種方式:
使用注解

web.xml配置
<servlet> <servlet-name>asyncServlet</servlet-name> <servlet-class>com.xiu.async.servlet.AsyncServlet</servlet-class> <!-- 開啟servlet的異步請求操作 --> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>asyncServlet</servlet-name> <url-pattern>*.async</url-pattern> </servlet-mapping>
上述代碼中通過request.startAsync()啟動異步處理 返回一個異步上下文對象AsyncContext最終是使用該上下文對象來進行異步業(yè)務邏輯處理,其中有兩個核心方法
asyncContext.start(new WeekTask(asyncContext)); 添加一個異步任務該任務是一個Runnable線程接口,這里就清晰了其實是servlet線程將處理任務交給另一個子線程,servlet直接返回從而達到提高系統(tǒng)吞吐量的作用。
對于異步請求可以我們需要獲取其中的結果,所有這里提供了監(jiān)聽器模式添加事件監(jiān)聽AsyncListener
| onComplete | 異步請求處理完成觸發(fā) 前提示需要調用 asyncContext.complete()方法(因為程序也不知道什么時候任務算是調用完畢了) |
| onTimeout | 異步請求處理超時觸發(fā),一般來說采用異步請求的任務都是比較耗時的任務,所以需要修改servlet默認的超時時間(修改的長一點) |
| onError | 異步處理錯誤的時候觸發(fā) |
| onStartAsync | 異步處理開始的時候觸發(fā)即為request.startAsync(),因為添加監(jiān)聽器在startAsync()方法后,所以第一個啟動是無法觸發(fā)該監(jiān)聽的 |
這里異步處理只是簡單的打印了相關日志,不過真實的業(yè)務場景中可以寫復雜的業(yè)務處理邏輯。
2.3、異步任務
這里提供相關的異步操作是實現(xiàn)runnable的線程實現(xiàn)類,同時這里提供了相關Job,PmJob(產品任務),RDJob(研發(fā)任務),TestJob(測試任務),每個任務模擬了10秒的耗時任務。
/**
* @ClassName WeekTask
* @Desc 每周任務
* @Author xieqx
* @Date 2020/12/10 9:36
**/
public class WeekTask implements Runnable {
private List<Job> jobs = null;
private AsyncContext asyncContext = null;
//這里初始化產品任務PmJob、研發(fā)任務RDJob 測試任務TestJob
public WeekTask(AsyncContext asyncContext) {
this.asyncContext = asyncContext;
jobs = new ArrayList<>();
PmJob pmJob = new PmJob();
RDJob rdJob = new RDJob();
TestJob testJob = new TestJob();
jobs.add(pmJob);
jobs.add(rdJob);
jobs.add(testJob);
}
@Override
public void run() {
for(Job job:jobs){
job.execute();
}
System.out.println("周任務工作完成");
//job執(zhí)行完成后通知
asyncContext.complete();
}
}PmJob
/**
* @ClassName PmTask
* @Desc 產品經(jīng)理任務
* @Author xieqx
* @Date 2020/12/9 16:03
**/
public class PmJob implements Job {
@Override
public void execute() {
System.out.println("產品經(jīng)理開評審會議");
try {
Thread.sleep(10);
System.out.println("模擬需求評審會議...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}RDJob
/**
* @ClassName PmTask
* @Desc 研發(fā)任務
* @Author xieqx
* @Date 2020/12/9 16:03
**/
public class RDJob implements Job {
@Override
public void execute() {
System.out.println("程序猿開始開發(fā)");
try {
Thread.sleep(10);
System.out.println("程序猿哼哧哼哧干活中...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}TestJob
/**
* @ClassName TestJob
* @Desc 測試任務
* @Author xieqx
* @Date 2020/12/9 16:03
**/
public class TestJob implements Job {
@Override
public void execute() {
System.out.println("測試開始測試");
try {
Thread.sleep(10);
System.out.println("測試用例測試...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}2.4、測試場景
請求立馬響應,但是異步任務在后面處理


到此這篇關于servlet異步請求的實現(xiàn)的文章就介紹到這了,更多相關servlet異步請求內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
AQS同步組件CyclicBarrier循環(huán)屏障用例剖析
這篇文章主要為大家介紹了AQS同步組件CyclicBarrier循環(huán)屏障用例剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-08-08
SpringBoot接口防抖(防重復提交)的實現(xiàn)方案
所謂防抖,一是防用戶手抖,二是防網(wǎng)絡抖動,在Web系統(tǒng)中,表單提交是一個非常常見的功能,如果不加控制,容易因為用戶的誤操作或網(wǎng)絡延遲導致同一請求被發(fā)送多次,所以本文給大家介紹了SpringBoot接口防抖(防重復提交)的實現(xiàn)方案,需要的朋友可以參考下2024-04-04

