中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

tomcat下使用Servlet異步模式的方法

發布時間:2021-07-07 15:48:10 來源:億速云 閱讀:176 作者:chen 欄目:大數據

這篇文章主要介紹“tomcat下使用Servlet異步模式的方法”,在日常操作中,相信很多人在tomcat下使用Servlet異步模式的方法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”tomcat下使用Servlet異步模式的方法”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

servlet3.0版本以后,增加了對異步模式的支持。

    以往在servlet里面,每一個新的請求到來都會由一個線程來接收處理,在處理過程中如果需要等待其他操作的結果,則線程就會處于阻塞狀態不能執行其他任務,待任務結束后該線程將結果輸出給客戶端,這時該線程才能繼續處理其他的請求。為了提高線程利用效率,servlet3.0版本以后增加了異步處理請求的模式,允許當前線程將任務提交到給其他后臺線程處理(一般是后臺線程池,這樣只需要較少的線程就可以處理大量的任務),自身轉而去接收新的請求。

  protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().println("hello");
    }

    要使用異步模式,只需要調用request對象的startAsync方法即可,該方法返回一個AsyncContext對象供后續使用,可以通過該對象設置異步處理的超時間,添加異步處理的監聽器等。然后將要處理的任務提交到某個線程池,當前線程執行完后續的代碼后就能去處理其他新的請求,不用等待當前任務執行完。當前任務交由后臺線程池執行完后,可以調用asyncContext.complete方法表示任務處理完成,觸發之前添加的監聽器對事件進行響應。

protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
        //啟用異步模式
        final AsyncContext ac = request.startAsync();
        //超時設置
        ac.setTimeout(1000L);
        //添加監聽器便于觀察發生的事件
        ac.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent asyncEvent) throws IOException {
                System.out.println("onComplete");
            }
            @Override
            public void onTimeout(AsyncEvent asyncEvent) throws IOException {
                System.out.println("onTimeout");
            }
            @Override
            public void onError(AsyncEvent asyncEvent) throws IOException {
                System.out.println("onError");
            }
            @Override
            public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
                System.out.println("onStartAsync");
            }
        });

        executor.submit(new Runnable() {
            @Override
            public void run() {
                //這里可以使用request, response,ac等對象
                try {
                    String user = request.getParameter("user");
                    response.getWriter().println("hello from async " + user);
                    ac.complete();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        //方法結束當前線程可以去處理其他請求了
    }

    由于asyncContext對象中持有請求中的request和response對象,所以在任務異步執行完后仍然可以通過response將結果輸出給客戶端。但是,tomcat在經過超時間之后還未收到complete消息,會認為異步任務已經超時,需要結束當前的請求,從而將response對象放回對象池供其他請求繼續使用。這時response對象會分配給新的請求使用,按理就不應該再被之前的異步任務共用!但是異步任務本身并不知道任務已經超時了,還在繼續運行,因此還會使用response對象進行輸出,這時就會發生新的請求與后臺異步任務共同一個resonse對象的現象!這會造成多個線程向同一個客戶端輸出結果,將本不是該客戶端需要的結果輸出。試想一下:本來請求是的查詢我的訂單列表,結果收到了別人的訂單列表,這個后果是不是很嚴重呢?

為驗證這個問題,可以使用以下代碼進行測試:

package async;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class AsyncTimeoutServlet extends HttpServlet {

    boolean running = false;
    boolean stop = false;
    ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 50000L,
            TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(100));

    @Override
    public void init() throws ServletException {
        System.out.println("init AsyncTimeoutServlet");
    }

    @Override
    public void destroy() {
        executor.shutdownNow();
    }

    protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
        stop = "true".equals(request.getParameter("stop"));
        //這里只對第一次請求使用異步模式,后續請求均使用同步模式
        if (running) {
            System.out.println("running");
            try {
                //在同步模式下輸出response對象的hashcode
                response.getWriter().println("this response belong's to you:" + response.toString());
            } catch (IOException e) {
                System.out.println("response error");
            }
            return;
        }
        running = true;

        //啟用異步模式
        final AsyncContext ac = request.startAsync();
        System.out.println("startAsync");
        //超時設置為1s便于快速超時
        ac.setTimeout(1000L);
        //添加監聽器便于觀察發生的事件
        ac.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent asyncEvent) throws IOException {
                System.out.println("onComplete");
            }

            @Override
            public void onTimeout(AsyncEvent asyncEvent) throws IOException {
                System.out.println("onTimeout");
            }

            @Override
            public void onError(AsyncEvent asyncEvent) throws IOException {
                System.out.println("onError");
            }

            @Override
            public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
                System.out.println("onStartAsync");
            }
        });

        executor.submit(new Runnable() {
            @Override
            public void run() {
                while (!stop) {
                    try {
                        //每隔3s向原始的response對象中輸出結果,便于客戶端觀察是否有收到該結果
                        Thread.sleep(3000L);
                        System.out.println("async run");
                        try {

                            response.getWriter().println("if you see this message, something must be wrong. I'm " + response.toString());
                        } catch (IOException e) {
                            System.out.println("async response error");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        return;
                    }
                }
                System.out.println("stop");
            }
        });
        System.out.println("ok, async mode started.");
    }
}

在上面的測試示例中,我們對第一次請求開啟了異步模式,后續的請求仍然采用同步模式,并只是簡單地輸出response對象的hashcode,將一個任務提交到了線程池中運行。在異步任務里每隔3s向客戶端輸出一次response對象的hashcode,而這個response對象是第一個請求的response對象,也就是說,它應該與后續的請求使用了不同的response對象才對。但是在多次調用該servlet后,有些請求得到的結果中包含了第一次請求時產生的異步任務中輸出的內容,也就是后續的有些請求與第一次請求共用了同一個response對象,tomcat對response對象進行了重用!

測試結果如下:

curl -i "http://127.0.0.1:8080/servlet_async/async"
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Length: 192
Date: Wed, 21 Aug 2019 07:55:26 GMT

if you see this message, something must be wrong. I'm org.apache.catalina.connector.ResponseFacade@51582d92
this response belong's to you:org.apache.catalina.connector.ResponseFacade@51582d92

并不是每一次請求都能成功重用到同一個response,所以上述請求有可能需要運行多次才能出現預期的結果。

避坑方法:

異步任務如果需要使用response對象,先判斷當前異步模式是否已經超時和結束了,如果結束了則不要再使用該對象,使用request對象也是同理。不過,有時候我們會把request對象傳入異步任務,在任務執行的時候會從中取出一些數據使用,比如getParameter獲取參數,這種情況下可以事先從request對象中獲取到異步任務需要的所有數據,封裝成新的對象供異步任務使用,避免使用tomcat提供的request對象。

到此,關于“tomcat下使用Servlet異步模式的方法”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

克什克腾旗| 寿阳县| 云和县| 阿拉尔市| 盖州市| 田东县| 英德市| 四川省| 景德镇市| 大荔县| 古田县| 英超| 隆回县| 巴林右旗| 丹凤县| 嘉兴市| 屯门区| 丹江口市| 时尚| 项城市| 仁布县| 应用必备| 齐齐哈尔市| 金寨县| 行唐县| 栖霞市| 澜沧| 汪清县| 阿鲁科尔沁旗| 长白| 大港区| 商都县| 出国| 雷波县| 方城县| 日照市| 海兴县| 武定县| 胶南市| 桑日县| 灌云县|