博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Tomcat学习 HttpConnector和HttpProcessor启动流程和线程交互
阅读量:6352 次
发布时间:2019-06-22

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

一、tomat启动流程

1、启动HttpConnector

  connector等待连接请求,只负责接受socket请求,具体处理过程交给HttpProcessor处理。

  tomcat用户只能访问到connector,能设置接受的数据的buffer大小,而不能看见HttpProcessor的处理过程。

2、创建HttpProcessor对象池

  创建对象后马上调用start()方法启动processor的线程:

private HttpProcessor newProcessor() {        HttpProcessor processor = new HttpProcessor(this, curProcessors++);        if (processor instanceof Lifecycle) {            try {                ((Lifecycle) processor).start();            } catch (LifecycleException e) {                log("newProcessor", e);                return (null);            }        }        created.addElement(processor);        return (processor);    }

 

  创建对象池:

private HttpProcessor createProcessor() {        synchronized (processors) {            if (processors.size() > 0) {
return ((HttpProcessor) processors.pop()); } if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {
return (newProcessor()); } else { if (maxProcessors < 0) {
return (newProcessor()); } else {
return (null); } } }

 

3、等待客户端请求,对每个请求启动一个线程,并取出一个processor来处理这个socket

public void run() {        // Loop until we receive a shutdown command        while (!stopped) {            // Accept the next incoming connection from the server socket            Socket socket = null;            try {
socket = serverSocket.accept(); if (connectionTimeout > 0) socket.setSoTimeout(connectionTimeout); socket.setTcpNoDelay(tcpNoDelay); } catch (AccessControlException ace) { log("socket accept security exception", ace); continue; } catch (IOException e) {
try {
synchronized (threadSync) { if (started && !stopped) log("accept error: ", e); if (!stopped) {
serverSocket.close(); serverSocket = open(); } } } catch ...continue; } // Hand this socket off to an appropriate processor HttpProcessor processor = createProcessor(); if (processor == null) { try { log(sm.getString("httpConnector.noProcessor")); socket.close(); } catch (IOException e) { ; } continue; } processor.assign(socket); } synchronized (threadSync) { threadSync.notifyAll(); } }

 

  在connector的run()里面创建了processor对象池,创建processor的时候就启动了processor线程。但这时候所有的processor一直在阻塞着,因为没有等到要处理的socket对象。

  一旦有了一个新的socket请求,就把这个socket交给一个processor来处理,这里调用了processor.assign(socket)方法,用于processor里面的同步处理。

4、具体处理socket请求的http的url、head、cookie等

  这些处理过程在processor的process()方法里面处理,并把这些信息封装到request对象和response对象。

5、 交给特定的servlet处理业务逻辑

  process()方法里面有一行代码:

if (ok) {                    connector.getContainer().invoke(request, response);                }

 

  如果都处理正常,就交给对应的容器container去处理。

  invoke()方法里面,实例化一个ClassLoader对象,去加载指定的servlet的class文件,然后调用这个servlet的service方法等。

 

二、HttpConnector和HttpProcessor的同步配合

  HttpConnector初始化的的时候,建立了一系列的HttpProcessor,放在属性变量栈中。

  当获取到一个客户端的连接socket的时候,就会去获取一个processor,并且调用processor的assign方法,将该socket传递给这个processor。
  这个是在connector的线程里做的。

  先看下processor的assign方法:

/**     * Process an incoming TCP/IP connection on the specified socket.  Any     * exception that occurs during processing must be logged and swallowed.     * NOTE:  This method is called from our Connector's thread.  We     * must assign it to our own thread so that multiple simultaneous     * requests can be handled.     *     * @param socket TCP socket to process     */    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();    }

 

  这个方法是同步方法。一开始的时候,这个available是false的,因此,connector线程调用这个assign方法,不会陷入等待状态。而是顺序执行:

  this.socket = socket;        available = true;        notifyAll();

  这个notifyAll()起到什么作用呢?这个要先看Processor的run方法:

