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

Tomcat 源码学习 之 StandardServer

阅读更多

类名 StandardServer
继承关系

LifecycleMBeanBase

Server

关联类

NaingResources

PropertyChangeSupport

Service

实现功能 管理Service及全局的resources

 

 

 

分析

 

在catalina类中管理及维护的Server实例,实际上就是StandardServer的实例。这个类继承了Server接口,并实现了其中的管理并维护Servcie及全局的resource的方法。


而StandardServer所继承的另一个抽象类则是LifecycleMBeanBase,这个类的结构相对比较复杂,主要目的就为了同时继承Lifecycle和MBeanRegistration的默认实现,这样就不需要再重复许多代码。而因为要提供两个不同的接口的默认实现,同时又不会产生重复的代码,LifecycleMBeanBase先继承LifecycleBase类(lifecyle的默认实现),同时又继承MBeanRegistration接口,并在实现了这个接口的方法。

 

Lifecycle

Tomcat中所有可以被“Container”包含的组件(包括Container本身),都实现了这个接口。它提供了修改组件状态的方法,并使用观察者模式使得监控组件状态变化及在生命周期的不同阶段进行操作的成为可能。

 

修改组件状态得方法包括:

init()

start()

stop()

destroy()

 

组件的状态包括:

BEFORE_INIT

AFTER_INIT

BEFORE_START

START

AFTER_START

BEFORE_STOP

STOP

AFTER_STOP

BEFORE_DESTROY

AFTER_DESTROY

PERIODIC(周期性的事件)

CONFIG_START

CONFIG_STOP

 

所有的这些状态发生时,都会触发相应的事件,想要监听到这些事件并执行相应的操作,就需要用以下的方法注册成为其监听者:

addLifecycleListener(LifecycleListener listener)

findLifecycelListener()

removeLifecycleLisnter(LifecycleListener listener)

 

同时,也可以随时调用以下方法获得当前状态:

getState()

getStateName()

 

LifecycleListener

想要注册监听某个类的状态变化,就需要继承接口LifecycleListener,这个接口只声明了一个方法:

lifecyleEvent(LifecycleEvent event)

 

你需要在具体实现中决定需要监听哪些事件,对这些事件相应的做出什么样的操作。

 

LifecycleEvent

用来实现某个事件的final类,它的构造函数接收3个参数:

public LifecycleEvent(Lifecycle lifecycle, String type, Object Data)

 

参数分别表示产生当前event的Lifecycle类,类型以及相应的数据。许多时候我们只需要设置第一个和第二个参数即可。

 

LifecycleBase & LifecycleSupport

LifecycleBase类提供了Lifecycle接口的默认实现,对于事件处理的具体实现是在LifecycleSupport类中,LifecycleBase的方法仅是简单的代理到LifecycleSupport的相应方法。

 

LifecycleSupport中的维护了一个LifecycleListener的数组,同时利用一个同步锁来确保每次只有一个线程在操作。

 

    private LifecycleListener listeners[] = new LifecycleListener[0];//维护的LifecycleListener数组。
    
    private final Object listenersLock = new Object();//同步锁

    public void addLifecycleListener(LifecycleListener listener) {
      synchronized (listenersLock) {//首先同步锁,保证单线程访问
          LifecycleListener results[] =
            new LifecycleListener[listeners.length + 1];//创建一个新的,长度加一的数组。
          for (int i = 0; i < listeners.length; i++)
              results[i] = listeners[i];
          results[listeners.length] = listener;//将新的listener加入。
          listeners = results;
      }
//我想这样实现的目的是为了尽量减少Collection的使用,提高性能。Just a guess。
    }

 

 其它的方法实现也大同小异。

 

LifecycleBase中还实现了其它修改状态的的方法,具体步骤如下

1. 修改状态前确定该修改是否合法。

2. 修改状态标记,发起状态变化事件。

3. 利用模板方法给子类提供一个抽象方法来实现具体的修改状态的实现。

4. 修改状态标记,发起状态变化事件。

 

    public final synchronized void init() throws LifecycleException {
        if (!state.equals(LifecycleState.NEW)) {
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }//判断状态修改是否合法

        setStateInternal(LifecycleState.INITIALIZING, null, false); //状态标记修改,启动事件

        try {
            initInternal();//模板方法,将会由子类来实现。
        } catch (LifecycleException e) {
            setStateInternal(LifecycleState.FAILED, null, false);
            throw e;
        }

        setStateInternal(LifecycleState.INITIALIZED, null, false); //状态标记修改,启动事件
    }
    
    
    protected abstract void initInternal() throws LifecycleException;//模板方法

 

MBeanRegistration

这个接口是JDK中的JMX组件提供的一个接口,实现这个接口的MBean可以在JMX注册和反注册的之前之后提供一些相应的操作。

 

 

代码

