博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
tomcat源码分析(三)一次http请求的旅行-从Socket说起
阅读量:4688 次
发布时间:2019-06-09

本文共 6495 字,大约阅读时间需要 21 分钟。

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对象(AbstractEndPointcreateExecutor()建立了线程池),由线程池中的线程来处理该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具体处理过程。

转载于:https://www.cnblogs.com/yanfengfree/p/6128737.html

你可能感兴趣的文章
maven3在eclipse3.4.2中创建java web项目
查看>>
发布时间 sql语句
查看>>
黑马程序员 ExecuteReader执行查询
查看>>
记一些从数学和程序设计中体会到的思想
查看>>
题目1462:两船载物问题
查看>>
POJ 2378 Tree Cutting(树形DP,水)
查看>>
第二冲刺阶段个人博客5
查看>>
UVA 116 Unidirectional TSP (白书dp)
查看>>
第三方测速工具
查看>>
MySQL 网络访问连接
查看>>
在aws ec2上使用root用户登录
查看>>
数据访问 投票习题
查看>>
CIO知识储备
查看>>
cnblog!i'm coming!
查看>>
使用点符号代替溢出的文本
查看>>
Axios 中文说明
查看>>
fatal: remote origin already exists.
查看>>
gridview 自定义value值
查看>>
2018二月实现计划成果及其三月规划
查看>>
封装springmvc处理ajax请求结果
查看>>