Tomcat服务器配置参考 

 
 

Coyote HTTP/1.1 Connector

Tomcat服务器配置参考

 

概述

Coyote HTTP/1.1 Connector元素是一个支持HTTP/1.1协议的Connector组件。它使Catalina除了能够执行servlet和JSP页面外,还能够作为一个单独的web server运行。Connector对象的实例在服务器上监听特定的TCP端口。一个Service可以配置一个或多个这样的Connector,每个Connector都把请求转发给对应Engine进行处理,并产生响应。

在 服务器启动的时候,Connector会创建一些请求处理线程(基于minProcessors属性值)。每个请求需要一个线程为其服务,直到服务完成。 如果同一时刻的请求数多于可用的请求处理线程,会创建额外的处理线程,线程数的上限是maxProcessors。如果已经到达了最大请求数,仍然有请求 发生,它们被缓存在由Connector创建的server socket中,直到缓存的上限(由acceptCount属性的值定义)。这以后所有的请求都会收到“拒绝连接”的错误,直到有资源能够处理它们。

属性
公共属性

所有Connector的实现支持如下属性:

属性 描述
enableLookups

如果希望调用request.getRemoteHost()进行DNS查询,以返回远程客户的实际主机名,将enableLookups设为true。如果希望忽略DNS查询,仅仅返回IP地址,设为false(这样提高了性能)。缺省情况下,DNS查询是使能的。

redirectPort 如果Connector支持非SSL请求,在收到一个要求使用SSL传输的请求以后,Catalina会自动将该请求重定向到这里指定的端口号。
scheme

调用request.getScheme()时返回的协议名称。比如,在SSL Connector上可能将这个属性设为“https”。缺省值为“http”,更多信息,参考SSL支持

secure

如果希望在该Connector接收到的请求上调用request.isSecure()返回true,设置该属性为true。缺省值为false。

标准实现

Coyote HTTP/1.1 Connector的标准实现是org.apache.coyote.tomcat5.CoyoteConnector。它还支持如下的附加属性

属性 描述
acceptCount

当所有可能的请求处理线程都被使用的时候,连接请求队列的最大长度。如果该队列为满,所有的请求都被拒绝,缺省值为10。

address 对于具有多个IP地址的server,这个属性指定了用于监听特定端口的地址。缺省情况下,端口作用于server的所有IP地址。
bufferSize

connector创建的输入流缓冲区的大小(以字节为单位)。缺省情况下,bufferSize=2048。

compression

为 了节省服务器带宽,Connector可能使用HTTP/1.1GZIP压缩。这个参数的可接受值为“off”(不使用压缩),“on”(压缩文本数据),“force”(在所有的情况下强制压缩),或者使用一个数值 整数(等价于“on”,但是指定了输出被压缩是的最小的数据数)。如果content-length未知, 而compression设置成“on”或者 更强,输出也会被压缩。如果没有指定,这个属性被设成“off”

connectionLinger

当Connector使用的socket被关闭的时候,保留该socket的时间,以毫秒为单位。缺省值为-1(不使用socket linger)

connectionTimeout

在Connector接受一个连接以后,等待发生第一个请求的时间,以毫秒为单位。缺省值为60000(60秒)

debug

日志消息的详细程度,数字越大,输出越详细,如果没有指定,使用缺省值0。

disableUploadTimeout 这个标志允许servlet container在一个servlet执行的时候,使用一个不同的,更长的连接超时。最终的结果是给servlet更长的时间以便完成其执行,或者在数据上载的时候更长的超时时间。如果没有指定,设为false。
maxKeepAliveRequests 在server关闭连接之前,接受的HTTP请求的最大数目。如果该值设为1,会禁止HTTP/1.0保活,同时也会禁止HTTP/1.1保活和pipelining。如果没有指定,设为100。
maxSpareThreads

在线程池开始停止不必要的线程之前,允许存在的最大未使用的请求处理线程。缺省值为50。

 

maxThreads

Connector能够创建的最大请求处理线程数,这个值决定了同时能够处理的最大请求数。如果没有指定,缺省值为200。

minSpareThreads

当Connector第一次启动时,创建的请求处理线程数。connector同时必须保证指定数目的空闲处理线程。这个值应该设置成比maxThreads小的数值,缺省值为4。

port

