Tomcat响应数据过程
创始人
2024-09-24 19:33:03

        我们在用Tomcat响应数据给客户端的时候,一般会调用如下代码

OutputStreamoutputStream=resp.getOutputStream(); outputStream.write("test".getBytes());

        Tomcat在响应客户端或者接受客户端消息的时候会用到门面模式,所以响应的时候Response的类型其实是ResponseFacade(接收的时候是RequestFacade),得到的socket.getOutputStream()得到的类型是CoyoteOutputStream类型(接受端也是,Coyote是核心类)。CoyoteOutPutStream中有一个属性ob(OutputBuffer),write方法调用的就是ob.writeBytes(),底层代码如下

    private void writeBytes(byte b[], int off, int len) throws IOException {          if (closed) {             return;         }          append(b, off, len);         bytesWritten += len;          if (doFlush) {             //每次把缓冲区的数据发送出去             flushByteBuffer();         }      }

        OutputBuffer中有个属性是bb(ByteChunk类型),里面有一个字节数组buff(8092kb),每次往缓冲区里添加数据的时候,就是往buff中写数据。代码中可以看到必须要满足doFlush的时候才会清空缓冲区,doFlush基本上就是在Servlet代码执行完前开发手动的调用OutputStream.flush()方法就会把它设置成true。这里就需要思考,什么时候会将buff中的数据放到socket中?

        ByteChunk有一个属性out(ByteOutPutChannel),本质上就是一个管道,实现了数据流向到哪里,里面有一个realWriteBytes方法。当我们调用的时候就会把数据往下游(可以先理解为往socket发,实际是往socketbuff中发,第二层缓存)发送。

public void realWriteBytes(ByteBuffer buf) throws IOException {        outputChunk.setBytes(buf,off,cnt);        coyoteResponse.doWrite(buf); }

这里面主要就是通过一个outputChunk来标记这些数据是要传输下去的,真正实现发送功能的就是coyoteResponse.dowrite。

        ByteChunk里面有append方法,来表示往buff中添加数据,当缓冲区大小满了之后,也会触发数据清空操作。还有一种情况就是上述说的,在缓冲区没满的时候,开发手动调用flush方法,也会执行数据清空下发操作。

        当我们调用outputStream.flush()方法的时候

                1.判断是否生成过响应头,有的话就不生了,没有的话说明是第一次发送,那么生成响应头。

                2.因为调用了flush方法,所以会调用ByteChunk的flushBuffer方法,将buff中的数据发出去,然后清空

                3.2中的数据其实是发送给另一个缓存(socketBuffer,但是这个缓存你是可以不用的,通过一些设置),SocketBuffer中也有一个ByteChunk,数据也是存放在这里面的,所以最后做的是将SocketBuffer中的数据发送到socket

        有一个属性useSocketBuffer,默认是false(如果要用必须你将socketBuffer设置大小超过500才会启用,这个可以自己去配置)

        我们最后调用的是coyoteResponse.doWrite方法,这里往下推调用的是outPutBuffer.doWrite方法(执行AbstractOutPutBuffer的逻辑)

                1.doWrite方法会先判断响应头是否已经发送,如果还没有,那么会先构造响应头然后发给socketBuffer

               2.发送完响应头,就会去判断你的数据到底是用Chunk还是ContentLength发送,对应着三个Filter(ContentLength->IdentityFilter,分块响应体->ChunkedFilter,不发送->VoidFilter)

        所以需要确定的是,到底是执行哪个Filter的逻辑,Tomcat的选择逻辑如下:

                Servlet中的所有代码执行完之后,就会调用response.finishResponse()->OutputBuffer.close()。close方法中会去判断响应体是否已经发送过了,如果一直没有发送过,说明缓冲区肯定是没满的,并且玩家没有执行过flush,那么第一缓冲区中的数据就是我们这次要发送的所有数据,所以缓冲区中的数据大小就是我们要的ContentLength大小,如果说在调用close之前已经发送过数据了,那么就会调用ChunkedFIlter的逻辑,比如我们自己调用flush,或者缓冲区满了,就会执行这个逻辑。

                在执行close方法的时候,会先将响应头的数据发送到socketBuffer,然后再将响应体的数据通过对应的OutputFilter发送,最后会调用OutputFilter的end方法,分块发送的时候会循环掉(因为有很多块),IdentityFilter就会一次性全部发送出去。

        上文就是Tomcat响应数据的过程

               

                

相关内容

热门资讯

配音演员集体发文抵制AI,这场... 作者:卢洋 “这是AI生成的播客节目,由AI对文本进行了总结并朗读。” “这是AI参与的漫剧配音,内...
《全球人工智能企业科技创新指数... 3月30日,八月瓜科技创新研究院发布《全球人工智能企业科技创新指数报告2026》,依托全球专利大数据...
腾讯会议这波 AI 功能,让我... 有多少会议的录像,你从来没有回看过? 我想有些人的答案是:几乎全部。 录制文件躺在云端,没有索引,没...
傅氏幻术、瞳真科技入驻石景山中... 4月1日,“文化×科技激活创新力 —— 傅氏魔幻&瞳真科技共筑园区新生态”主题活动在中关村工业互联网...
年薪最高1.24亿元!优必选天... 国内具身智能开出了天价年薪招人。 4月2日,“人形机器人第一股”优必选(09880.HK)公开发布了...