`
ganqing1234
  • 浏览: 171659 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

《how tomcat works》读书笔记

 
阅读更多

一 简单web服务

客户端socket和服务端socket。

客户端socket:

Socket socket = new Socket("127.0.0.1", "8080");

socket有输入和输出流 socket.getInputstream(), socket.getOutputStream()

服务端socket:

监听服务端口,收到客户端socket请求,会创建一个socket。

serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));

socket = serverSocket.accept(); 这个socket和客户端的对应,它的输出端对应客户端输入端,输入端对于客户端的输出端。

操作系统管理一个先进先出的队列(一般大小为50)来存放客户端请求,超过限制就拒绝连接。每个request对应一个socket,

所以加载一个页面可能需要多次连接socket请求,比如图片等会有很多请求,可以使用http1.1协议的长连接新特性来避免多次连接请求。

在同一个线程里面serverSocket.accept()只有等待前一个socket处理完成之后,才能出来队列里面的下一个socket请求。

可以使用多线程提交效率,参见第四章HttpProcessor内容。

Http 1.1 新功能(书里面第四章,笔记里放在第一章)

Persistent Connections 长连接

不开启长连接时,请求一个page,里面的每个资源(图片、applets等)都需要建一个request,很耗资源,

开启长连接后,服务器可以不关闭连接,而等待一个page的所有内容加载完后才关闭,这样一个page和它引用的

相关资源可以共用一个连接。

request header 添加 connection: keep-alive即可。主流服务器软件都支持,可以设置长连接超时时间,apache

默认开启长连接功能,超时时间默认是5秒

Chunked Encoding

表明response消息体的长度,如:

9\r\n 

p a tree. 

0\r\n

100 (Continue) 状态

客户端先发送一个request 头包含:Expect:100-Continue,表明接下来需要发送一个内容很长的request,等待服务端

回应,是否接受,接受就发送,否则就不发了。

如果response返回100表示接受,HTTP/1.1 100 Continue。

二 简单servlet容器

servlet容器用于处理用户的请求,可以是静态的或者动态内容。

servlet容器需要实现javax.servlet.Servlet接口,3个方法:

// 类实例化后调用,只做一次

public void init(ServletConfig config) throws ServletException 

// 服务方法,每次处理都调用

public void service(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException 

// 容器关闭时调用,清理一些内容

public void destroy() 

request一般分为获取静态文件或者动态内容,如果是静态文件就从web——root目录下去读取文件写入response返回。

动态内容则利用URLClassLoader加载相关servlet进行处理。具体代码参见ex02.pyrmont.ServletProcessor1;

三 连接器connector

Catalina主要的两个模块:container和connector。连接器主要是用于提供HttpServletRequest和HttpServletResponse实例。

connector解析http请求获取header,cookies和参数。最后拼装response返回。

StringManager 类: 用于错误信息。需要支持国际化。本身是单例模式。

每个package下面的都会存放这个包下类需要的错误信息,所以这么实现:

private static Hashtable managers = new Hashtable();

public synchronized static StringManager getManager(String packageName) {

StringManager mgr = (StringManager) managers.get(packageName);

if (mgr == null) {

mgr = new StringManager(packageName);

managers.put(packageName, mgr);

}

return mgr;

}

构造函数是private保证单例,使用ResourceBundle支持国际化。

private ResourceBundle bundle;

private StringManager(String packageName) {

String bundleName = packageName + ".LocalStrings";

bundle = ResourceBundle.getBundle(bundleName);

}

错误消息体的拼装使用的是java.text.MessageFormat。

HttpConnector:实现Runnable接口。在run方法里面:

serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));

socket = serverSocket.accept();   // 等待一个请求

// Hand this socket off to an HttpProcessor 

HttpProcessor processor = new HttpProcessor(this);  // 生成一个HttpProcessor处理请求

processor.process(socket);                          

connetor支持多线程,调用它的start方法就好:

public void start() {

Thread thread = new Thread(this);

thread.start();

}

HttpProcessor: 对于每个请求,processor做下面的工作:

创建一个HttpRequest对象.

创建一个HttpResponse对象.

解析http request的第一行(请求方法、资源),消息头header,然后填充HttpRequest对象,消息体在servlet service时

才解析,因为只在使用时解析,提高效率。

分静态资源StaticResourceProcessor和动态内容ServletProcessor分别处理。

public void process(Socket socket)

// SocketInputStream是对inputStream的封装。主要有两个方法readRequestLine和readHeader,这部分代码挺复杂,可以细细研究

SocketInputStream input = new SocketInputStream(socket.getInputStream(), 2048); 

parseRequest(input, output); // 解析命令

parseHeaders(input);         // 解析头信息

// 最后分静态和动态内容进行处理

if (request.getRequestURI().startsWith("/servlet/")) {

ServletProcessor processor = new ServletProcessor();

processor.process(request, response);

} else {

StaticResourceProcessor processor = new StaticResourceProcessor();

processor.process(request, response);

}

HttpRequest: 继承javax.servlet.http.HttpServletRequest

主要用于存放请求消息的header,cookies,request uri,消息体等。存放的结构各不相同:

protected HashMap headers = new HashMap(); 

protected ArrayList cookies = new ArrayList(); 

protected ParameterMap parameters = null;

解析和填充HttpRequest对象是个复杂的工作,可以分为下面几个步骤:

Reading the socket's input stream

Parsing the request line

Parsing headers

Parsing cookies

Obtaining parameters

 

四 Tomcat默认Connector

Connector被设计成一个模块,目前有多个实现(Coyote, mod_jk, mod_jk2, and mod_webapp),必须满足:

1. It must implement the org.apache.catalina.Connector interface.

2. It must create request objects whose class implements the org.apache.catalina.Request interface.

3. It must create response objects whose class implements the org.apache.catalina.Response interface.

HttpConnector: 一个Tomcat connector里面的主要方法为:getContainer,createRequest,createResponse

HttpConnector同时实现了Lifecycle这个接口,主要实现initialize 和 start方法。

initialize里面主要创建一个server socket,采用工厂模式(ServerSocketFactory->DefaultServerSocketFactory)。

start里面则是启动一个线程等待请求并填充HttpProcessor实例

// Start our background thread

threadStart(); // 里面主要起个Daemon线程,里面socket = serverSocket.accept(); processor.assign(socket);

 

// Create the specified minimum number of processors

while (curProcessors < minProcessors) {

if ((maxProcessors > 0) && (curProcessors >= maxProcessors))

break;

HttpProcessor processor = newProcessor();

recycle(processor);

}

HttpConnector用一个栈维护 HttpProcessor 实例:private Stack processors = new Stack(); 这样重复利用,提高效率。

最开始初始化最少(5个)processors,不够就增加,直到最大值(20个).如果请求超过maxProcessors(20),就会被忽略,把maxProcessors设置为负值

则不限制。

HttpProcessor 相对于前一章节的HttpProcessor,它实现了Runnable接口,支持多线程。多线程没写过,这里是重点!!!

public void run() { 

// Process requests until we receive a shutdown signal 

while (!stopped) { 

//先获取一个socket

Socket socket = await(); 

if (socket == null) continue; 

// process处理

try { 

process(socket);  

} catch (Throwable t) { 

log("process.invoke", t); 

// 把processor归还pool

connector.recycle(this); 

// Tell threadStop() we have shut ourselves down successfully

synchronized (threadSync) { 

threadSync.notifyAll(); 

}

这里await()方法控制processor线程只在获取到socket后才能执行后面的操作。

而processor线程在HttpConnector的newProcessor方法里面生成时,线程就已经开始执行了

private HttpProcessor newProcessor() {

HttpProcessor processor = new HttpProcessor(this, curProcessors++);

if (processor instanceof Lifecycle) {

try {

((Lifecycle) processor).start();

} catch (LifecycleException e) {

log("newProcessor", e);

return (null);

}

}

created.addElement(processor);

return (processor);

}

HttpConnector线程里面调用assign后,await才能得到socket。

assign和await是在不同的线程里面执行的,assign是由HttpConnector线程run方法调用的,而await由HttpProcessor线程run方法调用。

它们之间的交流是靠HttpProcessor线程变量available和Object wait和notifyAll方法进行的。

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(); 

if ((debug >= 1) && (socket != null)) 

log(" The incoming request has been awaited"); 

return (socket); 

}

线程对比

The processor thread (the await method)            The connector thread (the assign method)

while (!available) {   while (available) {

wait(); wait();

}                                                  }

Socket socket = this.socket;                       this.socket = socket;

available = false;                                 available = true;

notifyAll();                                       notifyAll();

return socket;                                     ...

processor线程启动后available初始化为false,所以processor线程就卡在wait(); 直到其他线程调用notify或者notifyAll.

(线程内容补充,这里面HttpProcessor实现了Runnable,生成线程时先要实例化一个HttpProcessor对象,然后用new Thread(processor)来

创建线程,根据前面newProcessor代码来看,本章的程序里面一个HttpProcessor实例就对应一个HttpProcessor线程而不是多个。)

process方法:真正处理请求的方法,主要:

parse the connection

parse the request

parse headers

程序里面一个特点是ok的使用,用ok来控制最终的response(处理时发生异常错误仍然需要返回,只是返回错误信息)。

解析请求中为提高效率,在Chapter3里面的header字段比较是直接的String比较,本章节都改为了字符数组比较。

if (name.equals("cookie"))              if (header.equals(DefaultHeaders.COOKIE_NAME))

// 字符串直接比较                       //  DefaultHeaders.COOKIE_NAME是定义好的数组,equals被重写了比较字符数组

这个我试了下在jdk1.6下面,毫秒级都差不多。可能jdk本身做了优化,书是tomcat4的,估计jdk的版本也不高。

 

五 容器Container

容器的作用就是处理请求,填充response返回给客户端。容器需要实现org.apache.catalina.Container接口,Tomcat一共有4种容器:

Engine, Host, Context, and Wrapper。本章只讨论Context和Wrapper。

Engine. Represents the entire Catalina servlet engine.

Host. Represents a virtual host with a number of contexts.

Context. Represents a web application. A context contains one or more wrappers.

Wrapper. Represents an individual servlet.

Wrapper是最底层的实现,不能再包含任务子容器了,其他的容器都可以有0或者多个子容器:

public void addChild(Container child);

public void removeChild(Container child);

public Container findChild(String name); 

public Container[] findChildren();

容器里面还可以包含其他的支持组件如:Loader, Logger, Manager, Realm, and Resources。

Pipelining Tasks

pipeline(管道)和valve(阀门),它们类似于servlet的filter chain和filter概念。valve处理一个request和response,完成后

将request、response传给pipeline上的下一个valve,pipeline必须有个basicValve。

一个container有一个pipeline,当connector调用container的invoke方法处理request时,容器交给管道,然后pipeline上的阀门一个个处理。

实现方式上,不是采用:

for (int n=0; n<valves.length; n++) { valve[n].invoke( ... ); } // then, invoke the basic valve basicValve.invoke( ... );

这种方法。方式如下:

ContainerBase 里面的invoke如下:

public void invoke(Request request, Response response) throws IOException, ServletException { 

pipeline.invoke(request, response); 

}

而pipeline里面创建一个实现ValveContext接口的内部类(内部类可以访问pipeline的所有内容),内部类主要实现

public void invokeNext(Request request, Response response)这个方法。

pipeline内部ValveContext实现:

protected class StandardPipelineValveContext implements ValveContext { 

protected int stage = 0; 

public String getInfo() { return info; } 

public void invokeNext(Request request, Response response) throws IOException, ServletException { 

int subscript = stage; stage = stage + 1;

// Invoke the requested Valve for the current request thread 

if (subscript < valves.length) { 

valves[subscript].invoke(request, response, this); 

} else if ((subscript == valves.length) && (basic != null)) { 

basic.invoke(request, response, this);  // 最后执行默认的valve

} else { 

throw new ServletException (sm.getString("standardPipeline.noValve")); 

}

valveContext对象作为valve的invoke方法的参数,这样每个valve也都持有ValveContext对象,先由pipeline调用ValveContext的invokeNext调用第一个valve,

valve处理后再调用ValveContext对象的invokeNext传递给下一个valve.

以ClientIPLoggerValve的invoke方法为例:

public void invoke(Request request, Response response,

ValveContext valveContext) throws IOException, ServletException {

// Pass this request on to the next valve in our pipeline

valveContext.invokeNext(request, response);

System.out.println("Client IP Logger Valve");

ServletRequest sreq = request.getRequest();

System.out.println(sreq.getRemoteAddr());

System.out.println("------------------------------------");

}

先调用valveContext的invokeNext,然后才处理下日志,类似递归的过程(和servlet的filter机制类似)。这种设计的优势在于在调用valveContext.invokeNext(request, response);

前面和后面都可以添加代码,起到“包裹”的作用,简单的循环依次调用是做不到这点的.

The Contained Interface

valve可以实现这个接口,主要是为了和container关联。看它的定义就知道了:

public interface Contained { 

public Container getContainer(); 

public void setContainer(Container container);

The Wrapper Interface

wapper是最底层的container,代表一个servlet,接口继承了Container接口另外新增了一些方法。它主要是处理servlet class的生命周期,

如call ini、service、destroy方法等。

wapper里面两个重要的方法是allocate and load。allocate用于分配一个初始化好的servlet实例,load加载和初始化servlet实例。

The Context Interface

代表一个web application的Container,一般都有一个或者多个Wrapper作为子container,重要的方法有addWrapper, createWrapper等.

The Wrapper Application

这个应用包含的类:

SimpleWrapper、SimplePipeline、SimpeLoader、SimpleWrapperValve(basic value)、ClientIPLoggerValve、HeaderLoggerValve

相比较第四章,加载servlet在httpprocessor里面,这边加载servlet都放到SimpeLoader里面操作了。

SimpeLoadr: 构造函数返回一个类加载器给SimpleWrapper

public SimpleLoader() {

try {

URL[] urls = new URL[1];

URLStreamHandler streamHandler = null;

File classPath = new File(WEB_ROOT);

String repository = (new URL("file", null,

classPath.getCanonicalPath() + File.separator)).toString();

urls[0] = new URL(null, repository, streamHandler);

classLoader = new URLClassLoader(urls);

} catch (IOException e) {

System.out.println(e.toString());

}

}

SimplePipeline:这个参考Pipelining Tasks描述

SimpleWrapper: 

里面定义了loader和父container变量:

private Loader loader; 

protected Container parent = null;

获取loader时先从自己的类里面取,没有就从父container里面取:

public Loader getLoader() { 

if (loader != null) return (loader); 

if (parent != null) return (parent.getLoader()); 

return (null); 

}

在构造函数里面设置默认的valve:

public SimpleWrapper() {

pipeline.setBasic(new SimpleWrapperValve()); 

}

wrapper里面的allocate and load方法:

load用于加载和初始化servlet:

public void load() throws ServletException {

instance = loadServlet();

}

而loadServlet()代码如下:

private Servlet loadServlet() throws ServletException {

if (instance != null)

return instance;

Servlet servlet = null;

String actualClass = servletClass;

if (actualClass == null) {

throw new ServletException("servlet class has not been specified");

}

Loader loader = getLoader();

// Acquire an instance of the class loader to be used

if (loader == null) {

throw new ServletException("No loader.");

}

ClassLoader classLoader = loader.getClassLoader();

 

// Load the specified servlet class from the appropriate class loader

Class classClass = null;

try {

if (classLoader != null) {

classClass = classLoader.loadClass(actualClass);

}

} catch (ClassNotFoundException e) {

throw new ServletException("Servlet class not found");

}

// Instantiate and initialize an instance of the servlet class itself

try {

servlet = (Servlet) classClass.newInstance();

} catch (Throwable e) {

throw new ServletException("Failed to instantiate servlet");

}

 

// Call the initialization method of this servlet

try {

servlet.init(null);  // 初始化servlet

} catch (Throwable f) {

throw new ServletException("Failed initialize servlet.");

}

return servlet;

}

allocate则用于获取已经实例化的servlet

public Servlet allocate() throws ServletException {

// Load and initialize our instance if necessary

if (instance == null) {

try {

instance = loadServlet();

} catch (ServletException e) {

throw e;

} catch (Throwable e) {

throw new ServletException(

"Cannot allocate a servlet instance", e);

}

}

return instance;

}

SimpleWrapperValve

那么默认的valve做什么呢:

public void invoke(Request request, Response response,

ValveContext valveContext) throws IOException, ServletException {

SimpleWrapper wrapper = (SimpleWrapper) getContainer();

ServletRequest sreq = request.getRequest();

ServletResponse sres = response.getResponse();

Servlet servlet = null;

HttpServletRequest hreq = null;

if (sreq instanceof HttpServletRequest)

hreq = (HttpServletRequest) sreq;

HttpServletResponse hres = null;

if (sres instanceof HttpServletResponse)

hres = (HttpServletResponse) sres;

// Allocate a servlet instance to process this request

try {

servlet = wrapper.allocate();  // wrapper获取已经实例化完成的servlet,然后调用servlet的service处理请求返回response

if (hres != null && hreq != null) {

servlet.service(hreq, hres);

} else {

servlet.service(sreq, sres);

}

} catch (ServletException e) {

}

}

ClientIPLoggerValve、HeaderLoggerValve做些酱油操作。

启动服务:

HttpConnector connector = new HttpConnector();

Wrapper wrapper = new SimpleWrapper();

wrapper.setServletClass("ModernServlet");  // 指明servlet名称

Loader loader = new SimpleLoader();

Valve valve1 = new HeaderLoggerValve();

Valve valve2 = new ClientIPLoggerValve();

 

wrapper.setLoader(loader); // 设置类加载器

((Pipeline) wrapper).addValve(valve1);  // 设置额外的valve

((Pipeline) wrapper).addValve(valve2);

 

connector.setContainer(wrapper);  // 与Connector关联,wrapper作为connector的容器

 

try {

connector.initialize();

connector.start();

The Context Application

之前的只是一个wrapper不算一个完整的服务器,只能处理一个servlet,Context里面使用mapper接口保存子container(wrapper,Tomcat5不再使用mapper).

Mapper:

package org.apache.catalina; 

public interface Mapper {

public Container getContainer(); 

public void setContainer(Container container); 

public String getProtocol(); 

public void setProtocol(String protocol); 

public Container map(Request request, boolean update); // 获取处理request的子container(这里是wrapper)

}

启动服务:

HttpConnector connector = new HttpConnector();

Wrapper wrapper1 = new SimpleWrapper();  // 生成两个wrapper

wrapper1.setName("Primitive");

wrapper1.setServletClass("PrimitiveServlet");

Wrapper wrapper2 = new SimpleWrapper();

wrapper2.setName("Modern");

wrapper2.setServletClass("ModernServlet");

 

Context context = new SimpleContext(); // 生成context并包含wrapper

context.addChild(wrapper1);

context.addChild(wrapper2);

 

Valve valve1 = new HeaderLoggerValve();

Valve valve2 = new ClientIPLoggerValve();

 

((Pipeline) context).addValve(valve1);  // 添加valve

((Pipeline) context).addValve(valve2);

 

Mapper mapper = new SimpleContextMapper(); // mapper

mapper.setProtocol("http");

context.addMapper(mapper);

Loader loader = new SimpleLoader();

context.setLoader(loader);   //类loader交给context关联,wrapper也能用

context.addServletMapping("/Primitive", "Primitive");  // mapper设置对应关系,处理时取相应的wrapper处理请求

context.addServletMapping("/Modern", "Modern");        // 应相对uri和wrapper名称建关联 

connector.setContainer(context);    // 与Connector关联,context作为connector的容器

try {

connector.initialize();

connector.start();

处理过程是这样的:

connector调用container(Context)的invoke处理请求,Context invoke里面调用Context的SimplePipeline 的invoke来处理:

public void invoke(Request request, Response response) throws IOException, ServletException {

pipeline.invoke(request, response);

}

pipeline自然是先调用辅助的valve,最后调用basic valve(pipeline.setBasic(new SimpleContextValve()); 就是SimpleContextValve)处理,

Wrapper wrapper = null;

try {

wrapper = (Wrapper) context.map(request, true);  // 根据request从mapper获取对应的wrapper

} catch (IllegalArgumentException e) {

badRequest(requestURI, (HttpServletResponse) response.getResponse());

return;

}

if (wrapper == null) {

notFound(requestURI, (HttpServletResponse) response.getResponse());

return;

}

// Ask this Wrapper to process this Request

response.setContext(context);

wrapper.invoke(request, response);  // wrapper最终处理(wrapper里面也是有pipeline的,本例里面没有添加酱油valve

所以直接就到basic valve调用servlet service处理请求)

最后看看mapper是如何找到request和wrapper的对应关系的:

public Container map(Request request, boolean update) {

// Identify the context-relative URI to be mapped

String contextPath = ((HttpServletRequest) request.getRequest()).getContextPath();

String requestURI = ((HttpRequest) request).getDecodedRequestURI();

String relativeURI = requestURI.substring(contextPath.length());

// Apply the standard request URI mapping rules from the specification

Wrapper wrapper = null;

String servletPath = relativeURI;

String pathInfo = null;

String name = context.findServletMapping(relativeURI);  // 利用相对uri找到wrapper的名字

if (name != null)

wrapper = (Wrapper) context.findChild(name);

return (wrapper);

}

六 Lifecycle 生命周期

Catalina有很多组件构成,Catalina启动时他们也需要启动,Catalina停止时,组件需要释放资源等。

实现Lifecycle接口的组件可以下面的事件:

BEFORE_START_EVENT, START_EVENT, AFTER_START_EVENT, BEFORE_STOP_EVENT, STOP_EVENT, and AFTER_STOP_EVENT

有事件就有监听器接口:org.apache.catalina.LifecycleListener

The Lifecycle Interface

public interface Lifecycle { 

public static final String START_EVENT = "start"; 

public static final String BEFORE_START_EVENT = "before_start"; 

public static final String AFTER_START_EVENT = "after_start"; 

public static final String STOP_EVENT = "stop"; 

public static final String BEFORE_STOP_EVENT = "before_stop"; 

public static final String AFTER_STOP_EVENT = "after_stop"; 

public void addLifecycleListener(LifecycleListener listener); 

public LifecycleListener[] findLifecycleListeners(); 

public void removeLifecycleListener(LifecycleListener listener); 

public void start() throws LifecycleException; 

public void stop() throws LifecycleException; 

}

The LifecycleEvent Class

它继承了EventObject,本身持有lifecycle变量.

The LifecycleListener Interface

他只有一个方法:public void lifecycleEvent(LifecycleEvent event); 当一个事件触发时,对该事件感兴趣的listener执行这个方法.

The LifecycleSupport Class

这个是个Lifecycle辅助类,有事件有监听器就得注册监听器和事件关系吧,这个类就能帮忙了。

public LifecycleSupport(Lifecycle lifecycle) { // 构造函数 持有一个 lifecycle对象

super();

this.lifecycle = lifecycle;

}

然后实现了Lifecycle里面的addLifecycleListener,findLifecycleListeners,removeLifecycleListener三个方法。

额外有个触发事件的方法:

public void fireLifecycleEvent(String type, Object data) {

LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);

LifecycleListener interested[] = null;

synchronized (listeners) {

interested = (LifecycleListener[]) listeners.clone(); // 拷贝可能是怕listeners数组有新增或者减少会引起异常

}

for (int i = 0; i < interested.length; i++)

interested[i].lifecycleEvent(event);  // 对这个事件感兴趣的监听都执行lifecycleEvent

}

下面看看Lifecycle机制具体的使用:

参考第五章SimpleContext、SimpleWrapper、SimplePipeline、SimpleLoader、SimpleContextMapper都实现了Lifecycle.

SimpleContext: 代码和第5章类似

protected LifecycleSupport lifecycle = new LifecycleSupport(this);

然后addLifecycleListener什么的就不说了。

关键是start()和stop()方法:

public synchronized void start() throws LifecycleException {

if (started) // 是个变量用于标识对象是否启动了

throw new LifecycleException("SimpleContext has already started");

 

lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); // 触发“启动之前”事件

started = true;

try { // context下面相关的组件也都启动下

if ((loader != null) && (loader instanceof Lifecycle)) // loader组件

((Lifecycle) loader).start();

Container children[] = findChildren();            // 所有的wrapper

for (int i = 0; i < children.length; i++) {

if (children[i] instanceof Lifecycle)

((Lifecycle) children[i]).start();

}

if (pipeline instanceof Lifecycle)         // 启动所有valve包括 basic valve  

((Lifecycle) pipeline).start();

lifecycle.fireLifecycleEvent(START_EVENT, null); // 触发“启动”事件

} catch (Exception e) {

e.printStackTrace();

}

lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); // 触发“启动完成”事件

}

public void stop() throws LifecycleException {  // 和start类似,需要停止context下面相关的组件

if (!started)

throw new LifecycleException("SimpleContext has not been started");

// Notify our interested LifecycleListeners

lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);

lifecycle.fireLifecycleEvent(STOP_EVENT, null);

started = false;

try {

// Stop the Valves in our pipeline (including the basic), if any

if (pipeline instanceof Lifecycle) {

((Lifecycle) pipeline).stop();

}

// Stop our child containers, if any

Container children[] = findChildren();

for (int i = 0; i < children.length; i++) {

if (children[i] instanceof Lifecycle)

((Lifecycle) children[i]).stop();

}

if ((loader != null) && (loader instanceof Lifecycle)) {

((Lifecycle) loader).stop();

}

} catch (Exception e) {

e.printStackTrace();

}

// Notify our interested LifecycleListeners

lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);

}

可以看如果需要启动或者停止所有组件只需要启动和停止组件上层的container,本例里面是Context.

代码里面有触发事件,当然就得有监听器了。如SimpleContextLifecycleListener

public void lifecycleEvent(LifecycleEvent event) {

Lifecycle lifecycle = event.getLifecycle();

System.out.println("SimpleContextLifecycleListener's event "

+ event.getType().toString());

if (Lifecycle.START_EVENT.equals(event.getType())) {

System.out.println("Starting context.");

} else if (Lifecycle.STOP_EVENT.equals(event.getType())) {

System.out.println("Stopping context.");

}

}

本例中写的比较简单,就是打印一些信息.

总结下Lifecycle机制就是为了提供event和eventListener,这样便于不影响流程的情况下新增监听器添加新功能.

 

七 Logger

先看看org.apache.catalina.Logger接口的定义:

public static final int FATAL = Integer.MIN_VALUE;  // 5种verbosity日志级别

public static final int ERROR = 1;

public static final int WARNING = 2;

public static final int INFORMATION = 3;

public static final int DEBUG = 4;

public Container getContainer();

public void setContainer(Container container);

public String getInfo();

public int getVerbosity();

public void setVerbosity(int verbosity);

public void addPropertyChangeListener(PropertyChangeListener listener);

public void log(String message);

public void log(Exception exception, String msg);

public void log(String message, Throwable throwable);

public void log(String message, int verbosity);

public void log(String message, Throwable throwable, int verbosity);

public void removePropertyChangeListener(PropertyChangeListener listener);

在实现上tomcat提供了三种实现FileLogger、SystemErrLogger、SystemOutLogger这三种又有很多同样的代码,所以提供了一个父类:LoggerBase

LoggerBase: 是个抽象类

设置日志级别的方法:默认是ERROR(protected int verbosity = ERROR;)

public void setVerbosityLevel(String verbosity) {

if ("FATAL".equalsIgnoreCase(verbosity))

this.verbosity = FATAL;

else if ("ERROR".equalsIgnoreCase(verbosity))

this.verbosity = ERROR;

else if ("WARNING".equalsIgnoreCase(verbosity))

this.verbosity = WARNING;

else if ("INFORMATION".equalsIgnoreCase(verbosity))

this.verbosity = INFORMATION;

else if ("DEBUG".equalsIgnoreCase(verbosity))

this.verbosity = DEBUG;

}

log方法里面只有public abstract void log(String msg);是没有实现,由子类实现,其他的都提供了实现类.

public void log(String message, int verbosity) {

if (this.verbosity >= verbosity)

log(message); // 具体的实现由子类完成

}

The SystemOutLogger Class

看看的它的实现:

public class SystemOutLogger extends LoggerBase {

protected static final String info = "org.apache.catalina.logger.SystemOutLogger/1.0";

public void log(String msg) {

System.out.println(msg);

}

}

SystemErrLogger class: System.err.println(msg); 

The FileLogger Class

FileLogger实现了Lifecycle接口,可以和容器中的其他组件一起根据生命周期启动,停止。

start方法不看了。stop里面关闭输出流:

public void stop() throws LifecycleException {

// Validate and update our current component state

if (!started)

throw new LifecycleException(sm.getString("fileLogger.notStarted"));

lifecycle.fireLifecycleEvent(STOP_EVENT, null);

started = false;

close(); // 关闭文件输出

}

log方法,看来是以日期分割日志文件的,每天一个日志文件,open的时候用了锁,防止生成多个日志文件.

public void log(String msg) {

// Construct the timestamp we will use, if requested

Timestamp ts = new Timestamp(System.currentTimeMillis());

String tsString = ts.toString().substring(0, 19);

String tsDate = tsString.substring(0, 10);

 

// If the date has changed, switch log files

if (!date.equals(tsDate)) {

synchronized (this) {

if (!date.equals(tsDate)) {

close();

date = tsDate;

open();

}

}

}

// Log this message, timestamped if necessary

if (writer != null) {

if (timestamp) {

writer.println(tsString + " " + msg);

} else {

writer.println(msg);

}

}

}

open 用于打开文件:

private void open() {

// Create the directory if necessary

File dir = new File(directory);

if (!dir.isAbsolute())

dir = new File(System.getProperty("catalina.base"), directory);

dir.mkdirs();

// Open the current log file

try {

String pathname = dir.getAbsolutePath() + File.separator + prefix

+ date + suffix;

writer = new PrintWriter(new FileWriter(pathname, true), true);

} catch (IOException e) {

writer = null;

}

}

八 Loader

前面的章节提到过SimpleLoader,用于加载和初始化servlet实例,之前用的system loader,这样不安全,需要自己定制一个loader,防止不良企图的servlet.

一个servlet只能有权限访问/WEB-INF/classes下面的类和/WEB-INF/lib下面的jar里面的类.而且定制的loader可以检查类是否有修改,如果有可以重新加载,这个

就是热部署。

java有三种类加载器:bootstrap class loader, extension class loader, and system class loader, 他们之间存在父子关系。

bootstrap 处于最高级,system处于最低级。

bootstrap class loader用于引导jvm,它加载的是java核心的类(java.lang或者java.io packages),所以不是用java语言实现的(C语言).

extension class loader 各个jvm实现的不一样,sun的主要加载 /jdk/jre/lib/ext 里面的类或者jar。

system class loader 则是从CLASSPATH里面定义的目录中加载类或者jar.

一个类加载时,先交给system class loader,但是它不加载,交给它的父loader(extension class loader)加载,extension class loader同理也先交给它的父loader,

也就是bootstrap class loader来加载。如果bootstrap class loader能找到类就加载,找不到就让它的子loader加载,就这样。。。如果最底层的loader也加载不了就报

ClassNotFoundException. 这种机制主要是为了安全性,防止用户用非法的类替换标准的类。

先看看Loader接口:

public interface Loader {

public ClassLoader getClassLoader();

public Container getContainer();

public void setContainer(Container container);

public DefaultContext getDefaultContext();

public void setDefaultContext(DefaultContext defaultContext);

public boolean getDelegate();

public void setDelegate(boolean delegate);

public String getInfo();

public boolean getReloadable();

public void setReloadable(boolean reloadable);

public void addPropertyChangeListener(PropertyChangeListener listener);

public void addRepository(String repository);

public String[] findRepositories();

public boolean modified();

public void removePropertyChangeListener(PropertyChangeListener listener);

}

整体结构如下:

Loader                  loader.Reloader          java.net.URLClassLoader

  |                          |__________________________|

loader.WebappLoader                      |

       |_______use_________________loader.WebappClassLoader

分享到:
评论

相关推荐

    How Tomcat Works 读书笔记(第三章)

    《How Tomcat Works》这本书是理解Apache Tomcat服务器工作原理的重要资源,第三章主要探讨了Tomcat的架构和核心组件。以下是对这部分内容的详细解读: Tomcat作为一款开源的Java Servlet容器,其核心功能是解析...

    java版五子棋源码-HowTomcatWorks:HowTomcatWorks读书笔记

    java版五子棋源码HowTomcatWorks 《How Tomcat Works》 every chapter demo . Here's my ebook: Part of the UML diagram is as follows. 1.The default connector class diagram: 2.The class diagram of ...

    How-Tomcat-Works-中文版笔记

    《How Tomcat Works》中文版笔记详细介绍了Apache Tomcat的内部架构和运行机制,Tomcat是一个广泛使用的Java开源Web服务器和Servlet容器,它为处理基于Java的Web应用提供平台。本书不仅深入分析了Tomcat的核心组件和...

    学习tomcat源码+英文《How Tomcat Work》和每一章的相关项目+tomcat6源码依赖jar

    在深入探讨Tomcat源码之前,我们先了解一下Tomcat是什么。Tomcat是一款开源的Java Servlet容器,由...通过阅读《How Tomcat Works》并结合实际项目实践,你将能够逐步揭开Tomcat的神秘面纱,掌握这个强大工具的精髓。

    HowTomcatWorks:《深度剖析Tomcat》原始码及笔记

    《深入剖析Tomcat》原始码及笔记 -Tomcat的工作原理开发自己的Java Servlet容器的指南 章节 第三章连接器 第四章Tomcat的默认连接器 第五章servlet容器 第六章生命周期 第七章日志记录器 第八章加载器 第九章会议...

    Tomcat源码研究.pdf

    #### 1.13《How Tomcat Works》读书笔记(三): Tomcat default connector - **默认Connector**:默认情况下,Tomcat会配置一个HTTP/1.1 Connector,用于处理HTTP请求。 - **配置细节**:讲解了如何配置默认...

    tomcat源码分析

    8. **《How Tomcat Works》读书笔记** 这一系列笔记详细介绍了Tomcat的工作原理,包括Connector如何处理网络连接,Container如何管理Servlet,以及默认的Connector和容器配置。通过阅读这些笔记,可以深入理解...

    Tomcat_学习

    《How Tomcat Works》这本书是理解Tomcat工作原理的重要参考资料。书中详细介绍了Tomcat如何处理HTTP请求,如何加载和执行Servlet,以及它是如何管理线程和内存的。通过阅读这本书,你可以深入理解Tomcat的内部架构...

    SomeJava:将以往项目应用中各种框架、中间件的使用方式抽取出来,希望以后给刚接触直接运行,入门参考。。

    tomcat-work 是《How Tomcat Works》的代码,可惜是Jdk1.4的,也可以作为学习用。 ###分布式小框架Demo gh-soa 作为服务端 gh-soa-remote gh-web 作为客户端,通过hessian访问soa。 帮别人弄的一个框架Demo、基于...

    java8源码-cainiao:自娱自乐

    Books中存放分布式技术学习和书籍阅读后笔记、总结和一些面试搜集的问题,具体查看Books中ReadMe.md Internet中存放分布式技术等相关的学习总结 JUC中存放系列学习内容,包括系列学习总结+优秀博文搜集等 Collection...

    WebGoat笔记三_访问控制缺陷(AccessControlFlaws).pdf

    2.2.1.2 技术原理(How It works) 无 2.2.1.3 总体目标(General Goals) 每个用户都是角色的成员,每个角色只允许访问那些特定的资源。你的目标是浏览本站管理所使用的访问控制规则。 2.2.1.4 操作方法...

Global site tag (gtag.js) - Google Analytics