Connector创建server socket并等待连接的TCP端口号。操作系统在特定的IP地址上只允许一个服务器应用程序监听特定的端口。

protocol

为了使用HTTP处理器,该属性值必须为HTTP/1.1(缺省值)

proxyName

如果connector在代理配置中使用,将这个属性设置成调用request.getServerName()时返回的服务器名称。更多信息参考代理支持。

proxyPort

如果Connector在代理配置中使用,这个属性指定了调用request.getServerPort()返回的端口值。更多信息参考代理支持。

socketBuffer

socket输出缓冲区的大小。如果为-1,不使用缓冲。缺省值为9000字节。

tcpNoDelay

如果为true,服务器socket会设置TCP_NO_DELAY选项,在大多数情况下可以提高性能。缺省情况下设为true。

嵌套组件

可以嵌套在Connector中的唯一元素是Factory,用来配置服务器套接口工厂组件。这个组件从来都不需要,现在支持这个组件是为了与Tomcat的早期版本兼容。

专有特征
HTTP/1.1 和HTTP/1.0 支持

Connector 支持HTTP/1.1协议的所有必需特征(如RFC2616所描述的),包括永久性连接,流水线,expectations and chunked encoding。如果客户端(通常是一个浏览器)只支持HTTP/1.0,Connector会自动跳回到HTTP/1.0。不需要特殊的配置来使能这 个支持。Connector也支持HTTP/1.0保活机制。

RFC2616要求HTTP服务器的响应总是以它们宣称支持的最高HTTP版本开始。因此,这个Connector在它的响应的开始总是返回HTTP/1.1。

 

日志输出

Connector产生的任何调试或者异常信息都会被自动路由到与Connector所属的Engine的Logger。不需要特殊的配置来使能这个支持。

 

代理支持
Tomcat位于代理服务器后面时,可以使用proxyName和proxyPort属性。这些属性修改了调用 requset.getServerName()和request.getServerPort()的返回值,用来构造重定向的绝对URL。如果不设置这 些值,返回值反映了代理服务器收到的连接的服务器名称和端口号,而不是客户端发起的服务器名称和端口号。

更多信息,参考代理支持HOW-TO

SSL支持

对Connector的特定实例,可以将secure属性设为true,来使能SSL支持。另外,可以配置如下属性:

 

属性 描述
algorithm

使用的认证编码算法。缺省值为SunX509。

 

clientAuth

如果在接受某个连接之前,需要客户端发送有效证书链,将该值设为true。如果为false(缺省值),不需要使用证书链。除非客户端请求被CLIENT-CERT认证保护的资源。

 

keystoreFile

存储服务器证书的keystore文件路径。缺省情况下,路径指向运行

 

keystorePass

用来访问服务器证书的密码,缺省值未"changeit"

 

keystoreType

用于存储服务器证书的keystore文件的类型。缺省值未"JKS"

 

sslProtocol

SSL协议的版本号,缺省值是TLS

 

ciphers

可以使用的加密算法列表,用逗号分开。如果没有指定,可以使用任何算法。

 

 

 

Connector是在开始Connector探索之路之前,先看看Connector几个关键字

通过对如上名词的组合,Tomcat组成了如下的Connector系列:

  • Http11Protocol:支持HTTP1.1协议的连接器
  • Http11NioProtocol:支持HTTP1.1 协议+ NIO的连接器
  • Http11AprProtocol:使用APR技术处理连接的连接器
  • AjpProtocol:支持AJP协议的连接器
  • AjpAprProtocol:使用APR技术处理连接的连接器

二、范例       我们以最简单的Http11Protocol为例,看看从请求进来到处理完毕,连接器部件是处理处理的。首先我们利用Tomcat组件组成我们一个最简单的WebServer,其具备如下功能:

  • 监停某个端口,接受客户端的请求,并将请求分配给处理线程
  • 处理线程处理请求,分析HTTP1.1请求,封装Request/Response对象,并将请求由请求处理器处理
  • 实现最简单的请求处理器,向客户端打印Hello World

代码非常简单,首先是主功能(这里,我们利用JDK5.0的线程池,连接器不再管理线程功能):

