tomcat 源码分析 (三) 一次 http 请求的旅行
在 http 请求旅行之前,我们先来准备下我们所需要的工具。首先要说的就是 Connector,其作为 Service 的子容器,承担着 http 请求的核心功能。那我们先来准备下一啊吧。
我们知道一次网络请求过来之后,从网络的角度来看,是经过物理层→链路层→网络层 -> 传输层 -> 应用层,如下图所示。
我们所熟知的的 Socket 处于 TCP(传输层),操作系统为我们提供来一套 API 来操作 Socket,而 tomcat 其任务就是针对传输层过来的 Socket 进行包装,并实现应用层的协议,最常见的应用层协议应该算是 http 协议了。接下来就来具体看看 tomcat 是如何实现 http 协议 (实际上 tomcat 还实现了 ajp 协议以及处理请求的。
我们这里以最常见的 BIO(阻塞试 IO) 的方式来分析。我们先来看看 tomcat 是怎么处理 TCP 连接的。在 org.apche.tomcat.util.net 包主要是用于处理网络请求的,即对 TCP 的处理。
首先我们来看一下 org.apache.tomcat.util.net.AbstractEndPoint 这个类。在 Tomcat 的对请求的设计当中,由专门的线程接受 TCP 连接,并直接将 TCP 连接转交给工作线程。在 AbstrctEndPoint 中有一个抽象的静态内部类我们来一起看一下。
- public abstract static class Acceptor implements Runnable {
- public enum AcceptorState {
- NEW,
- RUNNING,
- PAUSED,
- ENDED
- }
- protected volatile AcceptorState state = AcceptorState.NEW;
- public final AcceptorState getState() {
- return state;
- }
- private String threadName;
- protected final void setThreadName(final String threadName) {
- this.threadName = threadName;
- }
- protected final String getThreadName() {
- return threadName;
- }
- }
可以看出在这个静态内部类中并没有实现 run() 方法,其实现交给子类来实现。在 Tomcat 中实际定义来一个 Acceptor 数组来表示一组接受 TCP 连接的线程。我们在简单看一下其启动这个接受线程的代码实现。
- protected final void 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();
- }
- }
其中 count 这个我们是可以在 server.xml 中去配置的,一般情况下,会配置 1-2。也就是说接受 TCP 连接的线程也只是 1-2 个。
说到这里,我们也应该来说重点来,就是接受线程是如何具体工作的,我们来看 JIOEndPoint, 这个类是 AbstractEndPoint 的子类,也是设计来处理 TCP 连接的,这个类实现了一个简单的服务器,会有一到 2 个监听线程来监听 Socket,对于每一个 TCP 连接,都会从创建一个工作线程来处理。
刚刚我们说道 AbstractEndPoint 中的抽象静态内部类 Acceptor,在其子类 JIOEndPoint 中也存在一个内部类,继承自 Acceptor,并实现来 run(); 方法。我们来看一下。
- protected class 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
- countUpOrAwaitConnection();
- Socket socket = null;
- try {
- // Accept the next incoming connection from the server
- // socket
- socket = serverSocketFactory.acceptSocket(serverSocket);
- } catch(IOException ioe) {
- countDownConnection();
- // Introduce delay if necessary
- errorDelay = handleExceptionWithDelay(errorDelay);
- // re-throw
- throw ioe;
- }
- // Successful accept, reset the error delay
- errorDelay = 0;
- // Configure the socket
- if (running && !paused && setSocketOptions(socket)) {
- // Hand this socket off to an appropriate processor
- if (!processSocket(socket)) {
- countDownConnection();
- // Close socket right away
- closeSocket(socket);
- }
- } else {
- countDownConnection();
- // Close socket right away
- closeSocket(socket);
- }
- } catch(IOException x) {
- if (running) {
- log.error(sm.getString("endpoint.accept.fail"), x);
- }
- } catch(NullPointerException npe) {
- if (running) {
- log.error(sm.getString("endpoint.accept.fail"), npe);
- }
- } catch(Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("endpoint.accept.fail"), t);
- }
- }
- state = AcceptorState.ENDED;
- }
- }
其核心在接收到 TCP 连接之后,即在接收到 Socket,会调用 processSocket(Socket socket); 这个方法。我们再来关注一下这个方法。
- protected boolean processSocket(Socket socket) {
- // Process the request from this socket
- try {
- SocketWrapper wrapper = new SocketWrapper(socket);
- wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
- wrapper.setSecure(isSSLEnabled());
- // During shutdown, executor may be null - avoid NPE
- if (!running) {
- return false;
- }
- getExecutor().execute(new SocketProcessor(wrapper));
- } catch(RejectedExecutionException x) {
- log.warn("Socket processing request was rejected for:" + socket, x);
- 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
- log.error(sm.getString("endpoint.process.fail"), t);
- return false;
- }
- return true;
- }
其核心代码在于 getExecutor().execute(new SocketProcessor(wrapper));getExecutor()会返回 Executor 对象 (在 AbstractEndPoint 中 createExecutor() 建立了线程池), 由线程池中的线程来处理该 Socket。我们再来看一下 SocketProccessor 这个在 JIOEndPoint 中的内部类,这个类 (注意此时已经在工作线程之中) 中核心代码
- if ((state != SocketState.CLOSED)) {
- if (status == null) {
- state = handler.process(socket, SocketStatus.OPEN_READ);
- } else {
- state = handler.process(socket, status);
- }
- }
从中我们可以看到实际处理又交给来 Handler 来处理,那么 Handler 怎么处理的,我们会在下一节当中具体阐述。这一节就先讲述到这里,下一节会讲述 handler 具体处理过程。
来源: http://www.cnblogs.com/yanfengfree/p/6128737.html