Tomcat处理响应过程

读取数据原理类似,getOutputStream方法调用的是Tomcat底层ResponsegetOutputStream方法,最终是通过CoyoteOutputStream方法的write方法去写数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
OutputStream outputStream = resp.getOutputStream();
outputStream.write("test".getBytes());
outputStream.flush();
outputStream.write("test2222".getBytes());
}

public ServletOutputStream getOutputStream() throws IOException {
if (usingWriter) {
throw new IllegalStateException
(sm.getString("coyoteResponse.getOutputStream.ise"));
}
usingOutputStream = true;
if (outputStream == null) {
// outputBuffer就是输出缓冲区
outputStream = new CoyoteOutputStream(outputBuffer);
}
return outputStream;
}
public class CoyoteOutputStream extends ServletOutputStream {
protected OutputBuffer ob;
@Override
public void write(byte[] b)
throws IOException {
write(b, 0, b.length);
}
@Override
public void write(byte[] b, int off, int len)
throws IOException {
ob.write(b, off, len);
}
}

public class OutputBuffer extends Writer implements ByteChunk.ByteOutputChannel, CharChunk.CharOutputChannel {
private final ByteChunk bb;
private byte[] buff;
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;
}
// 将数据先写入ByteChunk的缓冲区中, 如果缓冲区满了,可能会把数据发送出去
bb.append(b, off, len);
bytesWritten += len;
// 如果此前已经调用过flush方法
if (doFlush) {
// 那么每次write都把缓冲中的数据发送出去
bb.flushBuffer();
}
}

public void append(byte src[], int off, int len) throws IOException {
// will grow, up to limit
// 向缓冲区中添加数据,需要开辟缓存区空间,缓存区初始大小为256,最大大小可以设置,默认为8192
// 意思是现在要想缓冲区存放数据,首先得去开辟空间,但是空间是有一个最大限制的,所以要存放的数据可能小于限制,也可能大于限制
makeSpace(len);
int limit = getLimitInternal(); // 缓冲区大小的最大限制
// 如果要添加到缓冲区中的数据大小正好等于最大限制,并且缓冲区是空的,那么则直接把数据发送给out,不要存在缓冲区中了
if (optimizedWrite && len == limit && end == start && out != null) {
out.realWriteBytes(src, off, len);
return;
}
// 如果要发送的数据长度小于缓冲区中剩余空间,则把数据填充到剩余空间
if (len <= limit - end) {
System.arraycopy(src, off, buff, end, len);
end += len;
return;
}
// 如果要发送的数据长度大于缓冲区中剩余空间,
// Need more space than we can afford, need to flush buffer.
// The buffer is already at (or bigger than) limit.
// We chunk the data into slices fitting in the buffer limit, although
// if the data is written directly if it doesn't fit.
// 缓冲区中还能容纳avail个字节的数据
int avail = limit - end;
// 先将一部分数据复制到buff,填满缓冲区
System.arraycopy(src, off, buff, end, avail);
end += avail;
// 将缓冲区的数据发送出去
flushBuffer();
// 还剩下一部分数据没有放到缓冲区中的
int remain = len - avail;
// 如果剩下的数据 超过 缓冲区剩余大小,那么就把数据直接发送出去
while (remain > (limit - end)) {
out.realWriteBytes(src, (off + len) - remain, limit - end);
remain = remain - (limit - end);
}
// 知道最后剩下的数据能放入缓冲区,那么就放入到缓冲区
System.arraycopy(src, (off + len) - remain, buff, end, remain);
end += remain;
}

public void realWriteBytes(byte buf[], int off, int cnt) throws IOException {
if (closed) {
return;
}
if (coyoteResponse == null) {
return;
}
if (cnt > 0) {
outputChunk.setBytes(buf, off, cnt);
try {
coyoteResponse.doWrite(outputChunk);
} catch (IOException e) {
throw new ClientAbortException(e);
}
}
}
}

最终调用的底层的coyote包下面Response中的doWrite方法来写数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public final class Response {
public void doWrite(ByteChunk chunk) throws IOException {
// 把chunk中的数据写入InternalOutputBuffer
outputBuffer.doWrite(chunk, this);
contentWritten+=chunk.getLength();
}
}

