tomcat(一个牛人写的稿件,自己看)
tomcat(一个牛人写的文章,自己看)Tomcat源码系列1--Tomcat启动流程1文章分类:Java编程最近在看Tomcat的源
tomcat(一个牛人写的文章,自己看)
Tomcat源码系列1--Tomcat启动流程1
文章分类:Java编程
最近在看Tomcat的源码,下面用博客记下看源码的一些心得。
Tomcat是从org.apache.catalina.startup.Bootstrap#main()开始启动. 大致分为三个步骤,即init、load和start。代码如下:?

public static void main(String args[]) { try { // Attempt to load JMX class new ObjectName("test:foo=bar"); } catch (Throwable t) { System.out.println(JMX_ERROR_MESSAGE); try { // Give users some time to read the message before exiting Thread.sleep(5000); } catch (Exception ex) { } return; } if (daemon == null) { daemon = new Bootstrap(); try { daemon.init(); ★1 } catch (Throwable t) { t.printStackTrace(); return; } } try { String command = "start"; if (args.length > 0) { command = args[args.length - 1]; } if (command.equals("startd")) { args[0] = "start"; daemon.load(args); daemon.start(); } else if (command.equals("stopd")) { args[0] = "stop"; daemon.stop(); } else if (command.equals("start")) { daemon.setAwait(true); daemon.load(args); ★2 // 反射调用Catalina的start方法 daemon.start(); ★3 } else if (command.equals("stop")) { daemon.stopServer(args); } } catch (Throwable t) { t.printStackTrace(); } } ??
从以上可以很清楚的看出tomcat是通过参数的不同进行相应的命令调用。
★1 启动、初始化(加载类)
启动之前要进行相应的init()初始化,进行相应的环境设置以及包的加,以下是init()方法。(org.apache.catalina.startup.Bootstrap.init())

public void init() throws Exception { setCatalinaHome();//设置Catalina安装目录 setCatalinaBase();//设置Catalina工作目录 initClassLoaders();//加载jar包 // 将classload设置进线程,以便我们使用时进行调用 Thread.currentThread(). setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); // 加载启动类和调用它的process方法 if (log.isDebugEnabled()) log.debug("Loading startup class"); Class startupClass = catalinaLoader.loadClass ("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); // 设置共享扩张类加载器 if (log.isDebugEnabled()) log.debug("Setting startup class properties"); String methodName = "setParentClassLoader"; Class paramTypes[] = new Class[1]; paramTypes[0] = Class.forName("java.lang.ClassLoader"); Object paramValues[] = new Object[1]; paramValues[0] = sharedLoader; Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); catalinaDaemon = startupInstance; } ?
在加载jar的时候,需要初始化classloader,代码如下:(org.apache.catalina.startup.Bootstrap)

private void initClassLoaders() { try { commonLoader = createClassLoader("common", null); catalinaLoader= createClassLoader("server", commonLoader); sharedLoader = createClassLoader("shared", commonLoader); } catch (Throwable t) { log.error("Class loader creation threw exception", t); System.exit(1); } } ?
tomcat中的加载方式是:
|-------commonLoader (common)-> System Loader
|-------sharedLoader (shared)-> commonLoader -> System Loader
|-------catalinaLoader(server) -> commonLoader -> System Loader
Common是公共类加载器,负责加载tomcat内部和web应用程序可以看到的类(%CATALINA_HOME%/bin/common下的jar文件),Catalina负责加载的是tomcat内部使用的类(%CATALINA_HOME%/server下的jar文件),这些类对web应用程序不可见。Shared负责加载的是web应用程序之间共享的类(%CATALINA_BASE%/shared下的jar文件),这些类对于tomcat内部是不可见的。如果%CATALINA_HOME%/conf/catalina.Properties中没有指定Common的搜索路径,则用当前的类的类加载器即系统类加载器作为Common。??
★2 装载相应的资源
下面主要讲解tomcat的load()方法。下图是Catalina.load方法的时序图。

?
(1) 从上面的时序图可以看出首先调用Catalina类的load()方法,具体代码如下:
(org.apache.catalina.startup.Catalina)。

public void load() { initDirs(); // Before digester - it may be needed initNaming(); // Create and execute our Digester Digester digester = createStartDigester(); try { inputSource.setByteStream(inputStream); digester.push(this); digester.parse(inputSource); //对server.xml进行解析 inputStream.close(); } ...... // Start the new server if (server instanceof Lifecycle) { try { server.initialize(); //server初始化工作 } catch (LifecycleException e) { log.error("Catalina.start", e); } } long t2 = System.currentTimeMillis(); log.info("Initialization processed in " + (t2 - t1) + " ms"); } ?
(2) 在上面的load()方法中需要进行server的初始化工作,下图为Catalina.initialize的时序图,从图中可以看出server初始化所完成的工作。

?
至此,load方法结束,初期化的工作结束,下面开始进入start方法。
★3 容器启动
容器启动时,会调用Catalina.start(),下图为它的时序图。从图中可以看出StandardService的start方法被调用后会分别对Container和Connector进行start方法的调用。

?
1. Bootstrap调用Catalina的start方法
Catalina.start()方法(org.apache.catalina.startup.Catalina.start())

public void start() { // 启动server if (server instanceof Lifecycle) { try { ((Lifecycle) server).start(); ...... } ?
2. Catalina调用StandardServer的start方法
StandardServer.start() (org.apache.catalina.core.StandardServer.start() )?

public void start() throws LifecycleException { synchronized (services) { for (int i = 0; i < services.length; i++) { if (services[i] instanceof Lifecycle) ((Lifecycle) services[i]).start(); } } ?
3. StandardServer调用StandardService的start方法

org.apache.catalina.core.StandardService.start() ) public void start() throws LifecycleException { if (container != null) { synchronized (container) { if (container instanceof Lifecycle) { // standardEngine的启动 ((Lifecycle) container).start(); } } //两个connector的启动,8080和8009 synchronized (connectors) { for (int i = 0; i < connectors.length; i++) { if (connectors[i] instanceof Lifecycle) ((Lifecycle) connectors[i]).start(); } } } ?
以上StandardService.start()方法主要实现了两个功能,standardEngine的启动和connector的启动,下面分别来介绍。
?
?
Tomcat源码系列2--Tomcat启动流程2 文章分类:Java编程
下面是
public void start() throws LifecycleException { // Standard container startup //进行logger,manager,cluster,realm,resource的启动 super.start(); } ?
(2) super.start()--->org.apache.catalina.core.ContainerBase#start()

public synchronized void start() throws LifecycleException { //(省略) server.xml中配置应用组件的启动 //StandardHost容器的启动, Container children[] = findChildren(); for (int i = 0; i < children.length; i++) { if (children[i] instanceof Lifecycle) ((Lifecycle) children[i]).start(); } //StandardPipeline的启动(容器与容器间的管道) if (pipeline instanceof Lifecycle) ((Lifecycle) pipeline).start(); } ?
(3) StandardHost.start()被调用?

public synchronized void start() throws LifecycleException { //返回到以上的containerBase#start执行pipeline super.start(); }?
(4) StandardPipeline#start

public synchronized void start() throws LifecycleException { // 将会调用HostConfig#start方法 lifecycle.fireLifecycleEvent(START_EVENT, null); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); } ?
(5)? HostConfig#start

public void start() { //部暑webapps deployApps(); } ?
(6) HostConfig#deployApps?

protected void deployApps() { File appBase = appBase(); File configBase = configBase(); // Deploy XML descriptors from configBase deployDescriptors(configBase, configBase.list()); // Deploy WARs, and loop if additional descriptors are found deployWARs(appBase, appBase.list()); // Deploy expanded folders deployDirectories(appBase, appBase.list()); }??
(7) deployWARs

protected void deployWARs(File appBase, String[] files) { …… deployWAR(contextPath, dir, file); } ?
(8) deployWAR

protected void deployWAR(String contextPath, File war, String file) { if (context instanceof Lifecycle) { // (省略) Class clazz = Class.forName(host.getConfigClass()); LifecycleListener listener = (LifecycleListener) clazz.newInstance(); ((Lifecycle) context).addLifecycleListener(listener); } context.setPath(contextPath); context.setDocBase(file); //以下这一步跟进去,,StandardContext的启动 host.addChild(context); } ?
(9) StandardContext#start
在Context的启动过程中,主要完成了以下任务。
----------------------------------------------
a) 设置web app的具体目录webappResources。?
b) postWorkDirectory (),创建临时文件目录。Tomcat下面有一个work目录,用来存放临时文件。
c) 触发START_EVENT事件监听,在这个事件监听里面会启动ContextConfig的start()事件,ContextConfig是用来配置web.xml的。
d) 为context创建welcome files,通常是这三个启动文件:index.html、index.htm、index.jsp
e) 配置filter
f) 启动带有<load-on-startup>的Servlet。
g) 注册JMX。
----------------------------------------------
至此,Container启动完毕,下面是connector的启动。
● connector的启动
(1) org.apache.catalina.connector.Connector.start()?

public void start() throws LifecycleException { // Http11Protocol的启动 protocolHandler.start(); }?
(2) Http11Protocol#start?

public void start() throws Exception { try { //到了终点的启动 endpoint.start(); } catch (Exception ex) { log.error(sm.getString("http11protocol.endpoint.starterror"), ex); throw ex; }??
(3) JIoEndPoint#start?

public void start() throws Exception { for (int i = 0; i < acceptorThreadCount; i++) { //这里的acceptor是一个线程,里面是一个serversocket的启动 Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i); acceptorThread.setPriority(threadPriority); acceptorThread.setDaemon(daemon); acceptorThread.start(); } }???
(4) Acceptor#run

public void run() { // Accept the next incoming connection from the server socket try { //这里进行了accept(),等待客户端消息,进行接收 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); } } ?
至此Connector.start方法调用完毕。整个server启动完毕。
?
Tomcat源码系列3--Tomcat请求处理的流程 文章分类:Java编程
本次讲解一下Tomcat请求处理的流程,不当之处还请comment。
一. Tomcat 总体结构
Tomcat采用模块化管理,下面是 Tomcat 的总体结构图:

?
从上图中可以看出 Tomcat 的核心是两个组件:Connector 和 Container。下面是一些概念的介绍。
① Server
一个server代表了整个catalina servlet容器,在Tomcat里面的Server的用处是启动和监听服务端事件(诸如重启、关闭等命令)。
② Service
Service是由一个或多个Connector与一个Engine的组合。
③ Connector
Connector将在某个指定的端口上监听客户的请求,把从socket传递过来的数据,封装成Request,传递给Engine来处理,并从Engine处获得响应并返回给客户。
Tomcat通常会用到两种Connector:
a) Http Connector 在端口8080处侦听来自客户browser的http请求。
b) AJP Connector 在端口8009处侦听来自其它WebServer(Apache)的servlet/jsp代理请求。
二、请求处理过程解析
1. Connector处理请求
Connector处理请求的流程大致如下:

?
Connector组件启动后,会侦听相关的端口的客户端请求。
(1) 接受一个新的连接请求(org.apache.tomcat.util.net.TcpWorkerThread)?

void runIt(Object[] perThrData){ Socket s = null; try { s = endpoint.acceptSocket(); //获取一个请求 } finally { if (endpoint.isRunning()) { endpoint.tp.runIt(this); // 此处启动另一个TcpWorkerTread去接受其他请求,此线程处理已接受的请求 } } TcpConnection con = null; con = (TcpConnection) perThrData[0]; con.setEndpoint(endpoint); con.setSocket(s);endpoint.getConnectionHandler().processConnection(con,(Object[]) perThrData[1]); }????
(2) 新接收的请求被传到Http11ConnectionHandler中处理。(org.apache.coyote.http11.Http11Protocol.Http11ConnectionHandler)?

void processConnection(TcpConnection connection, Object[] thData){ Http11Processor processor=null; processor=(Http11Processor)thData[Http11Protocol.THREAD_DATA_PROCESSOR]; socket=connection.getSocket(); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); processor.setSocket(socket ); processor.process(in, out); //processor是org.apache.coyote.http11.Http11Processor 的 一个实例 }?
(3) 在 Http11Processor 中处理 http11 协议相关的信息(org.apache.coyote.http11.Http11Processor)

void process(InputStream input, OutputStream output) throws IOException{ ~~略~~ inputBuffer.setInputStream(input); outputBuffer.setOutputStream(output); inputBuffer.parseHeaders(); //http11 协议头在此方法中被取出 adapter.service(request, response); //adapter 是org.apache.catalina.connector.CoyoteAdapter 的 一个实例 } ?
接下来的流程交由容器进行处理。
2. 容器处理请求
容器交由Pipeline处理,这个Pipeline里面会放置一些vavle,请求沿着pipeline传递下去并且vavle对其进行相关的处理。比如说日志等,valve还可以自定义,具体需要查看server.xml配置文件。相关类图如下:

?
Tomcat的主要处理组件Engine、Host、Context和Wrapper的实现都会实现Pipeline接口,实际对请求的处理是一个Adpater,Tomcat中Adapter的实现是CoyoteAdapter,因此容器请求处理的入口是CoyoteAdapter的service方法。
1. CoyoteAdapter.service
?? --组装好请求处理链
?? --StandardEngine. getPipeline().getFirst().invoke(request, response);
?????? --StandardEngineValve.invoke
2. StandardEngineValve.invoke
?? --Host.getPipeline().getFirst().invoke(request, response);
????? --StandardHostValve.invoke
3. StandardHostValve.invoke
? --Context. getPipeline().getFirst().invoke(request, response);
???? --StandardContextValve.invoke
4. StandardContextValve.invoke
??? --ServletRequestListener.requestInitialized
??? --Wrapper.getPipeline().getFirst().invoke(request, response);
????????? --StandardWrapperValve.invoke
??? -- ServletRequestListener.requestDestroyed
5. StandardWrapperValve.invoke
??? --组装Filter+Servlet
??? --处理请求
(1) Connector传来的请求调用CoyoteAdapter.service()方法。(org.apache.catalina.connector.CoyoteAdapter)?

public?void?service(org.apache.coyote.Request?req,??????????????????????????org.apache.coyote.Response?res)?????????throws?Exception?{??????????????~~略~~???????if?(request?==?null)?{????????????request?=?(Request)?connector.createRequest();???????????request.setCoyoteRequest(req);???????????response?=?(Response)?connector.createResponse();????????response.setCoyoteResponse(res);????????//创建request、response对象?????</spa