上一节我们分析了容器的注册,现在我们继续分析tomcat的通信,如果没有通信,那么注册的容器就毫无用武之地,从StandardService的connector的启动开始
org.apache.catalina.core.StandardService.startInternal()
for (Connector connector: connectors) {
try {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
}
Connector的基类的start不再赘述,和其他的组件的基类是一样的,我们直接看到它实现的startInternal方法
protected void org.apache.catalina.connector.Connector.startInternal() throws LifecycleException {
// Validate settings before starting
if (getPort() < 0) {
throw new LifecycleException(sm.getString(
"coyoteConnector.invalidPort", Integer.valueOf(getPort())));
}
//设置状态并且触发starting事件
setState(LifecycleState.STARTING);
try {
//启动与当前连接器相关的协议处理程序
protocolHandler.start();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
}
}
协议处理器的启动,在分析之前,我们先看下协议处理器的类图,这个类图我们在前面某些章节中就已经见过,这里再次看下
从类图中可以看到tomcat使用了桥接模式针对不同的协议实现了对应的协议处理器与(如果我们有需求的话,我们也可以针对自己设计的协议实现对应的处理器)不同类型的socket端点实现进行桥接,避免类膨胀。
public void org.apache.coyote.AbstractProtocol.start() throws Exception {
if (getLog().isInfoEnabled()) {
getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
}
//启动端点
//(*1*)
endpoint.start();
// Start async timeout thread
//启动异步超时线程
asyncTimeout = new AsyncTimeout();
Thread timeoutThread = new Thread(asyncTimeout, getNameInternal() + "-AsyncTimeout");
int priority = endpoint.getThreadPriority();
if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
priority = Thread.NORM_PRIORITY;
}
timeoutThread.setPriority(priority);
timeoutThread.setDaemon(true);
timeoutThread.start();
}
//(*1*)
public final void org.apache.tomcat.util.net.AbstractEndpoint.start() throws Exception {
if (bindState == BindState.UNBOUND) {
//绑定ip地址,端口
bind();
bindState = BindState.BOUND_ON_START;
}
//具体的启动逻辑
startInternal();
}
我们首先来看看tomcat是怎么绑定端口的,tomcat有NIO,NIO2,ARP(以前还有BIO)这几种端点实现,我们就挑选NIO进行分析吧
public void org.apache.tomcat.util.net.NioEndpoint.bind() throws Exception {
//是否从stdin和stdout中获取用户指定的通道,默认false
if (!getUseInheritedChannel()) {
//打开通道
serverSock = ServerSocketChannel.open();
//给socket设置属性,比如backLog等
socketProperties.setProperties(serverSock.socket());
//绑定地址和设置最大可接受请求数
InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
serverSock.socket().bind(addr,getAcceptCount());
} else {
// Retrieve the channel provided by the OS
Channel ic = System.inheritedChannel();
if (ic instanceof ServerSocketChannel) {
serverSock = (ServerSocketChannel) ic;
}
if (serverSock == null) {
throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
}
}
//设置为阻塞
serverSock.configureBlocking(true); //mimic APR behavior
// Initialize thread count defaults for acceptor, poller
if (acceptorThreadCount == 0) {
// FIXME: Doesn't seem to work that well with multiple accept threads
//默认接收线程为1个,1个接收reactor
acceptorThreadCount = 1;
}
if (pollerThreadCount <= 0) {
//minimum one poller thread
//轮询线程数,默认是电脑cpu个数,如果实在没有就设置为1个
pollerThreadCount = 1;
}
//设置计数器
setStopLatch(new CountDownLatch(pollerThreadCount));
// Initialize SSL if needed
//初始化ssl安全连接,我们使用的https就是这种
initialiseSsl();
//打开selector池,主要是为了创建共享Selector对象
selectorPool.open();
}
绑定好ip地址和端口之后,那么就应该启动接收线程了
public void org.apache.tomcat.util.net.NioEndpoint.startInternal() throws Exception {
//如果还没有启动,那么进行启动
if (!running) {
//标记状态
running = true;
paused = false;
//构建同步栈,第一个参数为默认大小,第二参数为配置的最大size限制
//处理请求的处理器个数
processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getProcessorCache());
//事件个数
eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getEventCache());
//nio通道个数
nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getBufferPool());
// Create worker collection
//获取线程池(可从Service中设置的线程池选择),如果为空,那么自己创建一个
if ( getExecutor() == null ) {
createExecutor();
}
//设置连接限制同步计数器(LimitLatch),默认个数10000
initializeConnectionLatch();
// Start poller threads
//启动轮询器数组
pollers = new Poller[getPollerThreadCount()];
for (int i=0; i<pollers.length; i++) {
pollers[i] = new Poller();
Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
}
//启动接收线程
//(*1*)
startAcceptorThreads();
}
}
//(*1*)
protected final void org.apache.tomcat.util.net.AbstractEndpoint.startAcceptorThreads() {
//获取接收线程个数
int count = getAcceptorThreadCount();
acceptors = new Acceptor[count];
for (int i = 0; i < count; i++) {
acceptors[i] = createAcceptor();
String threadName = getName() + "-Acceptor-" + i;
acceptors[i].setThreadName(threadName);
Thread t = new Thread(acceptors[i], threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}
}
从上面的代码中我们可以看到tomcat启动两类关于socket接收的线程组,Acceptor线程组和Poller线程组
我们先来看下Acceptor的实现
protected class org.apache.tomcat.util.net.NioEndpoint.Acceptor extends AbstractEndpoint.Acceptor {
@Override
public void run() {
int errorDelay = 0;
// Loop until we receive a shutdown command
while (running) {
// Loop if endpoint is paused
while (paused && running) {
state = AcceptorState.PAUSED;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// Ignore
}
}
if (!running) {
break;
}
state = AcceptorState.RUNNING;
try {
//if we have reached max connections, wait
//LimitLatch,如果接收的请求已经达到最大,那么会进行await
countUpOrAwaitConnection();
SocketChannel socket = null;
try {
// Accept the next incoming connection from the server
// socket
//获取socket
socket = serverSock.accept();
} catch (IOException ioe) {
// We didn't get a socket
//如果失败,那么将计数减一
countDownConnection();
if (running) {
// Introduce delay if necessary
errorDelay = handleExceptionWithDelay(errorDelay);
// re-throw
throw ioe;
} else {
break;
}
}
// Successful accept, reset the error delay
errorDelay = 0;
// Configure the socket
//配置socket,给socket设置属性,注册到轮询器中
if (running && !paused) {
// setSocketOptions() will hand the socket off to
// an appropriate processor if successful
//如果设置属性和注册失败,关闭这个socket
if (!setSocketOptions(socket)) {
closeSocket(socket);
}
} else {
//关闭socket
closeSocket(socket);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("endpoint.accept.fail"), t);
}
}
//设置状态表示结束
state = AcceptorState.ENDED;
}
private void closeSocket(SocketChannel socket) {
//计数减一
countDownConnection();
try {
//关闭socket通道
socket.socket().close();
} catch (IOException ioe) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("endpoint.err.close"), ioe);
}
}
try {
//关闭socket对象
socket.close();
} catch (IOException ioe) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("endpoint.err.close"), ioe);
}
}
}
}
当接收的Acceptor接收到请求后,获取到对应的socket,获取socket之后tomcat需要对它配置一些属性,并且注册到轮询器中
protected boolean org.apache.tomcat.util.net.NioEndpoint.setSocketOptions(SocketChannel socket) {
// Process the connection
try {
//disable blocking, APR style, we are gonna be polling it
//配置为阻塞
socket.configureBlocking(false);
//获取对应的socket
Socket sock = socket.socket();
//配置属性
socketProperties.setProperties(sock);
//从nioChannels同步栈中弹出一个NioChannel(这里主要是为了避免每次创建NioChannel对象,从而节省性能,属于设计模式中的享元模式)
//NioChannel是tomcat的包装类,用于包装JDK的socketChannel
NioChannel channel = nioChannels.pop();
//如果没有可复用的,创建新的
if (channel == null) {
//设置socket缓存处理器,读取缓冲,写缓冲,是否使用直接内存缓冲
SocketBufferHandler bufhandler = new SocketBufferHandler(
socketProperties.getAppReadBufSize(),
socketProperties.getAppWriteBufSize(),
socketProperties.getDirectBuffer());
if (isSSLEnabled()) {
channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
} else {
channel = new NioChannel(socket, bufhandler);
}
} else {
//复用已经存在的NioChannel,直接设置其需要关联的socket
//并且进行缓冲复位
channel.setIOChannel(socket);
channel.reset();
}
//tomcat采用轮询的策略获取轮询器,并将当前接收的通道注册到对应的轮询器的Selector中
//(*1*)
getPoller0().register(channel);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
try {
log.error("",t);
} catch (Throwable tt) {
ExceptionUtils.handleThrowable(tt);
}
// Tell to close the socket
return false;
}
return true;
}
//(*1*)
public void org.apache.tomcat.util.net.NioEndpoint.Poller.register(final NioChannel socket) {
//NioChannel引用是谁在处理它的请求
socket.setPoller(this);
//NioSocketWrapper 一个包装NioChannel与NioEndpoint端点对象的包装器
NioSocketWrapper ka = new NioSocketWrapper(socket, NioEndpoint.this);
//引用是谁包装了自己
socket.setSocketWrapper(ka);
//告诉包装器是谁在处理自己引用的通道
ka.setPoller(this);
//设置读写超时
ka.setReadTimeout(getSocketProperties().getSoTimeout());
ka.setWriteTimeout(getSocketProperties().getSoTimeout());
ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
ka.setSecure(isSSLEnabled());
ka.setReadTimeout(getConnectionTimeout());
ka.setWriteTimeout(getConnectionTimeout());
PollerEvent r = eventCache.pop();
//设置感兴趣的事件为读
ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
//构建当前将要发生的轮询事件,很明显当前的事件就是注册事件
if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
else r.reset(socket,ka,OP_REGISTER);//如果已经存在,那么重置为注册事件,这也是一种享元模式的使用
//给轮询器添加事件,如果有必要的话会唤醒当前轮询器关联的selector,这样就可以从事件队列中获取事件进行处理
//这里发生的是注册事件,唤醒之后就是注册通道
addEvent(r);
}
我们继续来看下轮询器的实现
public void org.apache.tomcat.util.net.NioEndpoint.Poller.run() {
// Loop until destroy() is called
while (true) {
//是否存在事件的标记
boolean hasEvents = false;
try {
//轮询器是否已经关闭
if (!close) {
//判断是否存在需要处理的事件,并处理事件
//(*1*)
hasEvents = events();
//如果唤醒计数原先的数字大于零,那么可能已经需要处理的socket事件
if (wakeupCounter.getAndSet(-1) > 0) {
//if we are here, means we have other stuff to do
//do a non blocking select
//立即获取需要处理的事件个数,并不会阻塞
keyCount = selector.selectNow();
} else {
//其他的情况,阻塞selectorTimeout毫秒
keyCount = selector.select(selectorTimeout);
}
//设置为零,表示无需立即处理socket事件
wakeupCounter.set(0);
}
if (close) {
//如果轮询器被关闭,那么处理剩下的轮询器事件
events();
//超时处理,主要是关闭socket,注销selectionKey
timeout(0, false);
try {
//关闭selector
selector.close();
} catch (IOException ioe) {
log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
}
break;
}
} catch (Throwable x) {
ExceptionUtils.handleThrowable(x);
log.error("",x);
continue;
}
//either we timed out or we woke up, process events first
//如果是在selector阻塞时被唤醒,那么优先处理事件
if ( keyCount == 0 ) hasEvents = (hasEvents | events());
//如果有感兴趣的事件发生,那么获取SelectionKey列表
Iterator<SelectionKey> iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// any active event.
//循环处理感兴趣的事件
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
//获取NioSocketWrapper
NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
//从感兴趣集合重移除,以免被重复处理
if (attachment == null) {
iterator.remove();
} else {
iterator.remove();
//处理事件
processKey(sk, attachment);
}
}//while
//process timeouts
//超时处理
timeout(keyCount,hasEvents);
}//while
//处理完请求后计数器减一
getStopLatch().countDown();
}
//(*1*)
public boolean events() {
boolean result = false;
PollerEvent pe = null;
//循环事件列表
for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
result = true;
try {
//调用事件的run方法,我们来看下我们在上面接口线程中接收的注册事件是怎么执行的。
//(*2*)
pe.run();
//复位,重置事件对象,用于复用
pe.reset();
if (running && !paused) {
//复用
eventCache.push(pe);
}
} catch ( Throwable x ) {
log.error("",x);
}
}
return result;
}
//(*2*)
public void org.apache.tomcat.util.net.NioEndpoint.PollerEvent.run() {
//如果是注册事件,那么进行给对应poller的selector注册读事件,设置附件为socketWrapper(socket包装器,持有NioChannel对象,缓冲处理器)
if (interestOps == OP_REGISTER) {
try {
socket.getIOChannel().register(
socket.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper);
} catch (Exception x) {
log.error(sm.getString("endpoint.nio.registerFail"), x);
}
} else {
//其他的事件,获取对应注册的SelectionKey对象
final SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
try {
if (key == null) {
// The key was cancelled (e.g. due to socket closure)
// and removed from the selector while it was being
// processed. Count down the connections at this point
// since it won't have been counted down when the socket
// closed.
//如果为空,那么表示已经被注销,那么计数器减一
socket.socketWrapper.getEndpoint().countDownConnection();
((NioSocketWrapper) socket.socketWrapper).closed = true;
} else {
final NioSocketWrapper socketWrapper = (NioSocketWrapper) key.attachment();
//如果已经存在了,那么合并感兴趣的事件
if (socketWrapper != null) {
//we are registering the key to start with, reset the fairness counter.
int ops = key.interestOps() | interestOps;
socketWrapper.interestOps(ops);
key.interestOps(ops);
} else {
//其他的情况,注销这个key(key.cancel,当然还有其他的一些操作,比如socket的close等)
socket.getPoller().cancelledKey(key);
}
}
} catch (CancelledKeyException ckx) {
try {
socket.getPoller().cancelledKey(key);
} catch (Exception ignore) {}
}
}
}
当tomcat接收到请求时,tomcat是怎么处理他们的呢?
protected void org.apache.tomcat.util.net.NioEndpoint.Poller.processKey(SelectionKey sk, NioSocketWrapper attachment) {
try {
//如果已经关闭,那么注销当前key
if ( close ) {
cancelledKey(sk);
//这个key是否有效,并且其附件不能为空,tomcat设置的附件是NioSocketWrapper对象
} else if ( sk.isValid() && attachment != null ) {
//处理读与写事件
if (sk.isReadable() || sk.isWritable() ) {
//如果存在待发生的文件数据,那么处理发生文件
if ( attachment.getSendfileData() != null ) {
processSendfile(sk,attachment, false);
} else {
//大部分请求都是属于这个分支
//取消当前感兴趣的事件,这里就是sk.readyOps()读事件,对于http请求,它是没有状态的,
//处理一次就可以了
unreg(sk, attachment, sk.readyOps());
boolean closeSocket = false;
// Read goes before write
//处理读事件
if (sk.isReadable()) {
if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {
//处理不成功,标记关闭socket
closeSocket = true;
}
}
//如果是写事件,那么处理写事件
if (!closeSocket && sk.isWritable()) {
if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {
closeSocket = true;
}
}
//注销这个key
if (closeSocket) {
cancelledKey(sk);
}
}
}
} else {
//invalid key
//注销key
cancelledKey(sk);
}
} catch ( CancelledKeyException ckx ) {
cancelledKey(sk);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("",t);
}
}
处理socket中的数据
//socketWrapper, event:读或写事件,dispatch:使用线程池处理
public boolean org.apache.tomcat.util.net.AbstractEndpoint.processSocket(SocketWrapperBase<S> socketWrapper,
SocketEvent event, boolean dispatch) {
try {
if (socketWrapper == null) {
return false;
}
//享元模式
SocketProcessorBase<S> sc = processorCache.pop();
if (sc == null) {
//如果没有,那么创建一个,用于维护socketWrapper与对应事件的关系
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
//获取线程池
Executor executor = getExecutor();
//如果设置了使用线程池处理,并且存在线程池,那么就通过线程池处理
if (dispatch && executor != null) {
executor.execute(sc);
} else {
sc.run();
}
} catch (RejectedExecutionException ree) {
getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
return false;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This means we got an OOM or similar creating a thread, or that
// the pool and its queue are full
getLog().error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}
既然SocketProcessorBase可以直接提交给线程池,那么这个类要么实现了Runnable,Callable,继承了Thread
public final void run() {
synchronized (socketWrapper) {
if (socketWrapper.isClosed()) {
return;
}
doRun();
}
}
protected abstract void doRun();
SocketProcessorBase是一个抽象模板类,在不同的端点中它有不同的实现了,我们研究的是NIO,所以我们就分析NIO的实现即可。
protected void org.apache.tomcat.util.net.NioEndpoint.SocketProcessor.doRun() {
//从包装器中获取NioChannel
NioChannel socket = socketWrapper.getSocket();
//获取感兴趣的key
SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
try {
int handshake = -1;
try {
if (key != null) {
//是否已经完成握手
if (socket.isHandshakeComplete()) {
// No TLS handshaking required. Let the handler
// process this socket / event combination.
handshake = 0;
//如果是停止,断连,或者错误,那么表示握手失败
} else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
event == SocketEvent.ERROR) {
// Unable to complete the TLS handshake. Treat it as
// if the handshake failed.
handshake = -1;
} else {
//握手,内部没有做什么逻辑,直接返回零,表示握手成功
handshake = socket.handshake(key.isReadable(), key.isWritable());
//读事件
event = SocketEvent.OPEN_READ;
}
}
} catch (IOException x) {
handshake = -1;
if (log.isDebugEnabled()) log.debug("Error during SSL handshake",x);
} catch (CancelledKeyException ckx) {
handshake = -1;
}
//如果握手成功,处理socket
if (handshake == 0) {
SocketState state = SocketState.OPEN;
// Process the request from this socket
//处理socket
if (event == null) {
state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
} else {
state = getHandler().process(socketWrapper, event);
}
//请求完毕后,如果状态被设置为SocketState.CLOSED,那么
//关闭socket,如果是长连接可能就不会那么早关闭了
if (state == SocketState.CLOSED) {
close(socket, key);
}
//握手失败,关闭socket,注销key
} else if (handshake == -1 ) {
close(socket, key);
//注册读事件
} else if (handshake == SelectionKey.OP_READ){
socketWrapper.registerReadInterest();
//注册写事件
} else if (handshake == SelectionKey.OP_WRITE){
socketWrapper.registerWriteInterest();
}
} catch (CancelledKeyException cx) {
socket.getPoller().cancelledKey(key);
} catch (VirtualMachineError vme) {
ExceptionUtils.handleThrowable(vme);
} catch (Throwable t) {
log.error("", t);
socket.getPoller().cancelledKey(key);
} finally {
socketWrapper = null;
event = null;
//return to cache
if (running && !paused) {
processorCache.push(this);
}
}
}
}
根据socket事件分发处理
protected Processor org.apache.coyote.http11.AbstractHttp11Protocol.createProcessor() {
//创建Http11Processor,这里大量的比如最大http请求头大小,等等都是AbstractHttp11Protocol的默认设置
Http11Processor processor = new Http11Processor(getMaxHttpHeaderSize(),
getAllowHostHeaderMismatch(), getRejectIllegalHeaderName(), getEndpoint(),
getMaxTrailerSize(), allowedTrailerHeaders, getMaxExtensionSize(),
getMaxSwallowSize(), httpUpgradeProtocols, getSendReasonPhrase(),
relaxedPathChars, relaxedQueryChars);
//获取适配器,这里获取的是CoyoteAdapter,这个适配器在Connector.initInternal初始化的时候设置的
processor.setAdapter(getAdapter());
//最大的长连接请求书
processor.setMaxKeepAliveRequests(getMaxKeepAliveRequests());
//上传超时时间
processor.setConnectionUploadTimeout(getConnectionUploadTimeout());
processor.setDisableUploadTimeout(getDisableUploadTimeout());
//压缩最小大小,为了节省带宽,会对一些数据比较的进行压缩处理,但是这会消耗cpu,所以需要
//设置触发压缩的最小大小
processor.setCompressionMinSize(getCompressionMinSize());
processor.setCompression(getCompression());
processor.setNoCompressionUserAgents(getNoCompressionUserAgents());
processor.setCompressibleMimeTypes(getCompressibleMimeTypes());
processor.setRestrictedUserAgents(getRestrictedUserAgents());
processor.setMaxSavePostSize(getMaxSavePostSize());
processor.setServer(getServer());
processor.setServerRemoveAppProvidedValues(getServerRemoveAppProvidedValues());
return processor;
}
public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
throws IOException {
。。。。。。省略代码
} else if (status == SocketEvent.OPEN_READ){
//处理Read事件,我们通常使用的就是这个
state = service(socketWrapper);
}
。。。。。。省略代码
return state;
}
public SocketState org.apache.coyote.http11.Http11Processor.service(SocketWrapperBase<?> socketWrapper)
throws IOException {
//获取请求处理器,一个RequestInfo对象
RequestInfo rp = request.getRequestProcessor();
//设置当前请求处于解析阶段
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
// Setting up the I/O
//设置当前Http11Processor处理的socketWrapper对象
setSocketWrapper(socketWrapper);
//初始化输入缓冲,将配置的缓冲长度初始化ByteBuffer的初始化容量
//这个配置我们在
inputBuffer.init(socketWrapper);
//初始化输出缓冲,将配置的缓冲长度初始化ByteBuffer的初始化容量
outputBuffer.init(socketWrapper);
// Flags
keepAlive = true;
openSocket = false;
readComplete = true;
boolean keptAlive = false;
SendfileState sendfileState = SendfileState.DONE;
while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
sendfileState == SendfileState.DONE && !endpoint.isPaused()) {
// Parsing the request header
try {
//解析http的请求头
if (!inputBuffer.parseRequestLine(keptAlive)) {
if (inputBuffer.getParsingRequestLinePhase() == -1) {
return SocketState.UPGRADING;
} else if (handleIncompleteRequestLineRead()) {
break;
}
}
if (endpoint.isPaused()) {
// 503 - Service unavailable
response.setStatus(503);
setErrorState(ErrorState.CLOSE_CLEAN, null);
} else {
keptAlive = true;
// Set this every time in case limit has been changed via JMX
request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
if (!inputBuffer.parseHeaders()) {
// We've read part of the request, don't recycle it
// instead associate it with the socket
openSocket = true;
readComplete = false;
break;
}
if (!disableUploadTimeout) {
socketWrapper.setReadTimeout(connectionUploadTimeout);
}
}
} catch (IOException e) {
。。。。。。省略部分异常处理
}
。。。。。。省略部分异常处理
// Has an upgrade been requested?
//获取请求头中Connection属性
Enumeration<String> connectionValues = request.getMimeHeaders().values("Connection");
boolean foundUpgrade = false;
while (connectionValues.hasMoreElements() && !foundUpgrade) {
foundUpgrade = connectionValues.nextElement().toLowerCase(
Locale.ENGLISH).contains("upgrade");
}
//如果找到upgrade属性
if (foundUpgrade) {
// Check the protocol
//获取升级协议,比如websocket这种
String requestedProtocol = request.getHeader("Upgrade");
UpgradeProtocol upgradeProtocol = httpUpgradeProtocols.get(requestedProtocol);
if (upgradeProtocol != null) {
if (upgradeProtocol.accept(request)) {
// TODO Figure out how to handle request bodies at this
// point.
response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
//设置升级响应头
response.setHeader("Connection", "Upgrade");
response.setHeader("Upgrade", requestedProtocol);
action(ActionCode.CLOSE, null);
getAdapter().log(request, response, 0);
InternalHttpUpgradeHandler upgradeHandler =
upgradeProtocol.getInternalUpgradeHandler(
getAdapter(), cloneRequest(request));
UpgradeToken upgradeToken = new UpgradeToken(upgradeHandler, null, null);
//响应客户端,发送协议升级http响应头
action(ActionCode.UPGRADE, upgradeToken);
return SocketState.UPGRADING;
}
}
}
//如果没有发生错误,那么
if (!getErrorState().isError()) {
// Setting up filters, and parse some request headers
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
try {
prepareRequest();
} catch (Throwable t) {
。。。。。。处理错误
}
}
if (maxKeepAliveRequests == 1) {
keepAlive = false;
} else if (maxKeepAliveRequests > 0 &&
socketWrapper.decrementKeepAlive() <= 0) {
keepAlive = false;
}
// Process the request in the adapter
if (!getErrorState().isError()) {
try {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
//处理请求
getAdapter().service(request, response);
。。。。。。省略
}
inputBuffer.parseRequestLine方法是具体读取socket中流信息的方法,根据http协议头进行解析,由于我们使用的NIO,所以不能一次性读取成功,tomcat会进行多次的读取,知道读取到完整的信息后停止读取,再进行下面的操作,具体的读取方式这里不做分析,首先说明一下几个类
- MessageBytes 消息字节类,这个类一般用于描述所持有的消息类型,比如字节,字符,字符串等,内部还会关联数据块
- AbstractChunk tomcat8.5中有两个实现ByteChunk和CharChunk,他们只维护对应数据的偏移位置,比如一个很长的字节数组,他们只做偏移指定某一个小块
public void org.apache.catalina.connector.CoyoteAdapter.service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception {
//获取Request对象,这个request对象就是实现了HttpServletRequest的对象
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
//如果为空,那么直接创建一个空的Request
if (request == null) {
// Create objects
request = connector.createRequest();
//将coyote的Request设置进去。这是一种适配器模式
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
//互相关联
request.setResponse(response);
response.setRequest(request);
// Set as notes
//设置notes,下次就可以直接从notes中获取
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
// Set query string encoding
//设置查询字符串编码
req.getParameters().setQueryStringCharset(connector.getURICharset());
}
if (connector.getXpoweredBy()) {
response.addHeader("X-Powered-By", POWERED_BY);
}
boolean async = false;
boolean postParseSuccess = false;
req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
try {
// Parse and set Catalina and configuration specific
// request parameters
//内部做了大量操作,比如从connector获取一些设置到coyoteRequest中,身份认证
//解析session,是否能够找到context等等操作
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
//check valves if we support async
request.setAsyncSupported(
connector.getService().getContainer().getPipeline().isAsyncSupported());
// Calling the container
//调用Engine的排管,开始容器的调用
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
}
。。。。。。省略request的异步操作
} catch (IOException e) {
// Ignore
} finally {
。。。。。。省略资源的释放,比如清除缓冲中数据等等
}
}
上面的connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);这段代码开始就是从engine-》host-》context-》wrapper,这部分内容我们留到下一节分析。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?