【tomcat调优系列2】Tomcat响应数据过程

Tomcat响应数据过程

关键部件解释

  1. OutputStream: 用于response的输出流。Tomcat这里是CoyoteOutputStream
  2. OutputBuffer: 输出流的缓冲
  3. ByteChunk: OutputBuffer的一个对象,缓冲的,缓冲区。
  4. ByteChunk的buff构成,大小8192.
  5. ByteOutputChannel:ByteChunk的out。缓冲区的数据流向的渠道。其实就是socket.
  6. realWriteBytes方法:ByteOutputChannel的方法。会把src的数据发送给对应的驱动。
  7. org.apache.coyote.Response:发送逻辑

触发缓冲区标记的发送

  1. 缓冲区满的情况:ByteChunk.append -> out.realWriteBytes(src,off,len)。换冲突的大小为8192 ByteChunk
  2. 缓冲区没满:调用outputStream.flush

ouputStream.flush的方法

  1. 判断是否发送过响应头,没发送则发送相应头。
  2. 调用ByteChunk的flushBuffer方法,把缓冲区的数据发送出去。
  3. 发送时候是从 ByteBuffer 发送到SocketBuffer(也是ByteChunk实现的)。SocketBuffer发送给socket

coyoteResponse.doWrite(outputChunk)

  1. 调用方法:outputBuffer.doWrite(chunk, this).OutputBuffer是InternalOutputBuffer。
  2. 该doWrite⽅法中,⾸先会判断响应头是否已经发送,如果没有发送,则会构造响应头,并发响应头发送给 socketBuffer,发送完响应头,会调⽤响应的output的activeFilters,对于不同的响应体需要使⽤不同的 发送逻辑。⽐如ChunkedOutputFilter是⽤来发送分块响应体的,IdentityOutputFilter是⽤来发送 Content-length响应体的,VoidOutputFilter不会真正的把数据发送出去。
  3. 在构造响应头时,会识别响应体应该通过什么OutputFilter来发送,如果响应中存在content-length那么 则使⽤IdentityOutputFilter来发送响应体,否则使⽤ChunkedOutputFilter,当然还有⼀些异常情况下会 使⽤VoidOutputFilter,表示不会发送响应体。

响应的Content-lenth什么时候确定

答案是:当请求在servlet中执⾏完成后,会调⽤response.finishResponse()⽅法,该⽅法会调⽤ outputBuffer.close(),该outputBuffer就是org.apache.catalina.connector.OutputBuffer,该⽅法会 判断响应体是否已发送,如果在调⽤这个close时响应头还没有发送,则表示响应体的数据在之前⼀直没有 发送过,⼀直存在了第⼀层缓冲区中,并且⼀直没有塞满该缓冲区,因为该缓冲区如果被塞满了,则会发 送响应头,所以当执⾏到close⽅法是,响应头还没发送过,那么缓冲区中的数据就是响应体全部的数据, 即,缓冲区数据的⻓度就是content-length。 反之,在调⽤close⽅法之前,就已经发送过数据了,那么响应头中就没有content-length,就会⽤ ChunkedOutputFilter来发送数据。

并且在执⾏close⽅法时,会先将响应头的数据发送给socketbuffer,然后将第⼀层缓冲区的数据通过对应 的OutputFilter发送给socketbuffer,然后调⽤OutputFilter的end⽅法,IdentityOutputFilter的end⽅ 法实现很简单,⽽ChunkedOutputFilter的end⽅法则相对做的事情更多⼀点,因为 ChunkedOutputFilter的doWrite⼀次只会发送⼀块数据,所以end要负责循环调⽤doWrite⽅法,把全部 的数据库发送完。

最后将socketbuffer中的数据发送给socket。