参考
理解请求信息
请求信息包括以下三条
-
请求行(request line)
例如GET /images/logo.gif HTTP/1.1,表示从/images目录下请求logo.gif这个文件。 -
请求头(request header),空行
例如Accept-Language: en -
其他消息体
这里以请求行数据的解析为例,在 Http 协议中该行内容格式为:
Request-Line = Method SP Request-URI SP HTTP-Version CRLF
步骤总结
-
Accepter
接收新的socket连接后,创建SocketProcessor
交给线程池运行。 - 后者在
run
内调用AbstractProtocol.AbstractConnectionHandler.process
。 - 然后进一步调用
AbstractHttp11Processor.process
,在此调用getInputBuffer().parseRequestLine(keptAlive)
、getInputBuffer().parseHeaders())
来解析请求行和请求头部。 -
InternalInputBuffer.parseRequestLine
用fill
填充缓冲区,然后读取缓冲区来解析请求行。如果当前缓冲读完了,还不够解析,则继续调用fill
读取,如下:do { // 缓冲区读完了还不够这部分解析的,需要继续读取。 if (pos >= lastValid) { if (!fill()) throw new EOFException(sm.getString("iib.eof.error")); // 没解析完读取流就见底了,报错 } ... chr = buf[pos++]; } while ((chr == Constants.CR) || (chr == Constants.LF));
- 请求头的最大长度是8192,在
Http11Processor
的构造函数中inputBuffer = new InternalInputBuffer(request, headerBufferSize);
,提供的headerBufferSize
就是最大长度。自行跟踪代码(全文搜索"ctrl+shift+f")可知道是8192) - 请求内容的具体处理在
adapter.service(request, response);
,下篇文章讲解。
总结
BIO中,Accepter在接收新的socket连接后,创建新的线程,加入线程池来执行。每个连接(socket)对应一个线程,想必效率有限,也不适合高并发。
最大支持连接数
跟踪代码可知,AbstractEndpoint.createExecutor
中:
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
其中getMaxThreads()
最终调用:
protected int getMaxThreadsExecutor(boolean useExecutor) {
if (useExecutor && executor != null) {
if (executor instanceof java.util.concurrent.ThreadPoolExecutor) {
return ((java.util.concurrent.ThreadPoolExecutor)executor).getMaximumPoolSize();
} else if (executor instanceof ResizableExecutor) {
return ((ResizableExecutor)executor).getMaxThreads();
} else {
return -1;
}
} else {
return maxThreads;
}
}
在executor没创建时,调用return maxThreads;
。其中maxThreads
在初始化时为200,因此线程池最大只能保持200个线程,支持的连接数应当也小于这个。在executor创建后,调用的是return ((java.util.concurrent.ThreadPoolExecutor)executor).getMaximumPoolSize();
,如果没变化(暂时没验证何时会使该值变化)则依旧是200。
网友评论