1 tomcat是模块化的服务器,由两大模块:connector和container。
connector主要用来处理网络通讯,接收tcp请求,构造httprequest和httpresponse对象,然后传递给container。
容器的主要任务是触发Servlet.service(ServletRequest arg0, ServletResponse arg1),响应用户请求。
connector和container是解耦的,通过接口规范协作。
org.apache.catalina.Container是一个接口,按照范围从大到小,他的实现类有四个,这几个实现类的关系类似于组合模式的树结构。范围最大的是Engine,他表示整个catalina servlet引擎,他包含一个或者多个Host、Context ;Host表示一个虚拟主机,包含一个或多个Context(好比在一个tomacat服务器下部署多个应用);Context表示一个独立的ServletContext(也就是一个应用,对应的部署描述符是web.xml),他包含一个或多个Wrapper(当需要多个不同的servlet的时候,就会引用多个wrapper子容器)。wraper表示web应用中部署描述符定义的一个servlet,他控制这个servlet的生命周期,负责创建和销毁servlet。wraper不能再有子容器。要部署一个web应用,不一定需要所有的容器。
一般来说,一个connector会引用一个容器,这个容器可能是层级结构的。当接受到客户端请求的时候,connector会调用容器的invoke,容器引用一个pipeline,把请求传递给pipeline,、在pipeline里面顺序执行valve链。如果容器是层级结构的,那么父容器的某个valve会根据某种映射选择一个子容器,子容器执行和父容器类似的过程,请求会横向或者纵向的传递下去。例如,当context接受到一个请求时,他会调用自身的pipeline,执行StandardContextValve,这个value会根据规则找到合适的子容器wrapper,再由wrapper处理请求。
何时需要engine呢?当需要监控到整个engine的请求或者使用独立的connector却需要支持多个host的时候;
用host的时机类似。当和web server 如apache结合使用的时候,一般不需要engine或者host,因为连接器会利用web server的功能确定合适的context或者wrapper.
2 http 服务器实现的主体是Connector.start()(基于server socket),connector.CoyoteAdapter.service(Request req, Response res)内部实现http的处理逻辑。
3 官方网站的学习资料十分重要。不可不看
4
核心类图
从类图结构可以看出,tomcat具有良好的模块化结构,注重针对接口编程,类的设计具有充分的可扩展性。抽象和实现分离,在通信层面支持bio,nio,apr。
5 处理流程
7.0.27版本的http请求处理流程
Connector.initInternal()-》Connector.startInternal()
-》AbstractProtocol.start()-》AbstractEndpoint.start()此处采用了模板方法,AbstractEndpoint.startInternal()具体实现由子类定义 -》以http11为例子进行推演JIoEndpoint.startInternal()-》AbstractEndpoint.startAcceptorThreads启动接收器-》JIoEndpoint.Acceptor.run()-》当有连接请求进来时处理请求,JIoEndpoint.processSocket(Socket socket),以线程池的方式处理getExecutor().execute(new SocketProcessor(wrapper))-》JIoEndpoint.Handler.process(SocketWrapper<Socket> socket, SocketStatus status),此处handler的具体实现类对应Http11ConnectionHandler extends AbstractConnectionHandler-》
AbstractProtocol.AbstractConnectionHandler.process(SocketWrapper<S> socket, SocketStatus status)对请求进行处理-》AbstractHttp11Processor.process(SocketWrapper<S> socketWrapper)构造http请求和http响应,触发servlet的service-》CoyoteAdapter.service(Request req, Response res),CoyoteAdapter类作为请求处理器-》coyoteAdapter关联着一个org.apache.catalina.connector.Connector,由connector调用 connector.getService().getContainer().getPipeline().getFirst().invoke(request, response),委托容器处理请求(webx的pipeline借鉴此处)-》pipeline执行至StandardWrapperValve.invoke(Request request, Response response)-》ApplicationFilterChain.doFilter(ServletRequest request, ServletResponse response),在这个filter里面进入我们熟悉的Servlet.service(ServletRequest arg0, ServletResponse arg1)过程。
(小记,在分析代码过程中state = handler.process(socket,status);,直接在eclipse下面用ctrl+鼠标,没有直接显示出handler的实现类,而实际上是有实现类的,需要先点击到接口里面,在按F4)
6 tomcat的通讯层的实现主要在Endpoint,例如JIoEndpoint,在JIoEndpoint.Acceptor接受连接,拿到套接字后转交给线程池,由线程池执行SocketProcessor。
acceptor接受一个请求的处理逻辑
int errorDelay = 0;
// Loop until we receive a shutdown command
while (running) {
// Loop if endpoint is paused
while (paused && running) {
state = AcceptorState.PAUSED;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// Ignore
}
}
if (!running) {
break;
}
state = AcceptorState.RUNNING;
try {
//if we have reached max connections, wait
countUpOrAwaitConnection();
Socket socket = null;
try {
// Accept the next incoming connection from the server
// socket
socket = serverSocketFactory.acceptSocket(serverSocket);
} catch (IOException ioe) {
// Introduce delay if necessary
errorDelay = handleExceptionWithDelay(errorDelay);
// re-throw
throw ioe;
}
// Successful accept, reset the error delay
errorDelay = 0;
// Configure the socket
if (running && !paused && setSocketOptions(socket)) {
// Hand this socket off to an appropriate processor
if (!processSocket(socket)) {
// Close socket right away
closeSocket(socket);
}
} else {
// Close socket right away
closeSocket(socket);
}
} catch (IOException x) {
if (running) {
log.error(sm.getString("endpoint.accept.fail"), x);
}
} catch (NullPointerException npe) {
if (running) {
log.error(sm.getString("endpoint.accept.fail"), npe);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("endpoint.accept.fail"), t);
}
}
state = AcceptorState.ENDED;
// Process the request from this socket
try {
SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);
wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
// During shutdown, executor may be null - avoid NPE
if (!running) {
return false;
}
getExecutor().execute(new SocketProcessor(wrapper));
} catch (RejectedExecutionException x) {
log.warn("Socket processing request was rejected for:"+socket,x);
return false;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(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;
最后由协议处理器AbstractHttp11Processor.process(SocketWrapper<S> socketWrapper),去进行底层io操作,读取输入流构造http请求。
RequestInfo rp = request.getRequestProcessor();
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
// Setting up the I/O
setSocketWrapper(socketWrapper);
getInputBuffer().init(socketWrapper, endpoint);
getOutputBuffer().init(socketWrapper, endpoint);
Http协议的解码由http11.InternalInputBuffer实现
8 为了提供服务器性能,tomcat还提供基于java nio的NioEndpoint,还有基于APR(Apache Portable Runtime)技术,直接和操作系统交互,更好的集成一些现有的本地服务器技术。web 服务器通过ajp协议和应用服务器通信。
参考(jni http://baike.baidu.com/view/1272329.htm;
http://hi.baidu.com/ugo5/blog/item/96710efe34aa7e5cd7887de2.html
http://www.itpub.net/thread-1064918-1-1.html
http://guojuanjun.blog.51cto.com/277646/688559
http://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html)
9 tomcat使用自定义的classloader加载servlet,是出于安全考虑。如果采用系统类加载器统一加载,那么运行的恶意servlet就可以访问到classpath中所有的类库。而使用自定义class loader加载servlet类就可以做到代码访问的隔离,除非应用允许,否则一个类只能访问到同一命名空间的类(即同一个类加载器加载的class)。
另外一个原因是为了支持动态类加载,例如reload功能,支持热部署。可以感知到web lib下的class文件修改,自动重新加载,这是系统类加载器做不到的。
To specify certain rules in loading classes.
To cache the previously loaded classes.
To pre-load classes so they are ready to use.
tomcat控制一个webapp只能访问到它自己的WEB-INF/lib 下面的class,不能访问tomcat所运行的jvm下面的classpath(由系统类加载器加载的)。
它的默认class loader是WebappLoader,由下面步骤完成隔离加载和自动热部署的效果
Creating a class loader
Setting repositories
Setting the class path
Setting permissions
Starting a new thread for auto-reload.
10 servlet依据是否实现SingleThreadModel,分为单线程模型和多线程模型。实现SingleThreadModel(该模型的含义具有一定误导性,已经被废弃)的servlet的,容器会做同步,保证任意时刻只会有一个线程访问servlet(为了保证性能,容器会创建一个servlet实例池)。相反,另一种servlet的,容器将允许多线程访问,如果有需要,业务要自己做好同步。
由于servlet具有两种含义的线程,所以wrapper需要根据自己所表示的servlet的线程安全性,在分配servlet的时候做更多的处理逻辑。
StandardWrapper.allocate()
@Override
public Servlet allocate() throws ServletException {
// If we are currently unloading this servlet, throw an exception
if (unloading)
throw new ServletException
(sm.getString("standardWrapper.unloading", getName()));
boolean newInstance = false;
// If not SingleThreadedModel, return the same instance every time
if (!singleThreadModel) {
// Load and initialize our instance if necessary
if (instance == null) {
synchronized (this) {
if (instance == null) {
try {
if (log.isDebugEnabled())
log.debug("Allocating non-STM instance");
instance = loadServlet();
if (!singleThreadModel) {
// For non-STM, increment here to prevent a race
// condition with unload. Bug 43683, test case
// #3
newInstance = true;
countAllocated.incrementAndGet();
}
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException
(sm.getString("standardWrapper.allocate"), e);
}
}
}
}
if (!instanceInitialized) {
initServlet(instance);
}
if (singleThreadModel) {
if (newInstance) {
// Have to do this outside of the sync above to prevent a
// possible deadlock
synchronized (instancePool) {
instancePool.push(instance);
nInstances++;
}
}
} else {
if (log.isTraceEnabled())
log.trace(" Returning non-STM instance");
// For new instances, count will have been incremented at the
// time of creation
if (!newInstance) {
countAllocated.incrementAndGet();
}
return (instance);
}
}
synchronized (instancePool) {
while (countAllocated.get() >= nInstances) {
// Allocate a new instance if possible, or else wait
if (nInstances < maxInstances) {
try {
instancePool.push(loadServlet());
nInstances++;
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException
(sm.getString("standardWrapper.allocate"), e);
}
} else {
try {
instancePool.wait();
} catch (InterruptedException e) {
// Ignore
}
}
}
if (log.isTraceEnabled())
log.trace(" Returning allocated STM instance");
countAllocated.incrementAndGet();
return instancePool.pop();
}
}
11 tomcat的启动类位于stratup包下面,有Bootstrap创建一个Catalina实例,并调用Catalina的处理方法。之所以分为2个类,是因为在不同的系统下面,会有多个Bootstrap的实现。bin目录下面的shell程序就是用来启动tomcat.
12 StringManager,当tomcat发生异常行为时,需要记录错误信息。tomcat采用properties文件来维护错误文案,每个package下面都有自己的一个properties文件。当需要获取错误文案时,直接调用StringManager.getManager(String packageName)即可,这边使用了单例模式,每个package下面共享一个manager.
/**
* Get the StringManager for a particular package. If a manager for
* a package already exists, it will be reused, else a new
* StringManager will be created and returned.
*
* @param packageName The package name
*/
public static final synchronized StringManager getManager(String packageName) {
StringManager mgr = managers.get(packageName);
if (mgr == null) {
mgr = new StringManager(packageName);
managers.put(packageName, mgr);
}
return mgr;
}
来源:网络
主要介绍tomcat启动涉及到的一些接口和类。 目录 概述 tomcat包含的组件 server和service Lifecycle Container Connector 总结 概述 tomcat作为一个服务器,它的主要功能就是接收请求——处理请求——返回,如果是我们自己实现一个最简单的服务器,启动一个线程监听某个端口,该端口有数据进来的话就接收数...
目录 Http11ConnectionHandler Http11Processor CoyoteAdapter StandardEngineValve StandardWrapperValve 总结 Http11ConnectionHandler 在tomcat 启动之后会使用socket.accept接收请求,接收到之后会调用自己的processSocket来处理请求,在该方法中启动一个Soc...
Container概述 (1)主要包含四种:Engine:整个catalina servlet容器, Host:一个虚拟主机包含多个context容器, Context:一个web应用容器,包含一道多个Wrapper容器, Wrapper:独立servlet容器 (2)UML图 (3)添加/移除/查找子容器: container.addChild(container) -> containe...
Connetor的业务流程图: StringManager: (1)用于给tomcat管理各种error message。不同语言包的error Message存储在如:LocalStrings.properties, 日文的文件如:LocalStrings_jp.properties (2)StringManager是包内共享,全局使用Singleton pattern将所有key-value(...
1. Logger接口 (1)Code: (2)接口包含五个日志层次: FATAL>ERROR>WARNING>INFORMATION>DEBUG (1)主要包含三个类: FileLogger, SystemErrLogger, SystemOutLogger。它们都继承自LoggerBase. UML图: (2)LoggerBase抽象类: 代码: (3)SystemOu...
一、Server 1. 总体架构位置 2. Server的设计目的 Server提供了优雅的启动停止所有内部组件的功能,有了server,无需再单独启动connector和Container 3. Server具体内部功能 ser...
1. Tomcat中的JMX概述 (1)概述 对于一个可以被管理的Java对象,我们需要创建另一个对象MBean或Managed Bean。org.apache.catalina.mbeans包中有:ConnectorMBean, StandardEngineMBean, StandardHostMBean, StandardCon...
第七章、日志 一、前言部分 A Logger是一个记录消息的组件。在Catalina中的一个logger是与一个Container关联,该组件和其他组件相比是相对比较简单。Tomcat在org.apache.catalina.logger包中提供不同类型重做日志功能。在本章的应用程序将会在ex07.pyrmont包中找到,这两个类( SimpleContext,Bootstrap)发...
Shutdown Hook 一、概述 1. JVM会响应关闭自己的两种Event (1)应用程序调用System.exit方法或最后一个非守护进程non-daemon退出 (2)用户在关java程序之前,突然强制关机,比如CTRL+C或者注销系统 ...
1 javax.servlet.Servlet 接口 (1)接口包含五个方法: Public void init(ServletCongig config) Public void service(ServletRequest request, ServletResponse response) Public void destory() Public void ServletConfig get...
I'm currently trying out the google cloud messaging service with its sample application "Guestbook." https://developers.google.com/cloud/samples/mbs/ I'm attempting to send notifications tha...
Now I came across an article that distinguishes between an Asynchronous function and Synchronous functions. From my understanding of the different examples and explanations, synchronous functions are ...
Good day all I'm busy creating a small costing calculator for the signage department. I'm not getting the calculator to output the amount. Brief Description: You enter the height and width and then wh...
I have 3 models created with Flask-SQLalchemy: User, Role, UserRole role.py: user.py: user_role.py: If I try (in the console) to get all users via User.query.all() I get AttributeError: 'NoneType' obj...
I have many particles that follow an stochastic process in parallel. For each particle, there is a PRNG associated to it. The simulation must go through many repetitions to get average results. For ea...