作为container,StandardServer维护着两种资源:Service和Global Resources。因此,StandardServer中也包含了管理维护这两者的代码,这里拿addservice方法作为一个例子:

 

    public void addService(Service service) {

        service.setServer(this);

        synchronized (services) {
            Service results[] = new Service[services.length + 1];
            System.arraycopy(services, 0, results, 0, services.length);
            results[services.length] = service;
            services = results;

            if (getState().isAvailable()) {
                try {
                    service.start();
                } catch (LifecycleException e) {
                    // Ignore
                }
            }

            // Report this property change to interested listeners
            support.firePropertyChange("service", null, service);
        }

    }

 

await方法

await方法包含了启动一个SocketServer并等待接收由其它Socket信息的过程。当接收到的request是一个SHUTDOWN时,整个循环就会结束,Tomcat也随之关闭。 同时,也可以通过调用该类的StopAwait方法,来达到同样的效果。我想这个应该是Tomcat内部的关闭机制。但尚未找到证据。此处存疑。

同时对于其它的非关闭request过来之后如何做处理的,这里好像也没有做处理,难道这里的SocketServer并非真正的HTTP Request监听者?尚未得知,需要进一步的学习和了解。

 

    public void await() {
        // Negative values - don't wait on port - tomcat is embedded or we just don't like ports
        if( port == -2 ) {
            // undocumented yet - for embedding apps that are around, alive.
            return;
        }
        if( port==-1 ) {
            try {
                awaitThread = Thread.currentThread();
                while(!stopAwait) {
                    try {
                        Thread.sleep( 10000 );
                    } catch( InterruptedException ex ) {
                        // continue and check the flag
                    }
                }
            } finally {
                awaitThread = null;
            }
            return;
        }

        // Set up a server socket to wait on
        try {
            awaitSocket = new ServerSocket(port, 1,
                    InetAddress.getByName(address));
        } catch (IOException e) {
            log.error("StandardServer.await: create[" + address
                               + ":" + port
                               + "]: ", e);
            return;
        }

        try {
            awaitThread = Thread.currentThread();

            // Loop waiting for a connection and a valid command
            while (!stopAwait) {
                ServerSocket serverSocket = awaitSocket;
                if (serverSocket == null) {
                    break;
                }
    
                // Wait for the next connection
                Socket socket = null;
                StringBuilder command = new StringBuilder();
                try {
                    InputStream stream;
                    try {
                        socket = serverSocket.accept();
                        socket.setSoTimeout(10 * 1000);  // Ten seconds
                        stream = socket.getInputStream();
                    } catch (AccessControlException ace) {
                        log.warn("StandardServer.accept security exception: "
                                + ace.getMessage(), ace);
                        continue;
                    } catch (IOException e) {
                        if (stopAwait) {
                            // Wait was aborted with socket.close()
                            break;
                        }
                        log.error("StandardServer.await: accept: ", e);
                        break;
                    }

                    // Read a set of characters from the socket
                    int expected = 1024; // Cut off to avoid DoS attack
                    while (expected < shutdown.length()) {
                        if (random == null)
                            random = new Random();
                        expected += (random.nextInt() % 1024);
                    }
                    while (expected > 0) {
                        int ch = -1;
                        try {
                            ch = stream.read();
                        } catch (IOException e) {
                            log.warn("StandardServer.await: read: ", e);
                            ch = -1;
                        }
                        if (ch < 32)  // Control character or EOF terminates loop
                            break;
                        command.append((char) ch);
                        expected--;
                    }
                } finally {
                    // Close the socket now that we are done with it
                    try {
                        if (socket != null) {
                            socket.close();
                        }
                    } catch (IOException e) {
                        // Ignore
                    }
                }

                // Match against our command string
                boolean match = command.toString().equals(shutdown);
                if (match) {
                    log.info(sm.getString("standardServer.shutdownViaPort"));
                    break;
                } else
                    log.warn("StandardServer.await: Invalid command '"
                            + command.toString() + "' received");
            }
        } finally {
            ServerSocket serverSocket = awaitSocket;
            awaitThread = null;
            awaitSocket = null;

            // Close the server socket and return
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
    }
 

 

分享到:
评论

相关推荐

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

    总之,学习Tomcat源码不仅能够提升你对Web服务器底层工作的理解,还能够提高你在Java Web开发中的问题排查能力,让你成为一名更出色的开发者。通过阅读《How Tomcat Works》并结合实际项目实践,你将能够逐步揭开...

    tomcat 6 源码

    通过研究Tomcat源码,开发者可以学习到如何构建一个高性能的Servlet容器,掌握Java Web应用的核心运行机制,这对于提升Java EE开发能力大有裨益。同时,如果你遇到Tomcat的使用问题或者想要进行定制化开发,源码分析...

    tomcat7源码环境部署

    关键类如`Catalina`、`StandardServer`、`StandardService`和`StandardEngine`在启动和管理Tomcat的过程中起着重要作用。 4. **环境配置**: 配置Tomcat环境涉及设置JAVA_HOME环境变量,确保Java运行时环境可用。...

    Tomcat服务器源码

    Tomcat源码主要由以下几个关键组件构成: 1. **Catalina**:这是Tomcat的核心组件,负责处理Servlet容器的主要功能,包括Servlet和JSP的加载、调度和执行。Catalina的核心类是`org.apache.catalina.core....

    tomcat6源码

    《深入剖析Tomcat6源码》 ...通过对Tomcat6源码的分析,开发者不仅可以提升对Web服务器内部运作的理解,还能学习到如何优化性能、调试问题以及定制化开发。这将对Java Web开发和系统架构设计有着深远的影响。

    apache-tomcat-6.0.18源码

    Tomcat以其轻量级、高效和易用性而闻名,它是Apache软件基金会的项目之一。在这个源码版本中,我们可以深入理解Tomcat的工作原理以及其内部机制。 源码分析: 1. **目录结构**:解压后的源码文件夹通常包含`bin`、`...

    apache-tomcat-8.5.100-src Tomcat源码解析

    总之,Apache Tomcat的源码解析是一次深入学习Java Web技术的宝贵机会,它可以帮助你更好地理解Servlet和JSP的运行机制,提升你的开发和调试技能。通过实际操作编译源码,你可以进一步掌握Maven和Java构建流程,为...

    Tomcat源码研究

    总的来说,Tomcat源码研究涉及了网络编程、多线程、XML解析、安全策略等多个领域,深入学习可以帮助我们理解Web服务器的工作机制,提升编程技巧,更好地解决实际开发中的问题。同时,源码研究也能为我们提供定制化...

    Tomcat7 核心包 catalina包源码

    深入到源码层面,Catalina的核心类`org.apache.catalina.core.StandardServer`负责服务器的初始化和关闭。它继承自`Lifecycle`接口,并实现了一系列的生命周期方法,如`start()`和`stop()`,确保Tomcat的正确启动和...

    tomcat8.0源码

    总结,Tomcat 8.0源码的学习能帮助开发者深入理解Web服务器的工作原理,提升问题排查和性能优化的能力。通过阅读和分析源码,我们可以学习到包括容器管理、请求处理、JSP编译、线程调度等多个领域的知识,这将对Java...

    tomcat 7 源码分析-4 server初始化背后getServer().init()

    通过对Tomcat源码的深入理解和分析,开发者可以更好地掌握服务器的运行机制,解决性能问题,定制化配置,甚至为Tomcat贡献自己的代码。无论是新手还是经验丰富的开发者,都能从这样的源码分析中受益。

    apache-tomcat-7.0.57-src可导入Myeclipse

    3. **定位源代码**:在“Select root directory”中,浏览并选择你刚刚解压的Tomcat源码目录。 4. **配置构建路径**:在导入的项目中,你需要配置Java构建路径,确保所有的依赖库都正确引用。这可能包括添加JDK和...

    TomcatSourceReview:Tomcat源码阅读

    总的来说,Tomcat源码阅读是一个深度学习Java Web技术的过程,涵盖了网络编程、多线程、容器管理、安全性等多个方面。通过这次源码探索,我们可以深入了解Web服务器的工作机制,为日常开发和问题排查提供有力支持。...

    tomacat-8.0.15源码

    四、源码学习价值 1. 了解Servlet和JSP的工作原理:Tomcat源码提供了对Servlet生命周期的管理,以及JSP的编译和执行过程的深入理解。 2. 自定义容器:可以基于Tomcat源码创建自己的Servlet容器,满足特定需求。 3. ...

    apache-tomcat-9.0.0.M1-src

    通过对"apache-tomcat-9.0.0.M1-src"的源码学习,开发者可以深入了解Tomcat的工作机制,提升Web应用的开发、调试和优化能力。同时,这也是对Java EE规范实现的深入理解,对于从事Java Web开发的人来说,是一份宝贵的...

    tomcat6.0.39源代码

    9. **性能优化**:Tomcat源码中包含了很多可调整的参数和设置,如线程池大小、缓冲区大小、超时时间等,这些都是性能调优的关键。 10. **集群支持**:如果需要构建高可用的Tomcat集群,`Cluster`模块的源码提供了...

    tomcat工作原理-组件

    Tomcat作为Apache软件基金会的开源项目,是Java Servlet、JavaServer Pages(JSP)和Java Expression Language(EL)的实现,是Web应用程序服务器的首选之一。深入理解Tomcat的工作原理对于优化性能、调试问题以及...

    Learning materials

    本文将基于标题"Learning materials",结合描述中的博文链接以及标签"源码"和"工具",详细探讨《How Tomcat Works (chinese).pdf》这份学习资料中所涵盖的关键知识点,帮助你深入理解Tomcat的工作原理。 1. **...

Global site tag (gtag.js) - Google Analytics