tomcat connector

tomcat主要由两大核心组件,一个是connector,一个是container。connector负责的是底层的网络通信的实现,而container负责的是上层servlet业务的实现。一个应用服务器的性能很大程度上取决于网络通信模块的实现,因此connector对于tomcat而言是重中之重。

从采用的网络通信技术来看,connector可分为:

 

  • JIoEndpoint,基于java bio实现,特点是每建立一个连接分配一个线程,读数据阻塞。
  • NioEndpoint,使用java nio实现,使用反应器模式,线程和连接解绑,多路复用。
  • AprEndpoint,使用Apache Portable Runtime实现,直接调用native方法,有更高的效率,但是实现依赖具体平台。
JIoEndpoint的实现
启动一个org.apache.tomcat.util.net.JIoEndpoint.Acceptor线程,用于接收用户的连接请求,当连接成功后,会把创建的socket传递给org.apache.tomcat.util.net.JIoEndpoint.SocketProcessor,由线程池去执行SocketProcessor。Acceptor还具有管理连接数的功能,当连接数达到上限时,会阻塞当前的请求。这里提到的连接数和线程池的容量都是在tomcat_home\conf\server.xml里面配置的。
 
Acceptor核心代码片段如下
 
Java代码   收藏代码
  1. int errorDelay = 0;  
  2.   
  3. // Loop until we receive a shutdown command  
  4. while (running) {  
  5.   
  6.     // Loop if endpoint is paused  
  7.     while (paused && running) {  
  8.         state = AcceptorState.PAUSED;  
  9.         try {  
  10.             Thread.sleep(50);  
  11.         } catch (InterruptedException e) {  
  12.             // Ignore  
  13.         }  
  14.     }  
  15.   
  16.     if (!running) {  
  17.         break;  
  18.     }  
  19.     state = AcceptorState.RUNNING;  
  20.   
  21.     try {  
  22.         //if we have reached max connections, wait  
  23.         countUpOrAwaitConnection();  
  24.   
  25.         Socket socket = null;  
  26.         try {  
  27.             // Accept the next incoming connection from the server  
  28.             // socket  
  29.             socket = serverSocketFactory.acceptSocket(serverSocket);  
  30.         } catch (IOException ioe) {  
  31.             // Introduce delay if necessary  
  32.             errorDelay = handleExceptionWithDelay(errorDelay);  
  33.             // re-throw  
  34.             throw ioe;  
  35.         }  
  36.         // Successful accept, reset the error delay  
  37.         errorDelay = 0;  
  38.   
  39.         // Configure the socket  
  40.         if (running && !paused && setSocketOptions(socket)) {  
  41.             // Hand this socket off to an appropriate processor  
  42.             if (!processSocket(socket)) {  
  43.                 // Close socket right away  
  44.                 closeSocket(socket);  
  45.             }  
  46.         } else {  
  47.             // Close socket right away  
  48.             closeSocket(socket);  
  49.         }  
  50.     } catch (IOException x) {  
  51.         if (running) {  
  52.             log.error(sm.getString("endpoint.accept.fail"), x);  
  53.         }  
  54.     } catch (NullPointerException npe) {  
  55.         if (running) {  
  56.             log.error(sm.getString("endpoint.accept.fail"), npe);  
  57.         }  
  58.     } catch (Throwable t) {  
  59.         ExceptionUtils.handleThrowable(t);  
  60.         log.error(sm.getString("endpoint.accept.fail"), t);  
  61.     }  
  62. }  
  63. state = AcceptorState.ENDED;  
 
processSocket
 
