Comet事件分析
简介【 Introduction 】
使用APR或者NIO API作为连接器的基础,Tomcat能够提供一些在阻塞IO之上的有效扩展,用于支持Servlet API。
【 With usage of APR or NIO APIs as the basis of its connectors, Tomcat is able to provide a number of extensions over the regular blocking IO as provided with support for the Servlet API. 】
重要说明:这些特性的使用需要用到APR或NIO HTTP连接器。java.io HTTP connector 和 AJP连接器是不支持这些特性的。
【 IMPORTANT NOTE: Usage of these features requires using the APR or NIO HTTP connectors. The classic java.io HTTP connector and the AJP connectors do not support them. 】
Comet支持【 Comet support 】
Comet的支持允许servlet进程执行异步IO操作,当数据读取时接收事件(而不是总使用阻塞读取),并且向连接异步的写数据(看起来更像从其他源接收一些事件)。
【 Comet support allows a servlet to process IO asynchronously, receiving events when data is available for reading on the connection (rather than always using a blocking read), and writing data back on connections asynchronously (most likely responding to some event raised from some other source. 】
Comet事件【 CometEvent 】
从org.apache.catalina.CometProcessor 接口实现的Servlet根据事件的发生具有自己的方法调用,而不使用那些通用的service方法。通常情况下,事件对象能够访问通用的request和response对象。主要的区别在于这些对象的有效期是从BEGIN事件直到END或者ERROR事件。事件类型如下:
【 Servlets which implement the org.apache.catalina.CometProcessor interface will have their event method invoked rather than the usual service method, according to the event which occurred. The event object gives access to the usual request and response objects, which may be used in the usual way. The main difference is that those objects remain valid and fully functional at any time between processing of the BEGIN event until processing an END or ERROR event. The following event types exist: 】 EventType.BEGIN:在连接开始时候被调用。常被用来初始化request和response对象的字段。在这个事件结束后,直到END或者ERROR事件开始前,可以用response对象向连接写入数据。 注意 ,这个response对象以及所依赖的OutputStream、Writer对象并不是线程安全的,所以,在多线程访问的情况下,需要强制完成同步工作。在初始化事件结束后,request被慎重的提交。
【 EventType.BEGIN: will be called at the beginning of the processing of the connection. It can be used to initialize any relevant fields using the request and response objects. Between the end of the processing of this event, and the beginning of the processing of the end or error events, it is possible to use the response object to write data on the open connection. Note that the response object and dependent OutputStream and Writer are still not synchronized, so when they are accessed by multiple threads, synchronization is mandatory. After processing the initial event, the request is considered to be committed. 】 EventType.READ:这个类型标识数据可以被有效的读取,并且读取过程不会被阻断。如果这里存在一个阻塞风险,应该使用InputStream或Reader的available和ready去判断:servlet在确认读取有效时去读数据,并产生附加读取去读取数据有效报告。当产生读取错误时,servlet可以通过传递一个exception属性去报告这个错误。抛出一个异常,会产生一个Error事件,并且连接会关闭。逐个的,能够捕获任何错误,在任何数据中使用servlet和事件的close方法,执行清除动作。不允许从servlet对象执行方法外部去读取数据。
【 EventType.READ: This indicates that input data is available, and that one read can be made without blocking. The available and ready methods of the InputStream or Reader may be used to determine if there is a risk of blocking: the servlet should read while data is reported available, and can make one additional read should read while data is reported available. When encountering a read error, the servlet should report it by propagating the exception properly. Throwing an exception will cause the error event to be invoked, and the connection will be closed. Alternately, it is also possible to catch any exception, perform clean up on any data structure the servlet may be using, and using the close method of the event. It is not allowed to attempt reading data from the request object outside of the execution of this method. 】
在某些平台上,比如windows,客户端的断开通过一个READ事件表示。从流中读取数据的结果会返回-1、一个IO异常或者一个EOF异常。确实的处理这三种情况,如果你没有捕获IO异常,Tomcat会调用事件队列生成一个ERROR存储这些错误给你,并且你会马上收到这个消息。
【 On some platforms, like Windows, a client disconnect is indicated by a READ event. Reading from the stream may result in -1, an IOException or an EOFException. Make sure you properly handle all these three cases. If you don't catch the IOException, Tomcat will instantly invoke your event chain with an ERROR as it catches the error for you, and you will be notified of the error at that time. 】 EventType.END:End在请求结束时被调用,在开始时候被初始化的字段这时恢复。在这个事件调用完成后,request、response以及所依赖的对象都将被收回给其他request使用。在request输入时,End总是在数据有效并且到达文件末尾的时候被调用(通常标识客户端使用管道操作提交一个请求)。
【 EventType.END: End may be called to end the processing of the request. Fields that have been initialized in the begin method should be reset. After this event has been processed, the request and response objects, as well as all their dependent objects will be recycled and used to process other requests. End will also be called when data is available and the end of file is reached on the request input (this usually indicates the client has pipelined a request). 】 EventType.ERROR:Error当连接时发生IO异常或者类似的不可恢复的错误时被容器调用。在开始时候被初始化的字段在这时候被恢复。在这个事件调用完成后,request、response以及所依赖的对象都将被收回给其他request使用。
【 EventType.ERROR: Error will be called by the container in the case where an IO exception or a similar unrecoverable error occurs on the connection. Fields that have been initialized in the begin method should be reset. After this event has been processed, the request and response objects, as well as all their dependent objects will be recycled and used to process other requests. 】
还有一些自类型用以更细致的控制进程(注意:有些子类型需要 org.apache.catalina.valves.CometConnectionManagerValve 的值是有效的)
【 There are some event subtypes which allow finer processing of events (note: some of these events require usage of the org.apache.catalina.valves.CometConnectionManagerValve valve): 】 EventSubType.TIMEOUT:连接超时(ERROR的子类型);注意这个ERROR类型不是致命的,连接不会关闭,除非servlet使用事件的close方法。
【EventSubType.TIMEOUT: The connection timed out (sub type of ERROR); note that this ERROR type is not fatal, and the connection will not be closed unless the servlet uses the close method of the event. 】 EventSubType.CLIENT_DISCONNECT:客户端连接关闭(ERROR的子类型)。事件的方法
【 EventSubType.CLIENT_DISCONNECT: The client connection was closed (sub type of ERROR). method of the event. 】 EventSubType.IOEXCEPTION:一个IO异常发生,比如上下文失效,例如,一个阻塞失效(ERROR的子类型)。
【 EventSubType.IOEXCEPTION: An IO exception occurred, such as invalid content, for example, an invalid chunk block (sub type of ERROR). 】 EventSubType.WEBAPP_RELOAD:web应用重载(END的子类型)。
【 EventSubType.WEBAPP_RELOAD: The web application is being reloaded (sub type of END). 】 EventSubType.SESSION_END:Session结束(END的子类型)。
【 EventSubType.SESSION_END: The servlet ended the session (sub type of END). 】
如上所述,一个Comet的典型声明周期经历这么一个过程:BEGIN->READ->READ->READ->ERROR/TIMEOUT。任何时候,servlet通过事件对象的close方法结束一个请求。
As described above, the typical lifecycle of a Comet request will consist in a series of events such as: BEGIN -> READ -> READ -> READ -> ERROR/TIMEOUT. At any time, the servlet may end processing of the request by using the close method of the event object.
Comet过滤器【 CometFilter 】
与规则过滤器类似,在Comet时间被处理时,一个过滤器队列被调用。这些过滤器实现了CometFilter接口(和规则过滤器接口同样的方式工作),并与规则过滤器使用同样的方法在部署描述中声明。事件处理时的过滤器队列只包含那些标记了通常映射规则的过滤器和实现了CometFilter接口的。
【 Similar to regular filters, a filter chain is invoked when comet events are processed. These filters should implement the CometFilter interface (which works in the same way as the regular Filter interface), and should be declared and mapped in the deployment descriptor in the same way as a regular filter. The filter chain when processing an event will only include filters which match all the usual mapping rules, and also implement the CometFiler interface. 】
<!--()--> 范例代码【 Example code 】
下面的Servlet伪代码实现了异步聊天室的功能:
【 The following pseudo code servlet implements asynchronous chat functionality using the API described above: 】
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 public \"-//w3c//dtd html 4.0 transitional//en\">"); writer.println("<head><title>JSP Chat</title></head><body bgcolor=\"#FFFFFF\">"); 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); } } } } } } }
<!--()--> Comet超时【 Comet timeouts 】
如果使用NIO连接器,你能够为每个comet连接器设置独立的超时时间。设置超时时间只需要向下面代码这样设置一个request属性:
【 If you are using the NIO connector, you can set individual timeouts for your different comet connections. To set a timeout, simply set a request attribute like the following code shows: 】
CometEvent event.... event.setTimeout(30*1000);
or
event.getHttpServletRequest().setAttribute("org.apache.tomcat.comet.timeout", new Integer(30 * 1000));
这里设置了30秒的超时时间。切记,为了设置超时,必须完成BEGIN事件。默认值是 soTimeout
【 This sets the timeout to 30 seconds. Important note, in order to set this timeout, it has to be done on the BEGIN event. The default value is soTimeout 】
如果你使用APR连接器,所有的Comet连接都具有同样的超时时间。 soTimeout*50
If you are using the APR connector, all Comet connections will have the same timeout value. It is soTimeout*50
<!--()--> 异步写操作【 Asynchronous writes 】
当APR或者NIO可用时,Tomcat支持使用sendfile方式去发送一个大文件。这种增加系统负荷的写操作异步执行是非常有效的方法。作为发送一个大块的阻塞写操作的替代方式,用一个sendfile代码去异步写一个文件的内容是不错的方式。为response数据用文件作为缓冲比缓冲到内存中要好。当request的org.apache.tomcat.sendfile.support设置为Boolean.TRUE时,Sendfile支持是有效的。
【 When APR or NIO is enabled, Tomcat supports using sendfile to send large static files. These writes, as soon as the system load increases, will be performed asynchronously in the most efficient way. Instead of sending a large response using blocking writes, it is possible to write content to a static file, and write it using a sendfile code. A caching valve could take advantage of this to cache the response data in a file rather than store it in memory. Sendfile support is available if the request attribute org.apache.tomcat.sendfile.support is set to Boolean.TRUE . 】
通过设置恰当的请求属性,servlet能够调用Tomcat去执行一个sendfile过程。这需要为响应正确的设置长度。使用sendfile最好的做法是要确保request和response被包装起来了。因为作为响应的内容会比连接器传输要晚一些,它不能被过滤。出了设置3个必要的request属性,selvlet不去发送任何响应数据,但有很多种方法去修改响应的头(如设置cookie)
【 Any servlet can instruct Tomcat to perform a sendfile call by setting the appropriate request attributes. It is also necessary to correctly set the content length for the response. When using sendfile, it is best to ensure that neither the request or response have been wrapped, since as the response body will be sent later by the connector itself, it cannot be filtered. Other than setting the 3 needed request attributes, the servlet should not send any response data, but it may use any method which will result in modifying the response header (like setting cookies). 】 org.apache.tomcat.sendfile.filename:一个字符串来描述一个文件名称。
【 org.apache.tomcat.sendfile.filename: Canonical filename of the file which will be sent as a String 】 org.apache.tomcat.sendfile.start:开始位置的偏移量,一个Long类型
【 org.apache.tomcat.sendfile.start: Start offset as a Long 】 org.apache.tomcat.sendfile.end: 结束位置的偏移量,一个Long类型
【 org.apache.tomcat.sendfile.end: End offset as a Long 】
-----------------------------------------------------------------------------------
人太笨了,这篇文章搞了我好几个小时,晕死,这要翻译完了不要把我累死呀。希望以后水平好了以后会快一些吧。
转载自:http://www.boyunjian.com/do/article/snapshot.do?uid=com.iteye.icefox-wjx%2Fblog%2F900685
Comet事件分析的更多相关文章
- OneAlert 入门(三)——事件分析
OneAlert 是国内首个 SaaS 模式的云告警平台,集成国内外主流监控/支撑系统,实现一个平台上集中处理所有 IT 事件,提升 IT 可靠性.有了 OneAlert,你可以更快更合理地为事件划分 ...
- OneAlert 入门(二)——事件分析
OneAlert 是国内首个 SaaS 模式的云告警平台,集成国内外主流监控/支撑系统,实现一个平台上集中处理所有 IT 事件,提升 IT 可靠性.有了 OneAlert,你可以更快更合理地为事件划分 ...
- 跨浏览器resize事件分析
resize事件 原生事件分析 window一次resize事件: IE7 触发3次, IE8 触发2次, IE9 触发1次, IE10 触发1次 Chrome 触发1次 FF 触发2次 Opera ...
- GridView事件分析
GridView事件分析 (转) P1默认数据绑定过程 编号 事件名称 作用 E1 DataBinding 数据绑定之前触发,在这个事件之前(第一次生成GridView),GridView不存在行数据 ...
- android 触摸事件分析
背景知识: 触摸屏可以有多个触控点 android中管理触控点通过一个数组来管理,涉及到index和id两个变量, index表示在数组中的下标,id表示这个触控点(pointer)的id,point ...
- jQuery源码解读-事件分析
最原始的事件注册 addEventListener方法大家应该都很熟悉,它是Html元素注册事件最原始的方法.先看下addEventListener方法签名: element.addEventList ...
- DuiLib事件分析(一)——鼠标事件响应
最近在处理DuiLib中自定义列表行元素事件,因为处理方案得不到较好的效果,于是只好一层一层的去剥离DuiLib事件是怎么来的,看能否在某一层截取消息,自己重写. 我这里使用CListContaine ...
- onclick事件分析
有些时候,我们想实现这样的一种效果: <a href="imgs/2.jpg" title="A fireworks display" onc ...
- Android中Preference的使用以及监听事件分析
在Android系统源码中,绝大多数应用程序的UI布局采用了Preference的布局结构,而不是我们平时在模拟器中构建应用程序时使用的View布局结构,例如,Setting模块中布局.当然,凡事都有 ...
随机推荐
- 一次HTTP通信过程
当我们在浏览器地址栏输入一个网址然后回车以后就看到了一个对应网址的网页,那这个过程到底是怎样的?都发生了什么? 大致是这几个步骤 服务器的应答部分是包含应答状态码:比较常见的是 200:ok 403: ...
- 使用Team Explorer Everywhere (TEE) 2015 SDK获取团队项目的签入策略
TFS的代码签入策略与IDE工具紧密相关,例如Visual Studio中设置的签入策略,只会影响Visual Studio的团队资源管理器:如果需要在Eclipse的TEE中启用签入策略,你还需要在 ...
- 配置git使用socks5代理
git config --global http.proxy 'socks5://127.0.0.1:1080' git config --global https.proxy 'socks5://1 ...
- bootstrap-treeview中文API 以及后台JSON数据处理
bootstrap-treeview 简要教程 bootstrap-treeview是一款效果非常酷的基于bootstrap的jQuery多级列表树插件.该jQuery插件基于Twitter Bo ...
- Android ScrollView 去掉 scrollbar 和 阴影
1. 在 layout 里: android:scrollbars="none" android:overScrollMode="never" 2. 代码里 / ...
- 表格Table宽度设置无效的解决方法
表格Table宽度设置无效的解决方法 bootstrap中使用table时发现不管用width赋值方式都无法改变table>td的宽度 解决方法: 设置table:table-layout:fi ...
- SQL中DateTime转换成Varchar样式
SQL中DateTime转换成Varchar样式语句及查询结果:Select CONVERT(varchar(100), GETDATE(), 0): 05 16 2006 10:57AMSelect ...
- 1. Socket网络编程
1. 借助服务器实现小写转大写的程序: 客户端:发送任意小写字母到服务器端. 服务器端:接收小写字母,转为大写,回传给客户端,然后客户端显示到屏幕. #include <stdio.h> ...
- 在PL/SQL里直接插入日期时提示 is not a valid date and time的解决方法
在PL/SQL Developer里直接往表里插入日期格式的数据时,经常会出现" is not a valid date and time"的错误,这是因为Oracle的日期格式和 ...
- iOS 本地时间 / UTC时间 / 时间戳等操作 / 获取当前年月日
//获得当前时间并且转为字符串 - (NSString *)dateTransformToTimeString { NSDate *currentDate = [NSDate date];//获得当前 ...