Java代码
  1. package ray.tomcat.test;  
  2.  
  3. import java.util.concurrent.BlockingQueue;  
  4. import java.util.concurrent.LinkedBlockingQueue;
  5. import java.util.concurrent.ThreadPoolExecutor;
  6. import java.util.concurrent.TimeUnit;  
  7.  
  8. import org.apache.coyote.http11.Http11Protocol;
  9.  
  10. public class TomcatMainV2  
  11. {  
  12.     public static void main(String[] args) throws Exception  
  13.     {  
  14.         Http11Protocol protocol = new Http11Protocol();
  15.         protocol.setPort(8000);  
  16.         ThreadPoolExecutor threadPoolExecutor = createThreadPoolExecutor();
  17.         threadPoolExecutor.prestartCoreThread();
  18.         protocol.setExecutor(threadPoolExecutor);
  19.         protocol.setAdapter(new MyHandler());
  20.         protocol.init();  
  21.         protocol.start();  
  22.     }  
  23.  
  24.     public static ThreadPoolExecutor createThreadPoolExecutor()
  25.     {  
  26.         int corePoolSize = 2;
  27.         int maximumPoolSize = 10;  
  28.         long keepAliveTime = 60;  
  29.         TimeUnit unit = TimeUnit.SECONDS;  
  30.         BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>();  
  31.         ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
  32.                 corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
  33.         return threadPoolExecutor;  
  34.     }  

请求处理器向客户端打引Hello World,代码如下

Java代码
  1. package ray.tomcat.test;  
  2.  
  3. import java.io.ByteArrayOutputStream;  
  4. import java.io.OutputStreamWriter;  
  5. import java.io.PrintWriter;  
  6.  
  7. import org.apache.coyote.Adapter;  
  8. import org.apache.coyote.Request;  
  9. import org.apache.coyote.Response;  
  10. import org.apache.tomcat.util.buf.ByteChunk;  
  11. import org.apache.tomcat.util.net.SocketStatus;  
  12.  
  13. public class MyHandler implements Adapter
  14. {  
  15.     //支持Comet,Servlet3.0将对Comet提供支持,Tomcat6目前是非标准的实现  
  16.     public boolean event(Request req, Response res, SocketStatus status)
  17.             throws Exception  
  18.     {  
  19.         System.out.println("event");  
  20.         return false;  
  21.     }  
  22.  
  23.     //请求处理  
  24.     public void service(Request req, Response res) throws Exception  
  25.     {  
  26.         System.out.println("service");  
  27.  
  28.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  29.         PrintWriter writer = new PrintWriter(new OutputStreamWriter(baos));  
  30.         writer.println("Hello World");  
  31.         writer.flush();  
  32.  
  33.         ByteChunk byteChunk = new ByteChunk();  
  34.         byteChunk.append(baos.toByteArray(), 0, baos.size());  
  35.         res.doWrite(byteChunk);  
  36.     }     

     运行主程序,在浏览器中输入http://127.0.0.1:8000,我们可以看到打印”Hello World” 三、分析      以如上Http11Protocol为例,我们可以看到,Tomcat实现一个最简单的处理Web请求的代码其实非常简单,其主要包括如下核心处理类:

  • Http11Protocol:Http1.1协议处理入口类,其本身没有太多逻辑,对请求主要由JIoEndPoint类处理
  • Http11Protocol$Http11ConnectionHandler:连接管理器,管理连接处理队列,并分配Http11Processor对请求进行处理
  • Http11Processor:请求处理器,负责HTTP1.0协议相关的工作,包括解析请求和处理响应信息,并调用Adapter做实际的处理工作,如上我们看到了我们自定义的Adapter实现响应”Hello World”
  • JIoEndPoint:监停端口,启动接受线程准备接收请求,在请求接受后转给工作线程处理
  • JIoEndPoint$Acceptor:请求接收器,接收后将Socket分配给工作线程继续后续处理
  • JIoEndPoint$Worker:工作线程,使用Handler来处理请求,对于我们的HTTP1.1协议来说,其实现是Http11Protocol$Http11ConnectionHandler。这部分不是必须的,也可以选择JDK的concurrent包的线程池

      实际上各种连接器实现基本大同小异,基本上都是由如上部分组合而成

      1.初始化:首先,还是从入口开始,先看看初始化init

Java代码
  1. public void init() throws Exception {
  2.         endpoint.setName(getName());  
  3.         endpoint.setHandler(cHandler); //请求处理器,对于HTTP1.1协议,是Http11Protocol$Http11ConnectionHandler  
  4.  
  5.          
  6. // 初始化ServerSocket工厂类,如果需SSL/TLS支持,使用JSSESocketFactory/PureTLSSocketFactory  
  7. . . . (略)         
  8.               //主要的初始化过程实际是在endpoint(JIoEndpoint)  
  9.         endpoint.init();  
  10.         . . . (略)  
  11.     } 

      Http11Protocol的初始化非常简单,准备好ServerSocket工厂,调用JIoEndPoint的初始化。让我们接下来看看JIoEndPoint的初始化过程

Java代码
  1. public void init()  
  2.         throws Exception {  
  3.  
  4.         if (initialized)  
  5.             return;  
  6.          
  7.         // Initialize thread count defaults for acceptor  
  8.         // 请求接收处理线程,这个值实际1已经足够  
  9.         if (acceptorThreadCount == 0) {  
  10.             acceptorThreadCount = 1;  
  11.         }  
  12.         if (serverSocketFactory == null) {  
  13.             serverSocketFactory = ServerSocketFactory.getDefault();
  14.         }  
  15.          
  16.         //创建监停ServerSocket,port为监听端口,address为监停地址  
  17.         // backlog为连接请求队列容量(@param backlog how many connections are queued)  
  18.         if (serverSocket == null) {  
  19.             try {  
  20.                 if (address == null) {  
  21.                     serverSocket = serverSocketFactory.createSocket(port, backlog);
  22.                 } else {  
  23.                     serverSocket = serverSocketFactory.createSocket(port, backlog, address);
  24.                 }  
  25.             } catch (BindException be) {  
  26.                 throw new BindException(be.getMessage() + ":" + port);  
  27.             }  
  28.         }  
  29.         //if( serverTimeout >= 0 )  
  30.         //    serverSocket.setSoTimeout( serverTimeout );  
  31.          
  32.         initialized = true;    
  33.     } 

        可以看到,监停端口在此处准备就绪

Java代码
  1. public void start()  
  2.      throws Exception {  
  3.      // Initialize socket if not done before  
  4.      if (!initialized) {  
  5.          init();  
  6.      }  
  7.      if (!running) {  
  8.          running = true;  
  9.          paused = false;  
  10.  
  11.          // Create worker collection  
  12.          // 初始化工作线程池,有WorkerStack(Tomcat自实现)和Executor(JDK concurrent包)两种实现 
  13.          if (executor == null) {  
  14.              workers = new WorkerStack(maxThreads);  
  15.          }  
  16.  
  17.          // Start acceptor threads  
  18.          // 启动请求连接接收处理线程  
  19.          for (int i = 0; i < acceptorThreadCount; i++) {  
  20.              Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);  
  21.              acceptorThread.setPriority(threadPriority);
  22.              acceptorThread.setDaemon(daemon); //设置是否daemon参数,默认为true  
  23.              acceptorThread.start();  
  24.          }  
  25.      }  

      2.准备好连接处理:初始化完毕,准备好连接处理,准备接收连接上来,同样的,Http11Protocol的start基本没干啥事,调用一下JIoEndPoint的start,我们来看看JIoEndPoint的start

Java代码
  1. public void start()  
  2.      throws Exception {  
  3.      // Initialize socket if not done before  
  4.      if (!initialized) {  
  5.          init();  
  6.      }  
  7.      if (!running) {  
  8.          running = true;  
  9.          paused = false;  
  10.  
  11.          // Create worker collection  
  12.          // 初始化工作线程池,有WorkerStack(Tomcat自实现)和Executor(JDK concurrent包)两种实现 
  13.          if (executor == null) {  
  14.              workers = new WorkerStack(maxThreads);  
  15.          }  
  16.  
  17.          // Start acceptor threads  
  18.          // 启动请求连接接收处理线程  
  19.          for (int i = 0; i < acceptorThreadCount; i++) {  
  20.              Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);  
  21.              acceptorThread.setPriority(threadPriority);
  22.              acceptorThread.setDaemon(daemon); //设置是否daemon参数,默认为true  
  23.              acceptorThread.start();  
  24.          }  
  25.      }  

      主要处理的事情无非就是准备和工作线程(处理具体请求的线程度池,可选,也可以使用JDK5.0的线程池),连接请求接收处理线程(代码中,一般acceptorThreadCount=1)

      3.连接请求接收处理:准备就绪,可以连接入请求了。现在工作已经转到了Acceptor(JIoEndPoint$Acceptor)这里,我们看看Acceptor到底做了些啥

Java代码
  1. public void run() {  
  2.  
  3.             // Loop until we receive a shutdown command  
  4.             while (running) {
  5.                 . . . (略)  
  6.                     //阻塞等待客户端连接  
  7.                     Socket socket = serverSocketFactory.acceptSocket(serverSocket);
  8.                     serverSocketFactory.initSocket(socket);
  9.                     // Hand this socket off to an appropriate processor  
  10.                     if (!processSocket(socket)) {  
  11.                         // Close socket right away  
  12.                         try {  
  13.                             socket.close();
  14.                         } catch (IOException e) {
  15.                             // Ignore  
  16.                         }  
  17.                     }  
  18.                   . . . (略)  
  19.             }  
  20. }  
  21. . . . (略)  
  22.     protected boolean processSocket(Socket socket) {  
  23.         try {  
  24.             //由工作线程继续后续的处理  
  25.             if (executor == null) {  
  26.                 getWorkerThread().assign(socket);
  27.             } else {  
  28.                 executor.execute(new SocketProcessor(socket));  
  29.             }  
  30.         } catch (Throwable t) {  
  31.             . . . (略)             
  32. return false;  
  33.         }  
  34.         return true;  
  35.     } 

       实际上也没有什么复杂的工作,无非就是有连接上来之后,将连接转交给工作线程(SocketProcessor)去处理

       4.工作线程:SocketProcessor

Java代码
  1. public void run() {  
  2.             // Process the request from this socket  
  3.             if (!setSocketOptions(socket) || !handler.process(socket)) {
  4.                 // Close socket  
  5.                 try {  
  6.                     socket.close();  
  7.                 } catch (IOException e) {
  8.                 }  
  9.             }  
  10.             // Finish up this request  
  11.             socket = null;  
  12.         }    

    工作线程主要是设置一下Socket参数,然后将请求转交给handler去处理,需要注意一下如下几个连接参数的意义:

  • SO_LINGER:若设置了SO_LINGER并确定了非零的超时间隔,则closesocket()调用阻塞进程,直到所剩数据发送完毕或超时。这种关闭称为“优雅的”关 闭。请注意如果套接口置为非阻塞且SO_LINGER设为非零超时,则closesocket()调用将以WSAEWOULDBLOCK错误返回。若在一个流类套接口上设置了SO_DONTLINGER,则closesocket()调用立即返回。但是,如果可能,排队的数据将在套接口关闭前发送。请注意,在这种情况下WINDOWS套接口实现将在 一段不确定的时间内保留套接口以及其他资源(TIME_WAIT),这对于想用所以套接口的应用程序来说有一定影响。默认此参数不打开
  • TCP_NODELAY:是否打开Nagle,默认打开,使用Nagle算法是为了避免多次发送小的分组,而是累计到一定程度或者超过一定时间后才一起发送。对于AJP连接,可能需要关注一下这个选项。
  • SO_TIMEOUT:JDK API注释如下,With this option set to a non-zero timeout,a read() call on the InputStream associated with this Socket will block for only this amount of time.  If the timeout expires, a java.net.SocketTimeoutException is raised, though the Socket is still valid. The option must be enabled prior to entering the blocking operation to have effect. The timeout must be > 0。默认设置的是60秒

        关于默认的设置,可以参见org.apache.coyote.http11.Constants定义       5.最终请求终于回到了Handler,此处的Handler实现是org.apache.coyote.http11.Http11Processor,其主要处理一些HTTP协议性细节的东西,此处代码不再列出,有兴趣可以自行读代码。最终请求终于回到了我们的Adapter对象,一个请求处理完毕,功德圆满。

来源:http://1632004.blog.163.com/blog/static/29991497201201912858468/

相关文章:

  • 2022-01-08
  • 2021-05-27
  • 2021-05-09
  • 2021-06-22
  • 2021-10-10
  • 2021-06-13
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-01-03
  • 2021-06-04
  • 2021-12-30
  • 2021-11-11
  • 2021-06-10
相关资源
相似解决方案