Java代码   收藏代码
  1. // Process the request from this socket  
  2.       try {  
  3.           SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);  
  4.           wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());  
  5.           // During shutdown, executor may be null - avoid NPE  
  6.           if (!running) {  
  7.               return false;  
  8.           }  
  9.           getExecutor().execute(new SocketProcessor(wrapper));  
  10.       } catch (RejectedExecutionException x) {  
  11.           log.warn("Socket processing request was rejected for:"+socket,x);  
  12.           return false;  
  13.       } catch (Throwable t) {  
  14.           ExceptionUtils.handleThrowable(t);  
  15.           // This means we got an OOM or similar creating a thread, or that  
  16.           // the pool and its queue are full  
  17.           log.error(sm.getString("endpoint.process.fail"), t);  
  18.           return false;  
  19.       }  
  20.       return true;  
  21.     
 
org.apache.tomcat.util.net.JIoEndpoint.SocketProcessor的职责是把具体的请求处理过程委派给org.apache.tomcat.util.net.JIoEndpoint.Handler,然后根据handler返回的不同SocketState,来决定是否关闭连接或者进行下一轮处理。 
 
Java代码   收藏代码
  1. boolean launch = false;  
  2. synchronized (socket) {  
  3.     try {  
  4.         SocketState state = SocketState.OPEN;  
  5.   
  6.         try {  
  7.             // SSL handshake  
  8.             serverSocketFactory.handshake(socket.getSocket());  
  9.         } catch (Throwable t) {  
  10.             ExceptionUtils.handleThrowable(t);  
  11.             if (log.isDebugEnabled()) {  
  12.                 log.debug(sm.getString("endpoint.err.handshake"), t);  
  13.             }  
  14.             // Tell to close the socket  
  15.             state = SocketState.CLOSED;  
  16.         }  
  17.   
  18.         if ((state != SocketState.CLOSED)) {  
  19.             if (status == null) {  
  20.                 state = handler.process(socket, SocketStatus.OPEN);  
  21.             } else {  
  22.                 state = handler.process(socket,status);  
  23.             }  
  24.         }  
  25.         if (state == SocketState.CLOSED) {  
  26.             // Close socket  
  27.             if (log.isTraceEnabled()) {  
  28.                 log.trace("Closing socket:"+socket);  
  29.             }  
  30.             countDownConnection();  
  31.             try {  
  32.                 socket.getSocket().close();  
  33.             } catch (IOException e) {  
  34.                 // Ignore  
  35.             }  
  36.         } else if (state == SocketState.OPEN ||  
  37.                 state == SocketState.UPGRADING  ||  
  38.                 state == SocketState.UPGRADED){  
  39.             socket.setKeptAlive(true);  
  40.             socket.access();  
  41.             launch = true;  
  42.         } else if (state == SocketState.LONG) {  
  43.             socket.access();  
  44.             waitingRequests.add(socket);  
  45.         }  
  46.     } finally {  
  47.         if (launch) {  
  48.             try {  
  49.                 getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN));  
  50.             } catch (NullPointerException npe) {  
  51.                 if (running) {  
  52.                     log.error(sm.getString("endpoint.launch.fail"),  
  53.                             npe);  
  54.                 }  
  55.             }  
  56.         }  
  57.     }  
  58. }  
  59. socket = null;  
  60. // Finish up this request  
 
 
其中handler根据所采用协议的不同,可以分为:
  • AjpConnectionHandler,当我们的服务器架构是前端服务器(apache or nginx)+tomcat服务器的时候。用户的请求先到前端服务器,再由前端服务器通过ajp协议和tomcat通信,由tomcat去执行应用的逻辑。使用这种架构的好处是提高性能,前端服务器在管理连接、解析http请求、压缩响应http请求方面性能优于tomcat。
  • Http11ConnectionHandler,当应用服务器直接暴露给用户访问时,就会使用这个handler,由tomcat直接负责解析、处理、响应http请求。
下面我们以Http11ConnectionHandler为例来看之后的请求处理过程。




 
 
