tomcat服务器解析(一)

httpservlet自己并不能独立运行,需要依赖于一个web容器才能够运行。维基百科中对httpservlet和web容器的关系做了简要说明

A web container (also known as a servlet container) is essentially the component of a web server that interacts with the servlets. The web container is responsible for managing the lifecycle
of servlets, mapping a URL to a particular servlet and ensuring that the URL requester has the correct access rights.

apache的官网(http://tomcat.apache.org/tomcat-8.0-doc/config/index.html)上有相应的文档对容器的配置和线程的关系进行说明,在介绍HTTP Connector时,有如下的描述。

At server startup time, this Connector will create a number of request processing threads (based on the value configured for the minSpareThreads attribute). Each
incoming request requires a thread for the duration of that request. If more simultaneous requests are received than can be handled by the currently available request processing threads, additional threads will be created up to the configured maximum
(the value of the maxThreads attribute). If still more simultaneous requests are received, they are stacked up inside the server socket created by the Connector, up to the configured maximum (the value of the acceptCount attribute).
Any further simultaneous requests will receive "connection refused" errors, until resources are available to process them.

注意其中飘红的字段部分,每个到达这个Connector的请求都有一个对应的线程去处理,所以已经可以确认,每个请求是会有一个对应的线程去进行处理。现在只需要确认,线程的创建时机,调用时机即可。

The Executor represents a thread pool that can be shared between components in Tomcat. Historically there has been a thread pool per connector created but
this allows you to share a thread pool, between (primarly) connector but also other components when those get configured to support executors。

注意飘红的字,以前的版本是每个connector都有一个自己的线程池。这一点,在tomcat-5.5的http connector的文档中有相应的说明(http://tomcat.apache.org/tomcat-5.5-doc/config/http.html),原文为“At server startup time, this Connector will create
a number of request processing threads”, 说明connector在启动的时候就会创建一个线程池,。但在tomcat-6.0的http connector的文档中,这句话被去掉了。同时相应的

https://tomcat.apache.org/tomcat-4.1-doc/catalina/docs/api/org/apache/catalina/connector/http/HttpConnector.html的文档中说明HttpConnector在后续的版本中被移除了。

一些新版本的tomcat代码在官网上可以找到,对于一些较早的版本,可以从这里(http://www.java2s.com/Code/Jar/c/Downloadcatalinasourcesjar.htm)找到(catalina-source.jar)相应的源代码 。通过catalian.jar的源代码上也能看出HttpConnector类的变化。

通过上面的对比可以猜测,线程池是在服务中配置的,在connector之间共享的。

OK,官网上的理论介绍到这里就差不多了,下面通过源代码来进行解读。官网上有提到Server的实现是org.apache.catalina.core.StandardServer通过这个类找到相应的jar包,catalina.jar。借用一个已有的maven工程引入catalina.jar包的maven依赖,相关的maven依赖如下

<dependency>

<groupId >org.apache.tomcat </groupId>

<artifactId >tomcat-catalina</artifactId >

<version >8.0.20 </version>

</dependency>

ps: jetty的代码参考 org.eclipse.jetty.server.Server; org.eclipse.jetty.server.Connector;
---- 可作为对比学习的参考

在详细看代码之前,首先得了解官网中关于tomcat的web服务器的几个概念 server、service、connector以及engine

server就代表一个web容器,代表一个服务器,其它的几个概念都是属于server的一部分

service表示web容器能够提供的某项服务。如果把server按照功能粒度进行细化,那么每个单元就是一个service

connector和engine是组成server的两个组成部分。engine是执行用户的请求的引擎,connector是请求执行的通道。connector本身并不做请求的处理,只是提供通道功能。

StandardServer代表一个服务器实例,它有一个Service数组的属性,为当前这个Server所拥有的service。这里省去了其它与本次讨论无关的属性

public final class StandardServer extends LifecycleMBeanBase implements Server{

/**

* The set of Services associated with this Server.

*/

private Service services[]
new Service[0];

private final Object servicesLock = new Object();

}

StandardService表示一项具体的服务能力,它有一个Connector数组的属性,是属于这个Service的连接器。

public class StandardService extends LifecycleMBeanBase implements Service
{

/**

* The <code>Server</code> that
owns this Service, if any.

*/

private Server
server = null;

/**

* The set of Connectors associated with this Service.

*/

protected Connector
connectors[] = new Connector[0];

private final Object
connectorsLock = new Object();

     protected final ArrayList<Executor>
executors = new ArrayList<>();

/**

* The Container associated with this Service.

*/

protected Container
container = null;

}

上面列出了与我们讨论相关的一些Service的属性。server和connectors不用再多说,container可作为一个Engine来作为服务的执行引擎。executors代表为这个服务配置的线程池,可在多个Connector之间共享。

从代码结构上,已经大致与上面的结论对应起来。那么,具体线程池的初始化、调用,请求的解析又是如何完成的呢?

因为Connector(org.apache.catalina.connector.Connector)是请求传递的通道,并且在不同版本之间有过较大结构升级,我们以Connector为突破口来学习。

来看下构造函数和启动函数

public Connector(String protocol)
{

setProtocol(protocol);

// Instantiate protocol handler

ProtocolHandler p = null;

try {

Class<?> clazz = Class.forName(protocolHandlerClassName);

p = (ProtocolHandler) clazz.newInstance();

catch (Exception
e) {

log.error(sm.getString(

"coyoteConnector.protocolHandlerInstantiationFailed" ),
e);

finally {

this.protocolHandler
= p;

}

if (!Globals.STRICT_SERVLET_COMPLIANCE)
{

URIEncoding = "UTF-8";

URIEncodingLower = URIEncoding.toLowerCase(Locale.ENGLISH);

}

}

@Override

protected void initInternal() throws LifecycleException
{

super.initInternal();

// Initialize adapter

adapter = new CoyoteAdapter(this);

protocolHandler.setAdapter(adapter);

// Make sure parseBodyMethodsSet has a default

ifnull ==
parseBodyMethodsSet ) {

setParseBodyMethods(getParseBodyMethods());

}

if (protocolHandler.isAprRequired()
&&

!AprLifecycleListener.isAprAvailable()) {

throw new LifecycleException(

sm.getString( "coyoteConnector.protocolHandlerNoApr" ,

getProtocolHandlerClassName()));

}

try {

protocolHandler.init();

catch (Exception
e) {

throw new LifecycleException

(sm.getString

( "coyoteConnector.protocolHandlerInitializationFailed" ),
e);

}

}

/**

* Begin processing requests via this Connector.

*

@exception LifecycleException
if a fatal startup error occurs

*/

@Override

protected void startInternal() throws LifecycleException
{

// Validate settings before starting

if (getPort()
< 0) {

throw new LifecycleException(sm.getString(

"coyoteConnector.invalidPort",
Integer.valueOf(getPort())));

}

setState(LifecycleState.STARTING);

try {

protocolHandler.start();

catch (Exception e)
{

String errPrefix = "";

if(this .service
!= null) {

errPrefix += "service.getName(): \"" + this .service.getName()
+ "\"; ";

}

throw new LifecycleException

(errPrefix + " " +
sm.getString

( "coyoteConnector.protocolHandlerStartFailed" ),
e);

}

}

构造函数,主要是初始化一个ProtocolHandler对象。初始化函数,对这个PrococolHandler对象进行初始化。启动函数,则主要是启动这个对象。

ProtocolHandler相关的信息如下

/**

* Coyote Protocol handler class name.

* Defaults to the Coyote HTTP/1.1 protocolHandler.

*/

protected String
protocolHandlerClassName = "org.apache.coyote.http11.Http11NioProtocol" ;

到这里为止,还没有发现与处理http请求和调用线程池相关的地方。所以,继续去看Http11NioProtocol这个类。

Http11NioProtocol所处的类继承关系如下

Http11NioProtocol extends AbstractHttp11JsseProtocol<NioChannel>

public abstract class AbstractHttp11JsseProtocol <S> extends AbstractHttp11Protocol<S>

public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S>

在ProtocolHandler上的init和start所执行的操作,通过AbstractProtocol这两个方法的实现来看

public void init() throws Exception
{

// 这里省略了调试日志和rpc服务注册相关的代码。。。

String endpointName = getName();

endpoint.setName(endpointName.substring(1, endpointName.length()-1));

try {

endpoint.init();

catch (Exception
ex) {

getLog().error(sm.getString( "abstractProtocolHandler.initError" ,

getName()), ex);

throw ex;

}

}

@Override

public void start() throws Exception
{

if (getLog().isInfoEnabled())

getLog().info(sm.getString( "abstractProtocolHandler.start" ,

getName()));

try {

endpoint.start();

catch (Exception
ex) {

getLog().error(sm.getString( "abstractProtocolHandler.startError" ,

getName()), ex);

throw ex;

}

}

从代码上看AbstractProtocol本身并不做太多请求解析处理相关的事情,它是把事情委托给了一个AbstractEndpoint对象来完成。类似init和start生命周期,其它的几个生命周期如pause、resume、stop、destory也主要是通过AbstractEndpoint对象来完成。

时间: 2024-08-10 08:05:29

tomcat服务器解析(一)的相关文章

tomcat服务器解析(四) ---- 组成模块分解

前面部分梳理了tomcat服务器处理http请求的一个流程,这里进行内容总结,梳理下在tomcat服务器实现中的,各种功能模块. [Endpoint] Endpoint是基础的网络设施,通过Endpoint来实现网络连接和控制,它是服务器对外I/O操作的接入点.主要任务是管理对外的socket连接,同时将建立好的socket连接交到合适的工作线程中去. content:org.apache.tomcat.util.net.AbstractEndpoint,org.apache.tomcat.ut

tomcat服务器解析(六)-- Acceptor

Acceptor负责用来管理连接到tomcat服务器的数量,来看看Acceptor在tomcat服务器中的应用,是如何实现连接管理的,socket连接建立成功之后,是如何实现内容的读写的(读写是交由Poller机制去完成). 先准备一点java nio中实现socket连接所需的基础知识:SocketChannel和ServerSocketChannel SocketChannel和ServerSocketChannel的概念与基础的阻塞式的java 网络编程中的socket和serversoc

tomcat服务器解析

tomcat服务器软件官方下载地址: 1.首先说明web服务器软件的作用吧! web服务器软件就是把把本地的资源共享给外部访问 2.为什么选用tomcat服务器软件? 因为tomcat服务器是阿帕奇组织的产品,他免费开源很适合web入门学习使用,而且tomcat服务器软件完全用java编写. 我使用的是tomcat9.0.13版本,给大家介绍一下文件的情况吧 bin目录:存放tomcat的命令也就是一些timcat的软件比如(启动服务器,和关闭服务器...) conf: 存放tomcat的配置信

tomcat服务器解析(七)-- Processor&amp;Endpoint&amp;ProtocolHandler

请求到达Poller处理,最终是由Processor来进行处理,为了说明这中间过程所涉及的部分,先整理下在tomcat服务的各个组成部分:ProtocolHandler.Endpoint.Endpoint.Handler.Processor 它们之间的引用关系如下 [ProtocolHandler]     <---------   Connector org.apache.coyote.ProtocolHandler org.apache.coyote.AbstractProtocol org

tomcat服务器解析(五)-- Poller

在前面的分析中介绍过,Acceptor的作用是控制与tomcat建立连接的数量,但Acceptor只负责建立连接.socket内容的读写是通过Poller来实现的. Poller使用java nio来实现连接的管理. 关于nio,主要需要明确三个概念:Channel.Selector和SelectionKey. 在这里的使用上,它们之间的关系可以简单这样理解,Channel必须注册到Selector上才能用于接收socket数据,在Selector上有数据到达的Channel可以用Selecti

tomcat服务器解析(二) --- Endpoint

Endpoint主要用来提供基础的网络I/O服务,封装了网络通讯相关的细节.在AbstractProtocol中对Endpoint有这样一段注释 /** * Endpoint that provides low - level network I/O - must be matched to the * ProtocolHandler implementation (ProtocolHandler using BIO, requires BIO * Endpoint etc.). */ prot

tomcat服务器解析(三)---- Handler for Endpoint

前面的分析到,请求最终被封装成了一个SocketProcessor对象,放在Executors线程池中去执行.这些都还只是在tomcat内部的socket的处理层面上,那请求最终是如何被转到开发人员所写的servlet上的? NioEndpoint.SocketProcessor所做的工作在私有方法doRun中                 if (handshake == 0) { SocketState state = SocketState.OPEN; // Process the re

在Tomcat服务器中去端口访问域名

在刚购买域名并解析后,从外网访问Tomcat服务器时是需要在域名后面加端口":8080".要去端口访问的步骤如下: 在Tomcat目录下的conf文件夹下,打开server.xml文件. 找到: 1 <Connector port="8080" redirectPort="8443" connectionTimeout="20000" protocol="HTTP/1.1"/> 改成: 1 &l

Tomcat服务器绑定域名的配置

前面写到过Linux下tomcat服务器的部署,实际上只要域名正常解析到了服务器,那么不用绑定域名也是可以正常访问的,比如默认情况下访问xxx.net:8080与www.xxx.net:8080都可以正常访问,因为默认tomcat绑定位置是localhost,而带www的主机和不带www的主机都已经正常解析,所以访问是没有任何问题的,那么如果我们要确切的绑定一个域名怎么操作呢,方法如下: 使用vim打开conf/server.xml配置文件,找到<Host>节点,如下图: 这里的name属性值