Tomcat源码阅读之Connector启动

来源:互联网 发布:下载游戏的软件 编辑:程序博客网 时间:2024/06/07 00:58

Tomcat的主要功能简单概括起来就是接收请求,处理请求,返回结果,而接收请求和返回结果的过程都需要Connector组件参与。那么我们以Http/1.1对应的连接器为例,看下Connector组件启动过程的源码。Connector组件启动的主要逻辑在JIoEndpoint类的start方法中。

public void start()        throws Exception {        // Initialize socket if not done before        if (!initialized) {            init();        }        if (!running) {            running = true;            paused = false;            // Create worker collection            if (executor == null) {                workers = new WorkerStack(maxThreads);            }            // Start acceptor threads            for (int i = 0; i < acceptorThreadCount; i++) {                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);                acceptorThread.setPriority(threadPriority);                acceptorThread.setDaemon(daemon);                acceptorThread.start();            }        }    }

方法中首先实例化一个WorkerStack对象,该对象负责管理work线程,类中有个work数组负责存放work线程,在实例化WorkerStack对象的时候会将这个数组的大小初始化为200,WorkerStack对象利用这个数组实现了一个栈的数据结构。work线程的作用是负责处理请求,因此我们可以得出这样一个结论:Tomcat中处理请求的线程数量默认的最大是200。work线程中除了run方法外还有两个比较重要的方法,分别是assign和await。

synchronized void assign(Socket socket) {            // Wait for the Processor to get the previous Socket            while (available) {                try {                    wait();                } catch (InterruptedException e) {                }            }            // Store the newly available Socket and notify our thread            this.socket = socket;            available = true;            notifyAll();        }
private synchronized Socket await() {            // Wait for the Connector to provide a new Socket            while (!available) {                try {                    wait();                } catch (InterruptedException e) {                }            }            // Notify the Connector that we have received this Socket            Socket socket = this.socket;            available = false;            notifyAll();            return (socket);

关于这两个方法的作用我们待会儿再讲,接着看start方法。方法中通过一个for循环启动Acceptor线程,顾名思义Acceptor线程的作用就是接收请求。Tomcat启动了多少个线程来接收请求呢?答案是一个,前面我们有说到,在执行JIoEndpoint类的init方法的时候将acceptorThreadCount的值设置为1,表示启动接收线程的数量。那么问题来了,Tomcat只启动了一个接收线程性能会不会有问题,能快速的响应多个请求吗?其实不然,接收线程设置成一个是没有问题的,因为它只负责接收,收到请求后就扔给work线程去处理,然后Acceptor继续接收请求。相比于接收请求,处理过程将更会耗时,这就是为什么work线程有多个,而Acceptor线程只有一个的原因了。
Acceptor线程接收请求并将请求交给work线程处理的逻辑都在run方法中。首先通过Socket socket = serverSocketFactory.acceptSocket(serverSocket)
这样一句代码使socket处于监听状态,监听端口默认是8080,此时程序处于阻塞状态,直到有请求到来。当接收到请求后调用processSocket方法先创建并启动work线程,然后调用work线程的assign方法。`work线程启动过程中,会执行run方法里的代码:

public void run() {            // Process requests until we receive a shutdown signal            while (running) {                // Wait for the next socket to be assigned                Socket socket = await();                if (socket == null)                    continue;                // Process the request from this socket                if (!setSocketOptions(socket) || !handler.process(socket)) {                    // Close socket                    try {                        socket.close();                    } catch (IOException e) {                    }                }                // Finish up this request                socket = null;                recycleWorkerThread(this);            }        }

方法中首先执行await方法,由于此时available的值为false,因此work线程会处于等待状态。此时会调用work线程的assign方法,因为available的值为false,因此assign方法不会处于进入wait方法,并且将带有请求信息的socket对象传递给this.socket,设置available的值,最重要的一点是唤醒所有等待的线程。因此assign和await方法的执行顺序是先执行await阻塞程序等待socket的到来,然后执行assign方法传递socket对象,唤醒work线程。assign执行完之后,因为await方法而处于等待状态的work线程被唤醒了,并且获取到了从Acceptor线程那边传递过来的socket对象,而socket对象中又有请求信息,也就是说Acceptor线程将处理请求的任务给了work线程,而Acceptor线程也可以接受新的请求了,就这样把接受请求和处理请求的线程分开了。

0 0
原创粉丝点击