首先在Http11Processor的process方法里,会先从socket里读取http请求数据,并解析请求头,构造httprequest对象,然后调用Adapter.service()。Adapter.service()是connector和container的桥梁,经过这一步,请求就从connector传递到container里了,Adapter.service之后便是filter和servlet的执行逻辑了。对于普通的servlet来说,最后Http11ConnectionHandler会返回SocketState.CLOSED的状态,然后SocketProcessor关闭连接,容器线程回收。
 
2 NioEndpoint的实现

NioEndpoint是基于java nio机制的,它的特点是采用了异步io经典的reactor模式,无阻塞解析http请求,大大提高了性能。和JioEndpoint一样,它也有一个线程专门负责接收用户连接请求org.apache.tomcat.util.net.NioEndpoint.Acceptor。实现上也和Jio的类似,在一个线程里循环调用java.nio.channels.ServerSocketChannel.accept()接收连接,并维护容器连接数。当接收到一个连接后,就把SocketChannel注册到reactor里面,这里的reactor称为Poller。

 

Acceptor的核心逻辑

 

Java代码   收藏代码
  1. int errorDelay = 0;  
  2.   
  3. // Loop until we receive a shutdown command  
  4. while (running) {  
  5.   
  6.     // Loop if endpoint is paused  
  7.     while (paused && running) {  
  8.         state = AcceptorState.PAUSED;  
  9.         try {  
  10.             Thread.sleep(50);  
  11.         } catch (InterruptedException e) {  
  12.             // Ignore  
  13.         }  
  14.     }  
  15.   
  16.     if (!running) {  
  17.         break;  
  18.     }  
  19.     state = AcceptorState.RUNNING;  
  20.   
  21.     try {  
  22.         //if we have reached max connections, wait  
  23.         countUpOrAwaitConnection();  
  24.   
  25.         SocketChannel socket = null;  
  26.         try {  
  27.             // Accept the next incoming connection from the server  
  28.             // socket  
  29.             socket = serverSock.accept();  
  30.         } catch (IOException ioe) {  
  31.             // Introduce delay if necessary  
  32.             errorDelay = handleExceptionWithDelay(errorDelay);  
  33.             // re-throw  
  34.             throw ioe;  
  35.         }  
  36.         // Successful accept, reset the error delay  
  37.         errorDelay = 0;  
  38.   
  39.         // setSocketOptions() will add channel to the poller  
  40.         // if successful  
  41.         if (running && !paused) {  
  42.             if (!setSocketOptions(socket)) {  
  43.                 closeSocket(socket);  
  44.             }  
  45.         } else {  
  46.             closeSocket(socket);  
  47.         }  
  48.     } catch (SocketTimeoutException sx) {  
  49.         // Ignore: Normal condition  
  50.     } catch (IOException x) {  
  51.         if (running) {  
  52.             log.error(sm.getString("endpoint.accept.fail"), x);  
  53.         }  
  54.     } catch (OutOfMemoryError oom) {  
  55.         try {  
  56.             oomParachuteData = null;  
  57.             releaseCaches();  
  58.             log.error("", oom);  
  59.         }catch ( Throwable oomt ) {  
  60.             try {  
  61.                 try {  
  62.                     System.err.println(oomParachuteMsg);  
  63.                     oomt.printStackTrace();  
  64.                 }catch (Throwable letsHopeWeDontGetHere){  
  65.                     ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);  
  66.                 }  
  67.             }catch (Throwable letsHopeWeDontGetHere){  
  68.                 ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);  
  69.             }  
  70.         }  
  71.     } catch (Throwable t) {  
  72.         ExceptionUtils.handleThrowable(t);  
  73.         log.error(sm.getString("endpoint.accept.fail"), t);  
  74.     }  
  75. }  
  76. state = AcceptorState.ENDED;  
 

 

 

