- 浏览: 19998 次
- 性别:
- 来自: 杭州
文章分类
最新评论
先上个图先,一个只有我自己能看懂的url时序图.
这个基本上是connentor初始化的时候,初始化了Http11Protocol,接着初始化JIoEndpoint,初始化介绍后,connentor调用start()方法开始工作鸟,接着调用Http11Protocol,JIoEndpoint的start()方法,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(); } } }
start()方法中如果没有外部的executor的话,会使用的自己内部的简单的工作线程池WorkerStack,这个线程池放着处理请求的线程集合,当请求的数量超过定义的最大支持线程数,那么后面的请求一直阻塞等待只到有可以使用的线程为止。
看代码
for (int i = 0; i < acceptorThreadCount; i++) { Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i); acceptorThread.setPriority(threadPriority); acceptorThread.setDaemon(daemon); acceptorThread.start(); }
acceptorThreadCount一般的值就是1,这里另外开启一个线程,Acceptor是个内部类, 看代码:
protected class Acceptor implements Runnable { /** * The background thread that listens for incoming TCP/IP connections and * hands them off to an appropriate processor. */ public void run() { // Loop until we receive a shutdown command while (running) { // Loop if endpoint is paused while (paused) { try { Thread.sleep(1000); } catch (InterruptedException e) { // Ignore } } // Accept the next incoming connection from the server //socket try { Socket socket = serverSocketFactory.acceptSocket(serverSocket); serverSocketFactory.initSocket(socket); // Hand this socket off to an appropriate processor if (!processSocket(socket)) { // Close socket right away try { socket.close(); } catch (IOException e) { // Ignore } } }catch ( IOException x ) { if ( running ) log.error(sm.getString("endpoint.accept.fail"), x); } catch (Throwable t) { log.error(sm.getString("endpoint.accept.fail"), t); } // The processor will recycle itself when it finishes } } }
Acceptor实现runnable接口,在run方法里面是一个while循环,当JIoEndpoint的start()的已经执行过的话,那么running为true,while循环就一直运行下去,只到JIoEndpoint执行了resume,stop等方法。while循环里面利用工厂类来产生一个socket,这里只是把socket编程的步骤移动到了工厂里面了,符合类单一责任原则。
最主要的是调用JIoEndpoint的processSocket方法,另外如果方法调用返回false的话,socket会被关闭,因为false一般代表了这次请求处理可能有问题,另外在这个类中,发现如果出现异常的话,基本上是不用throw异常的,一是可能考虑到这只是对一个请求而已,抛出异常没有意义,二是为了性能的考虑,所有一般的方法都是以boolean来判断异常和正常与否。我们看下JIoEndpoint的processSocket方法:
protected boolean processSocket(Socket socket) { try { if (executor == null) { getWorkerThread().assign(socket); } else { executor.execute(new SocketProcessor(socket)); } } catch (Throwable 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; }
有外部executor的话,那么外部的executor来执行这个socket;没有的话,就需要JIoEndpoint的内部线程类Worker来完成了
先看如果有executor的情况,此时调用很简单,一个runnable实现SocketProcessor被调用
public void run() { // 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; }
设置socket的属性,然后调用内部接口Handler的process处理socket的具体内容,Handler的具体实现在Http11Protocol内部类Http11ConnectionHandler中,这个后面再讲。
这样子JIoEndpoint的工作基本完成了。
再来看当executor==null的时候的分支,首先需要获得一个工作线程,但是如果线程池设置了上限的话,可能会阻塞只到有可用的线程或者可以创建新的线程,之后我们来看Worker的assign方法,看这个方法需要和Worker的await方法一起来看:
protected boolean available = false; 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); }
首先调用assign时,是不用wait的,但是await方法就会阻塞在这里,这里这样做的第1个原因出现:
assign()方法设置了socket后才设置available,这里主要是为了await()方法返回的socket是正确的,不为空的,是这个线程的,当assign()最后nofifyAll(),那么await()方法可以自由的执行了
反过来看,当available为true的时候,await()可以执行,而assign()方法阻塞了,这里有第2个原因出现,Worker存活在线程池中,在这两个方法中我们用的同一个属性socket,所以这个每个请求来了之后都会改变这个属性,所以这两个方法必须同步,在await()返回当前的socket之前,是不能设置这个socket的.
我们在来看Worker的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()方法,就是上面所说的获得正确的socket,然后执行和SocketProcessor内部类实现的一样的功能的代码,最后做一些线程回收的工作
这里有一点可能会影响到性能,就是Worker线程池最大时,就需要阻塞等待,知道有可以用的线程,这个可能是考虑到如果线程开太多,内部压力大,线程的切换消耗大,这个可以作为tomcat调优的一个点,另外一个就是可以使用外部的线程池,这个也可以尝试
哇,真长!
发表评论
-
tomcat StandardSession
2011-05-11 16:33 3254Tomcat中Session的实现还是比较清晰的,先看类图 ... -
tomcat session复制(二)
2011-05-07 18:02 1732上文http://nod0620.iteye.c ... -
tomcat session复制(一)
2011-05-05 16:32 2721tomcat的session复制大致分两种:all-to-a ... -
tomcat NioSender
2011-05-02 01:58 1285上次说了NioReceiver,这次看看NioSe ... -
NIO基础
2011-04-29 16:48 5612tomcat集群的时候,在心跳通讯的时候,默认的接 ... -
tomcat的Http11Protocol,Http11Processor
2011-04-11 21:42 2161上文说到在JIoEndpoint类中处理请求最终是调用到内 ...
相关推荐
### Tomcat7源码手撕过程详解 #### Tomcat初始化流程分析 Tomcat是一个流行的Java Servlet容器,用于部署和运行Web应用程序。理解Tomcat的工作原理对于优化应用性能、解决部署问题至关重要。以下是对Tomcat7启动...
chm文件,方便查找,包含tomcat6.0核心类,如Connector,Lifecycle,http11Protocal,JIoEndPoint,javax包等。
- **BIO(JIoEndpoint)**:传统的阻塞式I/O模型。 - **NIO(NioEndpoint)**:非阻塞式I/O模型。 - **APR(Apache Portable Runtime libraries)**:利用本地库提供的更高效的I/O操作。 #### 二、请求处理 请求处理是...
在BIO模式下,Tomcat使用JIoEndpoint组件监听指定端口,每当有新的连接请求到达,它会将请求放入线程池进行处理。线程池中的线程会调用Http11Processor组件解析HTTP协议,并通过适配器(Adapter)将请求映射到相应的...
6. **Endpoint类**: - 通过`endpoint.init()`完成初始化,包括创建`serverSocketFactory`和`serverSocket`实例。 7. **启动过程**: - `StandardServer.start`继续执行启动流程,触发启动事件。 #### 四、总结 ...
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:624) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:445)
- **创建Socket连接**:Tomcat通过调用`JIoEndpoint.java`的`run()`方法创建socket连接。 - **解析HTTP协议**:通过`processor.process(socket)`方法解析HTTP请求并返回响应内容。`processor`是`HttpProcessor`的一...
java.lang.SecurityException: class "org.apache.commons.collections.SequencedHashMap... at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447) at java.lang.Thread.run(Unknown Source)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489) at java.lang.Thread.run(Thread.java:662) 网络解决办法: (虽然该办法可行,但是本人并不提倡。具体原因在之后解释。) 在...