/**     * The background thread that listens for incoming TCP/IP connections and     * hands them off to an appropriate processor.     */    public void run() {        // Process requests until we receive a shutdown signal        while (!stopped) {            // Wait for the next socket to be assigned            Socket socket = await();            if (socket == null)                continue;            // Process the request from this socket            try {                process(socket);            } catch (Throwable t) {                log("process.invoke", t);            }            // Finish up this request            connector.recycle(this);        }        // Tell threadStop() we have shut ourselves down successfully        synchronized (threadSync) {            threadSync.notifyAll();        }    }

  看这一句:

  // Wait for the next socket to be assigned            Socket socket = await();

  大概可以猜到在等待获得一个socket,继续看await方法:

/**     * Await a newly assigned Socket from our Connector, or null     * if we are supposed to shut down.     */    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();        if ((debug >= 1) && (socket != null))            log("  The incoming request has been awaited");        return (socket);    }

 

  这个await在等待一个socekt,当没有socket可以获取的时候,即available=false,也即是初始状态,这个await方法会调用wait(),导致运行该run方法的processor线程陷入等待。

什么时候这个processor线程才被唤醒?就是我们在assign方法里的notifyAll方法了。当这个processor线程被唤醒后,检查while循环的条件:while(!available),显然,此时available是true的,那么就会退出循环(即不进入等待),进而执行以下的代码:

  Socket socket = this.socket;        available = false;        notifyAll();

  即将processor实例的socket对象(在assign方法里得到的),赋给一个局部变量,这样就把processor对象的实例变量解放了,可以用来接收新的socket。接收了这个socket以后,就把available重新设置为false,即connector线程给的socket已经被处理了。

 

  从概念上来理解整个过程:

  一开始,processor在等待资源,connector负责提供资源。当connector得到一个资源的时候,会随机抽到一个processor,然后,将此资源给processor对象,并且通知所有processor对象“有资源啦”,所有processor都为之精神一震,都赶快去检查,结果只有一个确实得到了资源,可以干活了,另外的processor发现资源没有给自己,于是又继续等待。就是这样的过程。

 

三、一些疑问

  1、对于assign方法里面的:

  this.socket = socket;

 

  和awai方法里面的:

Socket socket = this.socket;

  难于理解。

  就是:为什么 await 需要使用一个本地变量(socket)而不是返回实例的 socket 变量呢?

  因为这样一来,在当前 socket 被完全处理之前,实例的 socket 变量可以赋给下一个前来的 socket

 

Reference

1、How Tomcat Works

2、

3、

 

 

 

 

转载地址:http://talla.baihongyu.com/

你可能感兴趣的文章
(轉貼) Ubuntu將在ARM平台netbook上現身 (SOC) (News) (Linux) (Ubuntu)
查看>>
SQL注入测试工具:Pangolin(穿山甲)
查看>>
在html 的img属性里只显示图片的部分区域(矩形,给出开始点和结束点),其他部份不显示,也不要拉伸...
查看>>
程序员第二定律:量化管理在程序员身上永无可能
查看>>
ubuntu一些脚本的执行顺序
查看>>
类继承的结构
查看>>
Intel 被 ARM 逼急了
查看>>
testng + reportng 测试结果邮件发送
查看>>
百度亮相iDASH,推动隐私保护在人类基因组分析领域的应用
查看>>
Python「八宗罪」
查看>>
你的隐私还安全吗?社交网络中浏览历史的去匿名化
查看>>
NeurIPS 2018|如何用循环关系网络解决数独类关系推理任务?
查看>>
Windows 10 份额突破 40%,Windows 7 连跌四月终回升
查看>>
怎么把Maven项目转为动态Web项目?
查看>>
Arm发布Cortex-A76AE自动驾驶芯片架构,宣示车载系统市场主权
查看>>
FreeBSD ports中make可带有的参数(转)
查看>>
Hibernate入门教程
查看>>
Java支付宝扫码支付[新]
查看>>
SpringMVC 拦截器 筛选
查看>>
CronExpression介绍
查看>>