Java代码   收藏代码
  1. protected boolean setSocketOptions(SocketChannel socket) {  
  2.       // Process the connection  
  3.       try {  
  4.           //disable blocking, APR style, we are gonna be polling it  
  5.           socket.configureBlocking(false);  
  6.           Socket sock = socket.socket();  
  7.           socketProperties.setProperties(sock);  
  8.   
  9.           NioChannel channel = nioChannels.poll();  
  10.           if ( channel == null ) {  
  11.               // SSL setup  
  12.               if (sslContext != null) {  
  13.                   SSLEngine engine = createSSLEngine();  
  14.                   int appbufsize = engine.getSession().getApplicationBufferSize();  
  15.                   NioBufferHandler bufhandler = new NioBufferHandler(Math.max(appbufsize,socketProperties.getAppReadBufSize()),  
  16.                                                                      Math.max(appbufsize,socketProperties.getAppWriteBufSize()),  
  17.                                                                      socketProperties.getDirectBuffer());  
  18.                   channel = new SecureNioChannel(socket, engine, bufhandler, selectorPool);  
  19.               } else {  
  20.                   // normal tcp setup  
  21.                   NioBufferHandler bufhandler = new NioBufferHandler(socketProperties.getAppReadBufSize(),  
  22.                                                                      socketProperties.getAppWriteBufSize(),  
  23.                                                                      socketProperties.getDirectBuffer());  
  24.   
  25.                   channel = new NioChannel(socket, bufhandler);  
  26.               }  
  27.           } else {  
  28.               channel.setIOChannel(socket);  
  29.               if ( channel instanceof SecureNioChannel ) {  
  30.                   SSLEngine engine = createSSLEngine();  
  31.                   ((SecureNioChannel)channel).reset(engine);  
  32.               } else {  
  33.                   channel.reset();  
  34.               }  
  35.           }  
  36.           getPoller0().register(channel);  
  37.       } catch (Throwable t) {  
  38.           ExceptionUtils.handleThrowable(t);  
  39.           try {  
  40.               log.error("",t);  
  41.           } catch (Throwable tt) {  
  42.               ExceptionUtils.handleThrowable(t);  
  43.           }  
  44.           // Tell to close the socket  
  45.           return false;  
  46.       }  
  47.       return true;  
  48.   }  
 

 

在NioEndpoint启动时,会实例化N个org.apache.tomcat.util.net.NioEndpoint.Poller(N为cpu核数)。这是因为在Poller里面无IO等待,所以最优吞吐量的Poller线程个数等于cpu核数。

Poller封装了java nio的就绪选择器java.nio.channels.Selector,实现了一个经典的反应器模式。

Acceptor建立好的socket连接会在Poller注册一个读就绪事件。Poller在一个while里,循环调用java.nio.channels.Selector.select(long),当有读事件就绪时,即http请求数据到达时,则从返回的selectedKeys里拿到socket进行后续处理。

Poller的核心代码

 

 

