貌似很久都没有写博客了,tomcat8的代码已经看了很多,主体部分的代码也都看得差不多了,发现在tomcat8中已经完全支持非阻塞的方式接收以及发送数据了。。。。但是比较遗憾的是,以前遗留下来的太多的老代码都不支持这种新的方式来发送数据。。。木有办法。。。
这里来看看Tomcat中是如何实现ServletOutputStream的吧。。。。
在具体的来看它之前,这里先来一张图来描述一下Tomcat的数据发送时候的流动。。。
这张图形已经比较好的展现成了Tomcat中对IO这一块封装的层次关系吧,首先是最上层的
ServletOutputStream对象,它是用户代码可以接触到的对象,它自带了自己的OutputBuffer,这里写数据其实是通过这个这个buffer完成的,数据是写到这个OutputBuffer里面
接下来的一层就是Tomcat内部的Response类型了,每一个ServletResponse对象都对应着一个唯一的Tomcat内部的response类型,当然了,他也有自己的buffer,InternalNioOutputBuffer类型的对象,这里上层ServletOutputStream写数据将会流动到这里。。。
最下层就是与channel关联的部分了,它也有自己的buffer,这里一般就是java类库中的niobuffer,上层的数据将会流动到这里。。。
最后才是将数据通过channel发送出去。。。
嗯,虽然好像层次稍微多了些,而且刚刚开始看代码就觉得稍微有点繁琐。。不过当将这个层次关系理清楚了之后还是蛮简单的。。。
好啦,接下来来看代码了。。。
这里就通过一次write来跟踪整个代码的执行轨迹吧,首先来看看CoyoteOutputStream类型的write方法:
//发送一个byte数组的一部分数据
public void write(byte[] b, int off, int len) throws IOException {
boolean nonBlocking = checkNonBlockingWrite(); //判断是否支持非阻塞的发送数据,这里判断的标准其实是用户代码是否加入了WriteListener
ob.write(b, off, len); //将数据写到outputbuffer里面
if (nonBlocking) {
checkRegisterForWrite(); //如果是非阻塞的发送数据的话,需要确保channel有注册写事件
}
}
这里首先要判断是否支持非阻塞的发送数据,这里就不细讲这部分的类容了,,,来看看这里的outputbuffer的write行为吧:
//写入一个字节数组的一部分
public void write(byte b[], int off, int len) throws IOException {
if (suspended) {
return;
}
writeBytes(b, off, len);
}
private void writeBytes(byte b[], int off, int len)
throws IOException {
if (closed) { //如果已经关闭了,那么直接返回吧
return;
}
//这里其实是写到buffer里,如果数据过大,也有可能调用realWriteBytes方法真正的调用底层写数据
bb.append(b, off, len);
bytesWritten += len;
// if called from within flush(), then immediately flush
// remaining bytes
if (doFlush) {
bb.flushBuffer();
}
}
这里其实就是将数据直接放到当年的bytechunk就好了。。。但是这里如果数据比较多的话,会将数据直接写到下一层,也就是tomcat内置的response。。。来看看bytechunk的append方法吧:
//加入一个字节数组的一部分数据
public void append( byte src[], int off, int len )