本文是Tomcat源码阅读系列的第四篇文章,本系列前三篇文章如下:
Tomcat源码阅读系列(一)使用IntelliJ IDEA运行Tomcat6源码
Tomcat源码阅读系列(二)Tomcat总体架构
Tomcat源码阅读系列(三)启动和关闭过程
本文主要介绍Tomcat的Connector连接器相关。
Tomcat主要由两大核心组件,一个是Coyote为代表的connector,一个是Catalina为代表的container。connector负责的是底层的网络通信的实现,而container负责的是上层servlet业务的实现。对于tomcat的servlet container这部分代码很少改动,一个应用服务器的性能很大程度上取决于网络通信模块connector的性能,因此connector对于tomcat而言是重中之重。本文首先从应用层次分析Tomcat所有的connector种类及用法,然后介绍Connector的初始化以及使用过程,最后对一些关节步骤进行说明。本文以BIO的Http11Protocol为例进行说明,如有需要可自行阅读Http11AprProtocol、AjpAprProtocol、AjpProtocol、Http11NioProtocol和JkCoyoteHandler等,不同的协议实现对接不同的技术,如NIO、APR和AJP等,具体技术说明可以参考《Tomcat源码阅读系列(二)Tomcat总体架构》。
根据Connector与外界交互协议的不同,可以将Connector分为使用HTTP协议进行交互类型和使用AJP协议交互类型两种,而这两种又可以分别于APR组件机型配合,所以产生的Protocol比较多,功能相对复杂。
BIO HTTP/1.1 Connector配置
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />使用ThreadPoolExecutor的BIO HTTP/1.1 Connector配置
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4"/>
<Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />SSL配置
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="D:\Tools\Web\ssl\tomcat.keystore" keystorePass="tomcat" ciphers="tomcat"/>NIO HTTP/1.1 Connector配置
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="150" connectionTimeout="20000" redirectPort="8443" />AJP 1.3 Connector配置
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />Native APR Connector配置
具体如何安装,可自行搜索。配置如下:
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol" maxThreads="150" connectionTimeout="20000" redirectPort="8443"/>
查看Tomcat当前使用的是哪个protocol可以根据Tomcat的启动日志来查看
对Tomcat7而言,
bio 信息: Starting ProtocolHandler ["http-bio-8080"] 2015-8-20 22:17:50 org.apache.coyote.AbstractProtocol start nio 信息: Starting ProtocolHandler ["http-nio-8080"] 2015-8-20 22:27:50 org.apache.coyote.AbstractProtocol start apr 信息: Starting ProtocolHandler ["http-apr-8080"] 2015-8-20 22:29:50 org.apache.coyote.AbstractProtocol start
对Tomcat6而言,
bio 2015-8-20 22:25:36 org.apache.coyote.http11.Http11Protocol start 信息: Starting Coyote HTTP/1.1 on http-8080 nio 2015-8-20 22:30:38 org.apache.coyote.http11.Http11NioProtocol start 信息: Starting Coyote HTTP/1.1 on http-8080
注意:Tomcat6的处理线程的名字如http-8080-1,而Tomcat7如http-bio-8080-exec-1,增加了protocol的协议信息。
/** * Server socket acceptor thread. */ 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) { //如果endpoint暂停的话,这里也停止运行。 try { Thread.sleep(1000); } catch (InterruptedException e) { // Ignore } } // Accept the next incoming connection from the server socket try { //acceptSocket会阻塞,直到新的请求的到来。 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 } } }
/** * Process given socket. */ protected boolean processSocket(Socket socket) { try { if (executor == null) {//关键代码,决定是使用Tomcat自己的线程管理系统还是JDK的ThreadPoolExecutor, getWorkerThread().assign(socket); } else { executor.execute(new SocketProcessor(socket));//如果Server.xml中的Connector配置了executor="tomcatThreadPool"则,使用JDK的ThreadPoolExecutor } } 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; }以上代码比较关键,对于Tomcat6而言,默认情况下,是使用自己的模块管理线程的,只有配置了executor="tomcatThreadPool"才使用JDK的ThreadPoolExecutor,JDK的ThreadPoolExecutor管理线程时,具有线程收缩管理的功能,但是使用Tomcat 6自己的线程管理模块并没有伸缩管理的功能。
protected Worker getWorkerThread() { // Allocate a new worker thread synchronized (workers) { Worker workerThread; while ((workerThread = createWorkerThread()) == null) {//如果得不到workThread,则等待。只有在线程数到达最大线程数时,返回值才为null,这个时候需要等待有线程处理完某项工作,执行recycleWorkerThread中的workers.notify();才能被唤醒。 try { workers.wait(); } catch (InterruptedException e) { // Ignore } } return workerThread; } }
protected Worker createWorkerThread() { synchronized (workers) {// 必须为线程安全的操作,因为可能有多个线程同时过来,这时候对WorkerStack的操作的非线程安全的,需要使用synchronized保证线程安全 if (workers.size() > 0) {//如果WorkerStack不为空,则从WorkerStack中取值 curThreadsBusy++;// return workers.pop(); } if ((maxThreads > 0) && (curThreads < maxThreads)) {//如果最大线程数大于0并且当前最大线程数小于规定最大线程数 curThreadsBusy++; if (curThreadsBusy == maxThreads) { log.info(sm.getString("endpoint.info.maxThreads", Integer.toString(maxThreads), address, Integer.toString(port))); } return (newWorkerThread());//返回一个新线程 } else { if (maxThreads < 0) {//最大线程数为负数,则无限制的返回Worker curThreadsBusy++; return (newWorkerThread()); } else { return (null);//如果大于最大线程数,则返回null } } } }
protected Worker newWorkerThread() { Worker workerThread = new Worker(); workerThread.start(); return (workerThread); }
public void start() { thread = new Thread(this);//新建线程 thread.setName(getName() + "-" + (++curThreads));//此处设置线程名,http-端口 thread.setDaemon(true);//所有工作线程都为daemon的 thread.start(); }
protected void recycleWorkerThread(Worker workerThread) {//处理完成后,将线程放到WorkerStack中!回收操作 synchronized (workers) {//必须为线程安全的,因为WorkerStack为非线程安全 workers.push(workerThread);//放到WorkerStack中 curThreadsBusy--; workers.notify(); } }
public void run() { // Process requests until we receive a shutdown signal while (running) { // Wait for the next socket to be assigned Socket socket = await();//等待新的请求到来后调用notifyall()方法 if (socket == null) continue; // Process the request from this socket if (!setSocketOptions(socket) || !handler.process(socket)) {//处理socket // Close socket try { socket.close(); } catch (IOException e) { } } // Finish up this request socket = null; recycleWorkerThread(this);//回收worker } }以上代码分析,可以知道,只有对WorkerStack的存取操作,并没有线程数的收缩操作, 在峰值过后,线程数仍然会维持在峰值位置!
public boolean process(Socket socket) { Http11Processor processor = recycledProcessors.poll();//每次从资源池中取 try { if (processor == null) { processor = createProcessor();//取不到则创建 } //....省略 processor.process(socket);//Http11Processor.process return false; }//...省略 catch (Throwable e) {//此处使用了Throwable顶级异常 Http11Protocol.log.error (sm.getString("http11protocol.proto.error"), e); } finally { if (processor instanceof ActionHook) { ((ActionHook) processor).action(ActionCode.ACTION_STOP, null); } //回收processor recycledProcessors.offer(processor); } return false; }Http11Processor.process方法中的核心代码就是
adapter.service(request, response);此处的adapter是CoyoteAdapter,次类的地位非常重要,是连接catalina和coyote组件的适配器。是适配器模式的一个典型应用,关于适配器模式可以查看 设计模式笔记9:适配器模式(Adapter Pattern)。
CoyoteAdapter实现了Adapter,同时使用Connector作为自己的内部对象,是一种对象适配器模式,遵循了多用组合少用继承的设计准则,其适配coyote组件,为catalina组件提供服务。
而Adapter.service()方法的核心代码就是
connector.getContainer().getPipeline().getFirst().invoke(request, response);这段代码,直接将请求交给了catalina组件进行处理。关于catalina组件的处理过程,将在下篇博文中进行分析。