Java代码   收藏代码
  1. // Loop until destroy() is called  
  2. while (true) {  
  3.     try {  
  4.         // Loop if endpoint is paused  
  5.         while (paused && (!close) ) {  
  6.             try {  
  7.                 Thread.sleep(100);  
  8.             } catch (InterruptedException e) {  
  9.                 // Ignore  
  10.             }  
  11.         }  
  12.   
  13.         boolean hasEvents = false;  
  14.   
  15.         // Time to terminate?  
  16.         if (close) {  
  17.             events();  
  18.             timeout(0false);  
  19.             try {  
  20.                 selector.close();  
  21.             } catch (IOException ioe) {  
  22.                 log.error(sm.getString(  
  23.                         "endpoint.nio.selectorCloseFail"), ioe);  
  24.             }  
  25.             break;  
  26.         } else {  
  27.             hasEvents = events();  
  28.         }  
  29.         try {  
  30.             if ( !close ) {  
  31.                 if (wakeupCounter.getAndSet(-1) > 0) {  
  32.                     //if we are here, means we have other stuff to do  
  33.                     //do a non blocking select  
  34.                     keyCount = selector.selectNow();  
  35.                 } else {  
  36.                     keyCount = selector.select(selectorTimeout);  
  37.                 }  
  38.                 wakeupCounter.set(0);  
  39.             }  
  40.             if (close) {  
  41.                 events();  
  42.                 timeout(0false);  
  43.                 try {  
  44.                     selector.close();  
  45.                 } catch (IOException ioe) {  
  46.                     log.error(sm.getString(  
  47.                             "endpoint.nio.selectorCloseFail"), ioe);  
  48.                 }  
  49.                 break;  
  50.             }  
  51.         } catch ( NullPointerException x ) {  
  52.             //sun bug 5076772 on windows JDK 1.5  
  53.             if ( log.isDebugEnabled() ) log.debug("Possibly encountered sun bug 5076772 on windows JDK 1.5",x);  
  54.             if ( wakeupCounter == null || selector == null ) throw x;  
  55.             continue;  
  56.         } catch ( CancelledKeyException x ) {  
  57.             //sun bug 5076772 on windows JDK 1.5  
  58.             if ( log.isDebugEnabled() ) log.debug("Possibly encountered sun bug 5076772 on windows JDK 1.5",x);  
  59.             if ( wakeupCounter == null || selector == null ) throw x;  
  60.             continue;  
  61.         } catch (Throwable x) {  
  62.             ExceptionUtils.handleThrowable(x);  
  63.             log.error("",x);  
  64.             continue;  
  65.         }  
  66.         //either we timed out or we woke up, process events first  
  67.         if ( keyCount == 0 ) hasEvents = (hasEvents | events());  
  68.   
  69.         Iterator<SelectionKey> iterator =  
  70.             keyCount > 0 ? selector.selectedKeys().iterator() : null;  
  71.         // Walk through the collection of ready keys and dispatch  
  72.         // any active event.  
  73.         while (iterator != null && iterator.hasNext()) {  
  74.             SelectionKey sk = iterator.next();  
  75.             KeyAttachment attachment = (KeyAttachment)sk.attachment();  
  76.             // Attachment may be null if another thread has called  
  77.             // cancelledKey()  
  78.             if (attachment == null) {  
  79.                 iterator.remove();  
  80.             } else {  
  81.                 attachment.access();  
  82.                 iterator.remove();  
  83.                 processKey(sk, attachment);  
  84.             }  
  85.         }//while  
  86.   
  87.         //process timeouts  
  88.         timeout(keyCount,hasEvents);  
  89.         if ( oomParachute > 0 && oomParachuteData == null ) checkParachute();  
  90.     } catch (OutOfMemoryError oom) {  
  91.         try {  
  92.             oomParachuteData = null;  
  93.             releaseCaches();  
  94.             log.error("", oom);  
  95.         }catch ( Throwable oomt ) {  
  96.             try {  
  97.                 System.err.println(oomParachuteMsg);  
  98.                 oomt.printStackTrace();  
  99.             }catch (Throwable letsHopeWeDontGetHere){  
  100.                 ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);  
  101.             }  
  102.         }  
  103.     }  
  104. }//while  
  105. synchronized (this) {  
  106.     this.notifyAll();  
  107. }  
  108. stopLatch.countDown();  
 

 

Java代码   收藏代码
  1. public boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) {  
  2.     try {  
  3.         KeyAttachment attachment = (KeyAttachment)socket.getAttachment(false);  
  4.         if (attachment == null) {  
  5.             return false;  
  6.         }  
  7.         attachment.setCometNotify(false); //will get reset upon next reg  
  8.         SocketProcessor sc = processorCache.poll();  
  9.         if ( sc == null ) sc = new SocketProcessor(socket,status);  
  10.         else sc.reset(socket,status);  
  11.         if ( dispatch && getExecutor()!=null ) getExecutor().execute(sc);  
  12.         else sc.run();  
  13.     } catch (RejectedExecutionException rx) {  
  14.         log.warn("Socket processing request was rejected for:"+socket,rx);  
  15.         return false;  
  16.     } catch (Throwable t) {  
  17.         ExceptionUtils.handleThrowable(t);  
  18.         // This means we got an OOM or similar creating a thread, or that  
  19.         // the pool and its queue are full  
  20.         log.error(sm.getString("endpoint.process.fail"), t);  
  21.         return false;  
  22.     }  
  23.     return true;  
  24. }  

 

