servlet異步請(qǐng)求的實(shí)現(xiàn)
1、什么是servlet異步請(qǐng)求
Servlet 3.0 之前,一個(gè)普通 Servlet 的主要工作流程大致如下:
(1)、Servlet 接收到請(qǐng)求之后,可能需要對(duì)請(qǐng)求攜帶的數(shù)據(jù)進(jìn)行一些預(yù)處理;
(2)、調(diào)用業(yè)務(wù)接口的某些方法,以完成業(yè)務(wù)處理;
(3)、根據(jù)處理的結(jié)果提交響應(yīng),Servlet 線程結(jié)束。
其中第二步處理業(yè)務(wù)邏輯時(shí)候很可以碰到比較耗時(shí)的任務(wù),此時(shí)servlet主線程會(huì)阻塞等待完成業(yè)務(wù)處理,對(duì)于并發(fā)比較大的請(qǐng)求可能會(huì)產(chǎn)生性能瓶頸,則servlet3.0之后再此處做了調(diào)整,引入了異步的概念。
(1)、Servlet 接收到請(qǐng)求之后,可能需要對(duì)請(qǐng)求攜帶的數(shù)據(jù)進(jìn)行一些預(yù)處理;
(2)、調(diào)用業(yè)務(wù)接口的某些方法過(guò)程中request.startAsync()請(qǐng)求,獲取一個(gè)AsyncContext
(3)、緊接著servlet線程退出(回收到線程池),但是響應(yīng)response對(duì)象仍舊保持打開(kāi)狀態(tài),新增線程會(huì)使用AsyncContext處理并響應(yīng)結(jié)果。
(4)、AsyncContext處理完成觸發(fā)某些監(jiān)聽(tīng)通知結(jié)果
2、Servlet異步請(qǐng)求示例
2.1、示例準(zhǔn)備
本示例采用web.xml配置的形式,模擬場(chǎng)景為:筆者所在的it公司每周的工作內(nèi)容,首先研發(fā)總監(jiān)分配給產(chǎn)品、研發(fā)、測(cè)試相關(guān)的任務(wù),布置完任務(wù)就出差(模擬請(qǐng)求響應(yīng)),余下的各個(gè)小組進(jìn)行自己任務(wù)操作(模擬的耗時(shí)操作),最終出周報(bào)完成任務(wù)(異步任務(wù)處理完成的通知)
git地址:https://github.com/liushangzaibeijing/spsm.git 分支:dev_async
2.2、實(shí)現(xiàn)自定義的Servlet
/** * @ClassName AsyncServlet * @Desc 自定義異步Servlet處理器 * @Author xieqx * @Date 2020/12/9 15:38 **/ //通過(guò)注解的形式開(kāi)始異步 @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 { //開(kāi)啟異步支持 //異步管理上下文 resp.setCharacterEncoding("GBK"); PrintWriter writer = resp.getWriter(); writer.println("周工作任務(wù)布置開(kāi)始"); AsyncContext asyncContext = req.startAsync(); asyncContext.start(new WeekTask(asyncContext)); //添加監(jiān)聽(tīng)器 處理完成監(jiān)聽(tīng) 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()+"處理超時(shí)"); } @Override public void onError(AsyncEvent asyncEvent) throws IOException { System.out.println("工作在"+new Date()+"處理出錯(cuò)"); } @Override public void onStartAsync(AsyncEvent asyncEvent) throws IOException { System.out.println("工作在"+new Date()+"處理開(kāi)始"); } }); writer.println("周工作任務(wù)布置完成"); writer.flush(); } }
開(kāi)啟異步支持(默認(rèn)異步支持不開(kāi)啟)有兩種方式:
使用注解
web.xml配置
<servlet> <servlet-name>asyncServlet</servlet-name> <servlet-class>com.xiu.async.servlet.AsyncServlet</servlet-class> <!-- 開(kāi)啟servlet的異步請(qǐng)求操作 --> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>asyncServlet</servlet-name> <url-pattern>*.async</url-pattern> </servlet-mapping>
上述代碼中通過(guò)request.startAsync()啟動(dòng)異步處理 返回一個(gè)異步上下文對(duì)象AsyncContext最終是使用該上下文對(duì)象來(lái)進(jìn)行異步業(yè)務(wù)邏輯處理,其中有兩個(gè)核心方法
asyncContext.start(new WeekTask(asyncContext)); 添加一個(gè)異步任務(wù)該任務(wù)是一個(gè)Runnable線程接口,這里就清晰了其實(shí)是servlet線程將處理任務(wù)交給另一個(gè)子線程,servlet直接返回從而達(dá)到提高系統(tǒng)吞吐量的作用。
對(duì)于異步請(qǐng)求可以我們需要獲取其中的結(jié)果,所有這里提供了監(jiān)聽(tīng)器模式添加事件監(jiān)聽(tīng)AsyncListener
onComplete | 異步請(qǐng)求處理完成觸發(fā) 前提示需要調(diào)用 asyncContext.complete()方法(因?yàn)槌绦蛞膊恢朗裁磿r(shí)候任務(wù)算是調(diào)用完畢了) |
onTimeout | 異步請(qǐng)求處理超時(shí)觸發(fā),一般來(lái)說(shuō)采用異步請(qǐng)求的任務(wù)都是比較耗時(shí)的任務(wù),所以需要修改servlet默認(rèn)的超時(shí)時(shí)間(修改的長(zhǎng)一點(diǎn)) |
onError | 異步處理錯(cuò)誤的時(shí)候觸發(fā) |
onStartAsync | 異步處理開(kāi)始的時(shí)候觸發(fā)即為request.startAsync(),因?yàn)樘砑颖O(jiān)聽(tīng)器在startAsync()方法后,所以第一個(gè)啟動(dòng)是無(wú)法觸發(fā)該監(jiān)聽(tīng)的 |
這里異步處理只是簡(jiǎn)單的打印了相關(guān)日志,不過(guò)真實(shí)的業(yè)務(wù)場(chǎng)景中可以寫(xiě)復(fù)雜的業(yè)務(wù)處理邏輯。
2.3、異步任務(wù)
這里提供相關(guān)的異步操作是實(shí)現(xiàn)runnable的線程實(shí)現(xiàn)類(lèi),同時(shí)這里提供了相關(guān)Job,PmJob(產(chǎn)品任務(wù)),RDJob(研發(fā)任務(wù)),TestJob(測(cè)試任務(wù)),每個(gè)任務(wù)模擬了10秒的耗時(shí)任務(wù)。
/** * @ClassName WeekTask * @Desc 每周任務(wù) * @Author xieqx * @Date 2020/12/10 9:36 **/ public class WeekTask implements Runnable { private List<Job> jobs = null; private AsyncContext asyncContext = null; //這里初始化產(chǎn)品任務(wù)PmJob、研發(fā)任務(wù)RDJob 測(cè)試任務(wù)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("周任務(wù)工作完成"); //job執(zhí)行完成后通知 asyncContext.complete(); } }
PmJob
/** * @ClassName PmTask * @Desc 產(chǎn)品經(jīng)理任務(wù) * @Author xieqx * @Date 2020/12/9 16:03 **/ public class PmJob implements Job { @Override public void execute() { System.out.println("產(chǎn)品經(jīng)理開(kāi)評(píng)審會(huì)議"); try { Thread.sleep(10); System.out.println("模擬需求評(píng)審會(huì)議..."); } catch (InterruptedException e) { e.printStackTrace(); } } }
RDJob
/** * @ClassName PmTask * @Desc 研發(fā)任務(wù) * @Author xieqx * @Date 2020/12/9 16:03 **/ public class RDJob implements Job { @Override public void execute() { System.out.println("程序猿開(kāi)始開(kāi)發(fā)"); try { Thread.sleep(10); System.out.println("程序猿哼哧哼哧干活中..."); } catch (InterruptedException e) { e.printStackTrace(); } } }
TestJob
/** * @ClassName TestJob * @Desc 測(cè)試任務(wù) * @Author xieqx * @Date 2020/12/9 16:03 **/ public class TestJob implements Job { @Override public void execute() { System.out.println("測(cè)試開(kāi)始測(cè)試"); try { Thread.sleep(10); System.out.println("測(cè)試用例測(cè)試..."); } catch (InterruptedException e) { e.printStackTrace(); } } }
2.4、測(cè)試場(chǎng)景
請(qǐng)求立馬響應(yīng),但是異步任務(wù)在后面處理
到此這篇關(guān)于servlet異步請(qǐng)求的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)servlet異步請(qǐng)求內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Java對(duì)數(shù)據(jù)庫(kù)進(jìn)行基本的查詢(xún)和更新操作
這篇文章主要介紹了使用Java對(duì)數(shù)據(jù)庫(kù)進(jìn)行基本的查詢(xún)和更新操作,是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-10-10Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(30)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-07-07form表單回寫(xiě)技術(shù)java實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)form表單回寫(xiě)技術(shù)的相關(guān)資料,需要的朋友可以參考下2016-04-04Java中的NoClassDefFoundError報(bào)錯(cuò)含義解析
這篇文章主要為大家介紹了Java中的NoClassDefFoundError含義詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助2023-11-11MyBatis3傳遞多個(gè)參數(shù)(Multiple Parameters)
這篇文章主要介紹了MyBatis3傳遞多個(gè)參數(shù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07SpringBoot中yml多環(huán)境配置的3種方法
這篇文章主要給大家介紹了SpringBoot中yml多環(huán)境配置的3種方法,文中有詳細(xì)的代碼示例供大家參考,對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-10-10Spring中11個(gè)最常用的擴(kuò)展點(diǎn)總結(jié),你知道幾個(gè)
我們知道IOC(控制反轉(zhuǎn))和AOP(面向切面編程)是spring的基石,除此之外spring的擴(kuò)展能力非常強(qiáng),下面這篇文章主要給大家介紹了關(guān)于Spring中11個(gè)最常用的擴(kuò)展點(diǎn)的相關(guān)資料,需要的朋友可以參考下2022-12-12