最近在看Tomcat的源码,下面用博客记下看源码的一些心得。

10年积累的做网站、网站建设经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站设计后付款的网站建设流程,更有恭城免费网站建设让你可以放心的选择与我们合作。
Tomcat是从org.apache.catalina.startup.Bootstrap#main()开始启动. 大致分为三个步骤,即init、load和start。代码如下:
Java代码
- 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())
Java代码
- 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)
Java代码
- 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)。
Java代码
- 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())
Java代码
- public void start() {
 - // 启动server
 - if (server instanceof Lifecycle) {
 - try {
 - ((Lifecycle) server).start();
 - ......
 - }
 
2. Catalina调用StandardServer的start方法
StandardServer.start() (org.apache.catalina.core.StandardServer.start() )
Java代码
- 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方法
Java代码
- 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的启动,下面分别来介绍。
#p#
下面是standardEngine的启动和connector的启动
● standardEngine的启动
(1) 首先是StandardEngine.start()被调用
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()
Java代码
- 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()被调用
Java代码
- public synchronized void start() throws LifecycleException {
 - //返回到以上的containerBase#start执行pipeline
 - super.start();
 - }
 
(4) StandardPipeline#start
Java代码
- 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
Java代码
- public void start() {
 - //部暑webapps
 - deployApps();
 - }
 
(6) HostConfig#deployApps
Java代码
- 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
Java代码
- protected void deployWARs(File appBase, String[] files) {
 - ……
 - deployWAR(contextPath, dir, file);
 - }
 
(8) deployWAR
Java代码
- 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) 启动带有 的Servlet。
g) 注册JMX。
----------------------------------------------------------------------------------------------------------------------
至此,Container启动完毕,下面是connector的启动。
● connector的启动
(1) org.apache.catalina.connector.Connector.start()
Java代码
- public void start() throws LifecycleException {
 - // Http11Protocol的启动
 - protocolHandler.start();
 - }
 
(2) Http11Protocol#start
Java代码
- public void start() throws Exception {
 - try {
 - //到了终点的启动
 - endpoint.start();
 - } catch (Exception ex) {
 - log.error(sm.getString("http11protocol.endpoint.starterror"), ex);
 - throw ex;
 - }
 
(3) JIoEndPoint#start
Java代码
- 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
Java代码
- 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启动完毕。
#p#
本次讲解一下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)
Java代码
- 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)
Java代码
- 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)
Java代码
- 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)
Java代码
- 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对象
 - ~~略~~
 - }
 - try {
 - if (postParseRequest(req, request, res, response)) {
 - connector.getContainer().getPipeline().getFirst().invoke(request, response);
 - //此处的Container是StandardEngine对象
 - ~~略~~
 - }
 - }
 
(2) 默认StandardEngine的Pipeline会有StandardEngineValve处理单元(参照StandardEngine构造函数)。(org.apache.catalina.core.StandardEngineValve)
Java代码
- public final void invoke(Request request, Response response)
 - throws IOException, ServletException {
 - // Select the Host to be used for this Request
 - Host host = request.getHost();
 - if (host == null) { &nb
 新闻标题:分享Tomcat源码系列三部曲
网站网址:http://www.csdahua.cn/qtweb/news5/515405.html网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网