对socket处理在processSocket方法里进行,可以在当前线程中处理,也可以分发到线程池里处理。具体处理逻辑在org.apache.tomcat.util.net.NioEndpoint.SocketProcessor里面。对于普通的servlet请求来说,处理完成后,会返回SocketState.CLOSED。然后在SocketProcessor调用org.apache.tomcat.util.net.NioEndpoint.Poller.cancelledKey(SelectionKey, SocketStatus, boolean)关闭socket连接,时序图如下。

 

从NioEndpoint的实现原理可以看出,非阻塞读写和反应器模式,可以让NioEndpoint在以少量线程的条件下,并发处理大量的请求。特别是在使用长连接的场景下,反应器模式的多路复用方式,使得不需要给每个连接分配一个线程,这样就不会因为容器同时维护大量长连接而耗尽线程资源。这也就是为什么tomcat采用了NioEndpoint来实现servlet3中的Async servlet和comet。

我们先来看看Async servlet的实现,当需要在响应数据之前回收容器线程时就可以使用Async servlet。使用Async servlet需要把servlet配置为支持异步,例如

 

Xml代码   收藏代码
  1. <servlet>  
  2.     <servlet-name>asyncServlet</servlet-name>  
  3.     <servlet-class>com.longji.web.AsyncServlet</servlet-class>  
  4. t;async-supported>true</async-supported>  
  5.     <load-on-startup>1</load-on-startup>  
  6. </servlet>  

 另外在在servlet的处理逻辑里,需要调用javax.servlet.ServletRequest.startAsync(ServletRequest, ServletResponse),这样一来这个连接就被标记为Async。示例代码

 

 

Java代码   收藏代码
  1. protected void doGet(HttpServletRequest request,  
  2.         HttpServletResponse response) throws ServletException, IOException {  
  3.     response.setHeader("Cache-Control""private");  
  4.     response.setHeader("Pragma""no-cache");  
  5.     response.setHeader("Connection""Keep-Alive");  
  6.     response.setHeader("Proxy-Connection""Keep-Alive");  
  7.     response.setContentType("text/html;charset=UTF-8");  
  8.   
  9.     PrintWriter out = response.getWriter();  
  10.     out.println("Start ...          ");  
  11.     out.flush();  
  12.   
  13.     if (!request.isAsyncSupported()) {  
  14.         log.info("the servlet is not supported Async");  
  15.         return;  
  16.     }  
  17.   
  18.     request.startAsync(request, response);  
  19.   
  20.     if (request.isAsyncStarted()) {  
  21.         AsyncContext asyncContext = request.getAsyncContext();  
  22.         asyncContext.setTimeout(1L * 100000L * 1000L);// 60sec  
  23.           
  24.         new CounterThread(asyncContext).start();  
  25.     } else {  
  26.         log.error("the ruquest is not AsyncStarted !");  
  27.     }  
  28. }  

 可以看到,在servlet里面另起一个线程处理请求,这个线程持有一个AsyncContext asyncContext = request.getAsyncContext(),通过AsyncContext ,CounterThread异步线程可以拿到ServletRequest和ServletResponse,在完成业务处理之后,可以向客户端响应数据。

Async servlet的处理过程可以分为两个阶段,第一个阶段和普通servlet类似,直至调用了ServletRequest.startAsync,这个连接将被标记为Async的,并且在NioProcessor中返回SocketState.LONG,这样当容器线程回收的时候就不会关闭socket连接。


