`
mabusyao
  • 浏览: 248686 次
  • 性别: 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
                }
            }
        }
    }
 

 

分享到:
评论

相关推荐

    apache-tomcat-8.5.50-src.zip

    为啥StandardServer没有init方法 LifecycleBase中的init与initInternal方法 为什么这么设计? 分析Tomcat请求过程 链接器(Connector)与容器(Container) 解耦 Connector设计 监听服务端口,读取来自...

    myTomcat:WebServer + Tomcat源码分析总结

    来自《深入剖析Tomcat》 HTTP请求流程-&gt;初始日期一个HTTP请求: 前导工作: org.apache.catalina.startup.Bootstrap启动startup.sh/bat来启动其main(),main()调用Catalina的process() org.apache.catalina...

    Tomcat 配置与优化方案详解

    className:实现了org.apache.catalina.Server接口的类名,标准实现类是org.apache.catalina.core.StandardServer类。 Port:Tomcat服务器监听用于关闭Tomcat服务器的命令(必须) Shutdown:发送到端口上用于...

    how-tomcat-works

    14.3 StandardServer 114 14.3.1 initialize方法 114 14.3.2 start方法 115 14.3.3 stop方法 115 14.3.4 await方法 116 14.4 Service接口 116 14.5 StandardService类 116 14.5.1 connector和container 117 14.5.2 ...

    apache-tomcat-7.0.57:关于Apache-tomcat-7.0.57的研究来源

    ====================research the source about apache-tomcat-7.0.57====================/org/apache/catalina/startup/Bootstrap---2014/11/28/org/apache/catalina/startup/Catalina---2014/11/28当Bootstrap...

    How Tomcat Works: A Guide to Developing Your Own Java Servlet Container

    14.3 StandardServer 114 14.3.1 initialize方法 114 14.3.2 start方法 115 14.3.3 stop方法 115 14.3.4 await方法 116 14.4 Service接口 116 14.5 StandardService类 116 14.5.1 connector和container 117 14.5.2 ...

    struts2驱动包

    at org.apache.catalina.core.StandardServer.start(StandardServer.java:710) at org.apache.catalina.startup.Catalina.start(Catalina.java:578) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method...

    信息: Deploying web application directory lx01

    at org.apache.catalina.core.StandardServer.start(StandardServer.java:710) at org.apache.catalina.startup.Catalina.start(Catalina.java:581) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native ...

    client-server:python中的简单客户端和服务器

    小皮海在推荐行中输入一个推荐的“ python standardserver.py(数字)”。 (数字)是指服务器将在计算机上绑定到的端口号。 您最好使用大于10000的数字。否则,该端口可能已被您的计算机使用,这将导致服务器无法...

Global site tag (gtag.js) - Google Analytics