在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Linux/ 高級(jí) IO 機(jī)制
連接器
JSPs
重寫機(jī)制
CGI
Tomcat Manager
Windows 認(rèn)證
代理支持
虛擬主機(jī)
安全性注意事項(xiàng)
如何在 Maven 中使用 Tomcat 庫(kù)
安裝
MBean 描述符
JNDI 資源
類加載機(jī)制
Tomcat Web 應(yīng)用部署
基于 APR 的原生庫(kù)
負(fù)載均衡器
安全管理
附加組件
監(jiān)控與管理
Windows 服務(wù)
集群化與會(huì)話復(fù)制
高級(jí) IO 機(jī)制
SSI(服務(wù)器端嵌入)
WebSocket 支持
JDBC 數(shù)據(jù)源
日志機(jī)制
默認(rèn) Servlet
SSL/TLS 配置
Tomcat 的 JDBC 連接池
第一個(gè)應(yīng)用
簡(jiǎn)介
Realm 配置

高級(jí) IO 機(jī)制

簡(jiǎn)介

由于基于 APR 或 NIO API 來(lái)構(gòu)建連接器,Tomcat 能在通常的阻塞 IO 之上提供一些擴(kuò)展,從而支持 Servlet API。

重要說(shuō)明:這些特性需要使用 APR 或 NIO HTTP 連接器。經(jīng)典的 java.io HTTP 連接器 與 AJP 連接器并不支持它們。

Comet 支持

Comet 支持能讓 Servlet 實(shí)現(xiàn):對(duì) IO 的異步處理;當(dāng)連接可以讀取數(shù)據(jù)時(shí),接收事件(而不是總使用阻塞讀?。?;將數(shù)據(jù)異步地寫入連接(很可能是響應(yīng)其他一些源所產(chǎn)生的事件)。

1. Comet 事件

根據(jù)發(fā)生的具體事件,實(shí)現(xiàn) org.apache.catalina.comet.CometProcessor 接口的 Servlet 將調(diào)用自己的事件方法,而非通常的服務(wù)方法。事件對(duì)象允許訪問常見的請(qǐng)求與響應(yīng)對(duì)象,使用方式與通常方式相同。主要的區(qū)別在于:在處理 BEGIN 事件到 END 或 ERROR 事件之間,這些事件對(duì)象能夠保持有效和完整的功能性。事件類型如下:

  • EventType.BEGIN 在連接處理開始時(shí)被調(diào)用,用來(lái)初始化使用了請(qǐng)求和響應(yīng)對(duì)象的相關(guān)字段。從處理完該事件后直到 END 或 ERROR 事件開始處理時(shí)的這段時(shí)間內(nèi),有可能使用響應(yīng)對(duì)象在開放連接中寫入數(shù)據(jù)。注意,響應(yīng)對(duì)象以及所依賴的 OutputStream 和 Writer 仍不能同步,因此在通過(guò)多個(gè)線程訪問它們時(shí),需要進(jìn)行強(qiáng)制實(shí)現(xiàn)同步操作。處理完初始化事件后,就可以提交請(qǐng)求對(duì)象了。

  • EventType.READ 該事件表明可以使用輸入數(shù)據(jù),讀取過(guò)程不會(huì)阻塞??梢允褂?InputStream 或 Reader 的 available 和 ready 方法來(lái)確定是否存在阻塞危險(xiǎn):當(dāng)數(shù)據(jù)被報(bào)告可讀時(shí),Servlet 應(yīng)該進(jìn)行讀取。當(dāng)讀取遇到錯(cuò)誤時(shí),Servlet 可以通過(guò)正確傳播 Exception 屬性來(lái)報(bào)告這一情況。拋出異常會(huì)導(dǎo)致 ERROR 事件的調(diào)用,連接就會(huì)關(guān)閉。另外,也有可能捕獲一個(gè)異常,在 Servlet 可能使用的數(shù)據(jù)結(jié)構(gòu)上進(jìn)行清理,然后使用事件的 close 方法。不允許從 Servlet 對(duì)象執(zhí)行方法外部去讀取數(shù)據(jù)。

    在一些平臺(tái)(比如 Windows)上,利用 READ 事件來(lái)表示客戶端斷開連接。從流中讀取的結(jié)果可能是 -1、IOException 異?;?EOFException 異常。一定要正確處理這些情況。如果你沒有捕捉到 IOException 異常,那么當(dāng) Tomcat 捕獲到異常時(shí),它會(huì)立刻調(diào)用你的事件隊(duì)列生成一個(gè) ERROR 事件來(lái)存儲(chǔ)這些錯(cuò)誤,并且你會(huì)馬上收到這個(gè)消息。

  • EventType.END 請(qǐng)求處理完畢時(shí),就會(huì)調(diào)用 END 方法。Begin 方法初始化的字段也將被重置。在處理完這一事件后,請(qǐng)求和響應(yīng)對(duì)象,以及它們所依賴的對(duì)象,都將被回收,以便再去處理其他請(qǐng)求。當(dāng)數(shù)據(jù)可讀取時(shí),以及到達(dá)請(qǐng)求輸入的文件末尾時(shí)(這通常表明客戶端通過(guò)管線提交請(qǐng)求),也會(huì)調(diào)用 END。

  • EventType.ERROR:當(dāng)連接上出現(xiàn) IO 異?;蝾愃频牟豢苫厥盏腻e(cuò)誤時(shí),容器就會(huì)調(diào)用 ERROR。在開始時(shí)候被初始化的字段在這時(shí)候被重置。在處理完這一事件后,請(qǐng)求和響應(yīng)對(duì)象,以及它們所依賴的對(duì)象,都將被回收,以便再去處理其他請(qǐng)求。

下面是一些事件子類別,通過(guò)它們可以對(duì)事件處理過(guò)程進(jìn)行微調(diào)(注意:其中有些事件可能需要使用 org.apache.catalina.valves.CometConnectionManagerValve 值):

  • EventSubType.TIMEOUT: 連接超時(shí)(ERROR 的子類別)。注意,這個(gè) ERROR 子類型并不是必須的。除非 servlet 使用該事件的 close 方法,否則連接將不會(huì)關(guān)閉。
  • EventSubType.CLIENT_DISCONNECT:客戶端連接被關(guān)閉(ERROR 的子類別)。
  • EventSubType.IOEXCEPTION:表示發(fā)生了 IO 異常(比如無(wú)效內(nèi)容),例如無(wú)效的塊阻塞(ERROR 的子類別)。
  • EventSubType.WEBAPP_RELOAD:重新加載 Web 應(yīng)用(END 的子類別)。
  • EventSubType.SESSION_END:Servlet 終止了會(huì)話(END 的子類別)。

如上所述,Comet 請(qǐng)求的典型生命周期會(huì)包含一系列的事件:BEGIN -> READ -> READ -> READ -> ERROR/TIMEOUT。任何時(shí)候,Servlet 都能用事件的 close 方法來(lái)終止對(duì)請(qǐng)求的處理。

2. Comet 過(guò)濾器

跟一般的過(guò)濾器一樣,當(dāng)處理 Comet 事件時(shí),就會(huì)調(diào)用一個(gè)過(guò)濾器隊(duì)列。這些過(guò)濾器應(yīng)該實(shí)現(xiàn) CometFilter 接口(和常用的過(guò)濾器接口一樣),在部署描述符文件中的聲明與映像也都和通常的過(guò)濾器一樣。當(dāng)過(guò)濾器隊(duì)列在處理事件時(shí),它將只含有那些跟所有通常映射規(guī)則相匹配的過(guò)濾器,并且這些過(guò)濾器要實(shí)現(xiàn) CometFilter 接口。

3. 范例代碼

在下面的范例偽碼中,通過(guò)使用上文所述的 API,Servlet 實(shí)現(xiàn)了異步聊天功能。

public class ChatServlet
    extends HttpServlet implements CometProcessor {

    protected ArrayList<HttpServletResponse> connections =
        new ArrayList<HttpServletResponse>();
    protected MessageSender messageSender = null;

    public void init() throws ServletException {
        messageSender = new MessageSender();
        Thread messageSenderThread =
            new Thread(messageSender, "MessageSender[" + getServletContext().getContextPath() + "]");
        messageSenderThread.setDaemon(true);
        messageSenderThread.start();
    }

    public void destroy() {
        connections.clear();
        messageSender.stop();
        messageSender = null;
    }

    /**
     * Process the given Comet event.
     *
     * @param event The Comet event that will be processed
     * @throws IOException
     * @throws ServletException
     */
    public void event(CometEvent event)
        throws IOException, ServletException {
        HttpServletRequest request = event.getHttpServletRequest();
        HttpServletResponse response = event.getHttpServletResponse();
        if (event.getEventType() == CometEvent.EventType.BEGIN) {
            log("Begin for session: " + request.getSession(true).getId());
            PrintWriter writer = response.getWriter();
            writer.println("<!DOCTYPE html>");
            writer.println("<head><title>JSP Chat</title></head><body>");
            writer.flush();
            synchronized(connections) {
                connections.add(response);
            }
        } else if (event.getEventType() == CometEvent.EventType.ERROR) {
            log("Error for session: " + request.getSession(true).getId());
            synchronized(connections) {
                connections.remove(response);
            }
            event.close();
        } else if (event.getEventType() == CometEvent.EventType.END) {
            log("End for session: " + request.getSession(true).getId());
            synchronized(connections) {
                connections.remove(response);
            }
            PrintWriter writer = response.getWriter();
            writer.println("</body></html>");
            event.close();
        } else if (event.getEventType() == CometEvent.EventType.READ) {
            InputStream is = request.getInputStream();
            byte[] buf = new byte[512];
            do {
                int n = is.read(buf); //can throw an IOException
                if (n > 0) {
                    log("Read " + n + " bytes: " + new String(buf, 0, n)
                            + " for session: " + request.getSession(true).getId());
                } else if (n < 0) {
                    error(event, request, response);
                    return;
                }
            } while (is.available() > 0);
        }
    }

    public class MessageSender implements Runnable {

        protected boolean running = true;
        protected ArrayList<String> messages = new ArrayList<String>();

        public MessageSender() {
        }

        public void stop() {
            running = false;
        }

        /**
         * Add message for sending.
         */
        public void send(String user, String message) {
            synchronized (messages) {
                messages.add("[" + user + "]: " + message);
                messages.notify();
            }
        }

        public void run() {

            while (running) {

                if (messages.size() == 0) {
                    try {
                        synchronized (messages) {
                            messages.wait();
                        }
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                }

                synchronized (connections) {
                    String[] pendingMessages = null;
                    synchronized (messages) {
                        pendingMessages = messages.toArray(new String[0]);
                        messages.clear();
                    }
                    // Send any pending message on all the open connections
                    for (int i = 0; i < connections.size(); i++) {
                        try {
                            PrintWriter writer = connections.get(i).getWriter();
                            for (int j = 0; j < pendingMessages.length; j++) {
                                writer.println(pendingMessages[j] + "<br>");
                            }
                            writer.flush();
                        } catch (IOException e) {
                            log("IOExeption sending message", e);
                        }
                    }
                }

            }

        }

    }

}

4. Comet 超時(shí)

如果使用 NIO 連接器,你可以為不同的 comet 連接設(shè)置單獨(dú)的超時(shí)。只需設(shè)置一個(gè)如下所示的請(qǐng)求屬性即可設(shè)置超時(shí):

CometEvent event.... event.setTimeout(30*1000);

event.getHttpServletRequest().setAttribute("org.apache.tomcat.comet.timeout", new Integer(30 * 1000));

超時(shí)被設(shè)置為 30 秒。重要說(shuō)明:為了設(shè)置超時(shí),必須完成 BEGIN 事件。默認(rèn)值為 soTimeout

如果使用 APR 連接器,所有的 Comet 連接將擁有統(tǒng)一的超時(shí)值:soTimeout*50。

異步寫操作

當(dāng) APR 或 NIO 可用時(shí),Tomcat 支持使用 sendfile 方式去發(fā)送大型靜態(tài)文件。只要系統(tǒng)負(fù)載一增加,就會(huì)異步地高效執(zhí)行寫操作。作為一種使用阻塞寫操作發(fā)送大型響應(yīng)的替代方式,有可能使用 sendfile 代碼來(lái)將內(nèi)容寫入靜態(tài)文件。緩存值將利用這一點(diǎn)將響應(yīng)數(shù)據(jù)緩存至文件而非存儲(chǔ)在內(nèi)存中。如果請(qǐng)求屬性 org.apache.tomcat.sendfile.support 設(shè)為 Boolean.TRUE,則表示支持 sendfile。

通過(guò)合適的請(qǐng)求屬性,任何 Servlet 都可以指示 Tomcat 執(zhí)行 sendfile 調(diào)用。正確地設(shè)置響應(yīng)長(zhǎng)度也是很有必要的。在使用 sendfile 時(shí),最好確定請(qǐng)求與響應(yīng)都沒有被包裝起來(lái)。因?yàn)樯院筮B接器本身將發(fā)送響應(yīng)主體,所以不能夠過(guò)濾響應(yīng)主體。除了設(shè)置 3 個(gè)所需的請(qǐng)求屬性之外,Servlet 不應(yīng)該發(fā)送任何響應(yīng)數(shù)據(jù),但能使用一些能夠修改響應(yīng)報(bào)頭的方法(比如設(shè)定 cookie)。

  • org.apache.tomcat.sendfile.filename 作為字符串發(fā)送的標(biāo)準(zhǔn)文件名。
  • org.apache.tomcat.sendfile.start開始位置偏移值,長(zhǎng)整型值。
  • org.apache.tomcat.sendfile.end 結(jié)束位置偏移值,長(zhǎng)整型值。

除了設(shè)置這些屬性,還有必要設(shè)置內(nèi)容長(zhǎng)度報(bào)頭。不要指望 Tomcat 來(lái)處理,因?yàn)槟憧赡芤呀?jīng)將數(shù)據(jù)寫入輸出流了。

注意,使用 sendfile 將禁止 Tomcat 可能在響應(yīng)中執(zhí)行的壓縮操作。

上一篇:MBean 描述符下一篇:CGI