public abstract class AbstractOutputBuffer<S> implements OutputBuffer{
public int doWrite(ByteChunk chunk, Response res) throws IOException {
// 没有发送响应头,则先发送响应头
if (!committed) {
// Send the connector a request for commit. The connector should
// then validate the headers, send them (using sendHeaders) and
// set the filters accordingly.
response.action(ActionCode.COMMIT, null);
}
// chunk, content-
// 通过outputStreamOutputBuffer发送数据,可能会再次先发到缓冲区,也可能直接发送socket
// 在发送响应头的时候,会设置ActiveFilter,如果没有则直接发给outputStreamOutputBuffer,如果有先经过ActiveFilter
if (lastActiveFilter == -1)
return outputStreamOutputBuffer.doWrite(chunk, res);
else
return activeFilters[lastActiveFilter].doWrite(chunk, res);
}
}
protected class OutputStreamOutputBuffer implements OutputBuffer {
public int doWrite(ByteChunk chunk, Response res) throws IOException {
try {
int length = chunk.getLength();
// 如果再次发送到缓冲区中,则该缓冲区慢了之后就会发送,或者当前请求要结束时发送
if (useSocketBuffer) {
socketBuffer.append(chunk.getBuffer(), chunk.getStart(), length);
} else {
outputStream.write(chunk.getBuffer(), chunk.getStart(), length);
}
byteCount += chunk.getLength();
return chunk.getLength();
} catch (IOException ioe) {
response.action(ActionCode.CLOSE_NOW, ioe);
throw ioe;
}
}
}

与读数据有点区别在于,outputStream.write方法并不是直接将数据写到操作系统中的sendBuf,而是在中间有两层缓冲区outputBuffersocketBuffer,首先数据会进入到outputBuffer,当outputBuffer满了后会进入到socketBuffer,是否进入socketBuffer是有有点的条件的,当socketBuffer满了后会将数据写入到操作系统的sendBuf发送给客户端,当显示的调用flush方法或者close方法也会将数据写入sendBuf发送给客户端。当然如果程序执行完了后,若数据未发送出去,会进行相关的处理将数据写入sendBuf发送给客户端。

当显示调用flush方法是会生成响应头将数据发送出去,再次发送时就不会在发送响应头了,数据以chunk的方式发送数据,而不是以content-length的方式发送数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
public class CoyoteOutputStream extends ServletOutputStream {
protected OutputBuffer ob;
public void flush() throws IOException {
ob.flush();
}
}
public class OutputBuffer extends Writer implements ByteChunk.ByteOutputChannel, CharChunk.CharOutputChannel {
public void flush() throws IOException {
doFlush(true);
}
protected void doFlush(boolean realFlush) throws IOException {
if (suspended) {
return;
}
try {
doFlush = true;
if (initial) {
// 先发送请求头,再发送请求体
coyoteResponse.sendHeaders();
initial = false;
}
if (cb.getLength() > 0) {
cb.flushBuffer();
}
if (bb.getLength() > 0) {
// 这里只是把上层缓冲区中的数据发送到底层缓冲区中,所以数据到底会不会发送给socket并不确定
bb.flushBuffer();
}
} finally {
doFlush = false;
}
if (realFlush) {
// 如果是真正flush,把底层缓冲区中的数据发送给socket
coyoteResponse.action(ActionCode.CLIENT_FLUSH, null);
if (coyoteResponse.isExceptionPresent()) {
throw new ClientAbortException(coyoteResponse.getErrorException());
}
}
}
}
public final class Response {
public void sendHeaders() {
action(ActionCode.COMMIT, this);
setCommitted(true);
}

public void action(ActionCode actionCode, Object param) {
if (hook != null) {
if (param == null) {
hook.action(actionCode, this);
} else {
hook.action(actionCode, param);
}
}
}
}
public abstract class AbstractHttp11Processor<S> extends AbstractProcessor<S> {
public final void action(ActionCode actionCode, Object param) {

switch (actionCode) {
case CLOSE: {
// End the processing of the current request
try {
getOutputBuffer().endRequest();
} catch (IOException e) {
setErrorState(ErrorState.CLOSE_NOW, e);
}
break;
}
case COMMIT: {
// Commit current response
if (response.isCommitted()) {
return;
}
// Validate and write response headers
try {
prepareResponse(); // 把响应头的数据写入到InternalOutputBuffer中
getOutputBuffer().commit(); // 将InternalOutputBuffer中的数据发送给socket
} catch (IOException e) {
setErrorState(ErrorState.CLOSE_NOW, e);
}
break;
}
case CLIENT_FLUSH: {
try {
// 将InternalOutputBuffer中的数据发送给socket
getOutputBuffer().flush();
} catch (IOException e) {
setErrorState(ErrorState.CLOSE_NOW, e);
response.setErrorException(e);
}
break;
}
}
}
public class InternalOutputBuffer extends AbstractOutputBuffer<Socket> implements ByteChunk.ByteOutputChannel {
protected void commit() throws IOException {
committed = true;
response.setCommitted(true);
if (pos > 0) {
// Sending the response header buffer,如果用了socketbuffer则写写到socketbuffer中,如果没有则直接通过socketoutputstream返回
if (useSocketBuffer) {
socketBuffer.append(buf, 0, pos);
} else {
outputStream.write(buf, 0, pos);
}
}
}

public void flush() throws IOException {
super.flush();
// Flush the current buffer
// 上面的流程目的是把数据发送给socket,但是如果使用了socketBuffer,那么就只会把数据发送给socketbuffer,所以这里要调用socketbuffer最终发送数据
if (useSocketBuffer) {
socketBuffer.flushBuffer();
}
}
}