另外一个阶段是在业务处理结束后调用javax.servlet.AsyncContext.complete()的时候触发的。最终调用org.apache.tomcat.util.net.NioEndpoint.processSocket(NioChannel, SocketStatus, boolean)。




 
 
 org.apache.tomcat.util.net.NioEndpoint.processSocket(NioChannel, SocketStatus, boolean)处理流程如下图。

从时序图可以看出,请求又重新进入了SocketProcessor的处理流程,async请求经过几番状态变迁后,最后返回SocketState.CLOSED状态,由Poller关闭连接。

 

如果是使用comet servlet,需要在servlet里面实现CometProcessor接口,在com.longji.web.CometProcessor.event(CometEvent)方法里编写对应四种事件的处理逻辑。

示例程序

 

Java代码   收藏代码
  1. public void event(CometEvent event)  
  2.     throws IOException, ServletException {  
  3.     HttpServletRequest request = event.getHttpServletRequest();  
  4.     HttpServletResponse response = event.getHttpServletResponse();  
  5.     if (event.getEventType() == CometEvent.EventType.BEGIN) {  
  6.         log("Begin for session: " + request.getSession(true).getId());  
  7.         PrintWriter writer = response.getWriter();  
  8.         writer.println("<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">");  
  9.         writer.println("<head><title>JSP Chat</title></head><body bgcolor=\"#FFFFFF\">");  
  10.         writer.flush();  
  11.         synchronized(connections) {  
  12.             connections.add(response);  
  13.         }  
  14.           
  15.     } else if (event.getEventType() == CometEvent.EventType.ERROR) {  
  16.         log("Error for session: " + request.getSession(true).getId());  
  17.         synchronized(connections) {  
  18.             connections.remove(response);  
  19.         }  
  20.         event.close();  
  21.     } else if (event.getEventType() == CometEvent.EventType.END) {  
  22.         log("End for session: " + request.getSession(true).getId());  
  23.         synchronized(connections) {  
  24.             connections.remove(response);  
  25.         }  
  26.         PrintWriter writer = response.getWriter();  
  27.         writer.println("</body></html>");  
  28.         event.close();  
  29.     } else if (event.getEventType() == CometEvent.EventType.READ) {  
  30.         InputStream is = request.getInputStream();  
  31.         byte[] buf = new byte[512];  
  32.         do {  
  33.             int n = is.read(buf); //can throw an IOException  
  34.             if (n > 0) {  
  35.                 log("Read " + n + " bytes: " + new String(buf, 0, n)   
  36.                         + " for session: " + request.getSession(true).getId());  
  37.             } else if (n < 0) {  
  38.                 //error(event, request, response);  
  39.                 return;  
  40.             }  
  41.         } while (is.available() > 0);  
  42.     }  
  43. }  

 

comet包含四个事件,Begin、Read、End、Error。

Begin事件的处理过程和普通的servlet无异,由org.apache.coyote.Adapter.service(Request, Response)进入container处理逻辑,构造Begin事件,最后调用com.longji.web.ChatServlet.event(CometEvent),并返回SocketState.LONG至SocketProcessor,保持和客户端连接。

当客户端下一个请求到达时,会触发Poller的读就绪事件,事件会分发给SocketProcessor处理,处理流程如下时序图



 在org.apache.coyote.Adapter.event(Request, Response, SocketStatus)会构造读事件,调用com.longji.web.ChatServlet.event(CometEvent)。

当客户端和服务端一次完整的交互结束时,业务代码可以主动调用org.apache.catalina.comet.CometEvent.close(),这个方法会将Http11NioProcessor的comet标记为false,这样一来Http11NioProcessor.event则返回SocketState.CLOSED,SocketProcessor会执行关闭socket操作,如下图


 

小tip

char 2 byte



 

 如果对于同一个response,先调用getWriter,再调用getOutputStream,会抛非法状态异常

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值