`
charyle
  • 浏览: 164559 次
  • 性别: Icon_minigender_1
  • 来自: 天蝎座
社区版块
存档分类
最新评论

Tomcat源码之Session实现

 
阅读更多

     前段时间看了阵子Tomcat的源码,有所得。早想总结出来可惜太多了,不知道从何说起,今天早上有时间先从Session如何实现的说起吧,免得时间越久忘记的越多。

     Tomcat的容器有四个级别,分别为Engine、Host、Context和Wrapper。所有的容器都有相同的父类,即org.apache.catalina.core.ContainerBase。那么session的管理在哪个级别的,Engine、Host还是Context?答案是在Context级别实现的,即每个应用有自己的session管理对象。

      StandardContext是Context容器(即你的应用)的标准实现,在其start方法中(即启动时候)将创建该应用的session管理对象,非集群的情况下Session管理对象类为org.apache.catalina.session.StandardManager,它是session的标准管理器,每个Context(应用)关联一个,它实现了session的创建、查询、过期检测和序列化和反序列化,序列化和反序列化是在容器重启过程中用到的,保证重启不会丢失活动的session。

      StandardContext的父类是org.apache.catalina.session.ManagerBase,其session的保存变量声明为protected Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();即利用ConcurrentHashMap保存session,如果想所有的应用共用一个session可以先将此变量设置为static的,然后修改设置cookie的JSESSION的path为“/”。

      Session的创建——当request.getSession时候,根据请求中cookie中的或者url后的JSESSION参数查找session,如果存在则返回相应的session,不存在则创建并在响应(response)设置头信息,要求设置客户端cookie的JSESSION为session的id,path为应用的路径。

      Session的过期检测——session的超时检测如何实现的呢?

      在ContainerBase中有个ContainerBackgroundProcessor类。该类如下:

     

 protected class ContainerBackgroundProcessor implements Runnable {

        public void run() {
            while (!threadDone) {
                try {
                    Thread.sleep(backgroundProcessorDelay * 1000L);
                } catch (InterruptedException e) {
                    ;
                }
                if (!threadDone) {
                    Container parent = (Container) getMappingObject();
                    ClassLoader cl = 
                        Thread.currentThread().getContextClassLoader();
                    if (parent.getLoader() != null) {
                        cl = parent.getLoader().getClassLoader();
                    }
                    processChildren(parent, cl);
                }
            }
        }

        protected void processChildren(Container container, ClassLoader cl) {
            try {
                if (container.getLoader() != null) {
                    Thread.currentThread().setContextClassLoader
                        (container.getLoader().getClassLoader());
                }
                container.backgroundProcess();
            } catch (Throwable t) {
                log.error("Exception invoking periodic operation: ", t);
            } finally {
                Thread.currentThread().setContextClassLoader(cl);
            }
            Container[] children = container.findChildren();
            for (int i = 0; i < children.length; i++) {
                if (children[i].getBackgroundProcessorDelay() <= 0) {
                    processChildren(children[i], cl);
                }
            }
        }

    }

 

 

      当容器启动中会创建此类并运行,该类为多线程实现,即相当于启动一个与该容器相关的后台运行的线程。这个run方法中递归调用容器的processChildren方法,该方法首先运行容器相关的backgroundProcess方法,然后再递归调用子容器的processChildren,在backgroundProcess方法中会启动可能与容器相关的各种后台程序。其代码如下:

      

public void backgroundProcess() {
        
        if (!started)
            return;

        if (cluster != null) {
            try {
                cluster.backgroundProcess();
            } catch (Exception e) {
                log.warn(sm.getString("containerBase.backgroundProcess.cluster", cluster), e);                
            }
        }
        if (loader != null) {
            try {
                loader.backgroundProcess();
            } catch (Exception e) {
                log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e);                
            }
        }
        if (manager != null) {
            try {
                manager.backgroundProcess();
            } catch (Exception e) {
                log.warn(sm.getString("containerBase.backgroundProcess.manager", manager), e);                
            }
        }
        if (realm != null) {
            try {
                realm.backgroundProcess();
            } catch (Exception e) {
                log.warn(sm.getString("containerBase.backgroundProcess.realm", realm), e);                
            }
        }
        Valve current = pipeline.getFirst();
        while (current != null) {
            try {
                current.backgroundProcess();
            } catch (Exception e) {
                log.warn(sm.getString("containerBase.backgroundProcess.valve", current), e);                
            }
            current = current.getNext();
        }
        lifecycle.fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
    }

 

 

      其中如果为Context容器则会启动manager,此manager即为StandardManager,在其父类ManagerBase中的backgroundProcess方法如下:

     

/**
     * Implements the Manager interface, direct call to processExpires
     */
    public void backgroundProcess() {
        count = (count + 1) % processExpiresFrequency;
        if (count == 0)
            processExpires();
    }

 

      processExpiresFrequency默认为6,相当于每过6秒检测一次,检测方法主要调用session的isValid方法,该方法判断当前时间与最近访问时间间隔是否超过设定的时间间隔。

      另外,每次访问应用,都会调用StandardSession的access方法,即更新最近访问时间。

      要工作了,最后还有session的监听的实现,  注册监听是在应用的web.xml中声明的,那么相应的监听也是保存在Context容器的变量中,其getApplicationLifecycleListeners()方法获得容器的监听数组,其声明为:

 

    

private transient Object applicationLifecycleListenersObjects[] = 
        new Object[0];

  在StandardSession的expire方法如下:

 

Object listeners[] = context.getApplicationLifecycleListeners();
                if (notify && (listeners != null)) {
                    HttpSessionEvent event =
                        new HttpSessionEvent(getSession());
                    for (int i = 0; i < listeners.length; i++) {
                        int j = (listeners.length - 1) - i;
                        if (!(listeners[j] instanceof HttpSessionListener))
                            continue;
                        HttpSessionListener listener =
                            (HttpSessionListener) listeners[j];
                        try {
                            fireContainerEvent(context,
                                               "beforeSessionDestroyed",
                                               listener);
                            listener.sessionDestroyed(event);
                            fireContainerEvent(context,
                                               "afterSessionDestroyed",
                                               listener);
                        } catch (Throwable t) {
                            try {
                                fireContainerEvent(context,
                                                   "afterSessionDestroyed",
                                                   listener);
                            } catch (Exception e) {
                                // Ignore
                            }
                            manager.getContainer().getLogger().error
                                (sm.getString("standardSession.sessionEvent"), t);
                        }
                    }
                }

    如果监听为对Session的监听,则触发监听,而在创建session的设置session的id方法中会调用tellNew(),其会触发session创建时session监听程序。

    就这么多吧,要上班了。。。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics