Tomcat源码解析(四):session管理

来源:互联网 发布:腾讯红包数据控制 编辑:程序博客网 时间:2024/09/21 09:04

session是什么?

我们访问某个很多网站时,第一次进入需要登陆,但进入下一个页面时就不需要再登陆了,否则这个网站的可用性就是零啦~~

在第二次访问时,我们想让服务端识别出请求来自同一个客户。要怎么做呢?其中一个方法就是用session来实现。

首先,session是存储在服务端的数据。一个请求到来时,服务端首先看请求中有没有sessionId,若没有,就新建一个session,并将sessionId传回给客户。客户第二次访问时,带上sessionId,这样服务端就可以识别出它啦~

在分布式系统中,有多台服务器,怎样保证sessionId不会有重复呢?大家可以去看一下sessionId的生成方法,它包含了jvm机器的信息及随机数等,所以不会重复。

分布式系统中,如果session由机器A生成,即存储在机器A,但下一次请求到达了机器B,那么机器B上就找不到sessionId对应的session,这时候怎么办呢?这就涉及到分布式系统下的session共享问题,大家可以看一看淘宝等的解决方案。

tomcat处理请求流程图

tomcat怎么管理session?

首先,session要有存储的地方,tomcat提供了FileStore和JDBCStore,即文件存储和数据库存储。

tomcat包括了connector和container两部分,那么由谁负责管理session呢?当然是container啦。因为,connector是负责通信和分发任务的,而container负责处理请求这一部分。

container就是一个容器,包含了多个组件(见ManagerBase),比如类加载器loader。同样地,session的管理也不是由container直接负责,而是另一个组件manager来负责。manager的主要功能有创建session、查找session、从文件或DB加载session、将session存入文件或DB等。

tomcat和manager的交互过程:

  1. container.start(),设置完成后,调用manager.start(),开始加载session等。
  2. container.stop(),项目停止,调用manager.stop(),存储session等。
  3. container.reload(),项目重载,先调用manager.stop(),再调用manager.start().

嗯。。过程看起来还是很清晰简单的。下面我们具体看一下manager的一些操作吧。

manager的主要功能

  • StandardManager.start()调用load()加载存储的session,并启动后台线程判断session的过期。
public void start() throws LifecycleException {        if (debug >= 1)            log("Starting");        // Validate and update our current component state        if (started)            throw new LifecycleException                (sm.getString("standardManager.alreadyStarted"));        lifecycle.fireLifecycleEvent(START_EVENT, null);        started = true;        // Force initialization of the random number generator        if (debug >= 1)            log("Force random number initialization starting");        String dummy = generateSessionId();        if (debug >= 1)            log("Force random number initialization completed");        // Load unloaded sessions, if any        // 加载存储的session,这里是存储在文件中的        try {            load();        } catch (Throwable t) {            log(sm.getString("standardManager.managerLoad"), t);        }        // Start the background reaper thread        //启动后台线程,内容见run(),主要判断session的过期        threadStart();    }

load()加载存储在文件中的session。

public void load() throws ClassNotFoundException, IOException {if (debug >= 1)        log("Start: Loading persisted sessions");    // Initialize our internal data structures    recycled.clear();    sessions.clear();    // Open an input stream to the specified pathname, if any    //打开指定路径的文件    File file = file();    if (file == null)        return;    if (debug >= 1)        log(sm.getString("standardManager.loading", pathname));    FileInputStream fis = null;    ObjectInputStream ois = null;    Loader loader = null;    ClassLoader classLoader = null;    try {        fis = new FileInputStream(file.getAbsolutePath());        BufferedInputStream bis = new BufferedInputStream(fis);        if (container != null)            loader = container.getLoader();        if (loader != null)            classLoader = loader.getClassLoader();        if (classLoader != null) {            if (debug >= 1)                log("Creating custom object input stream for class loader "                    + classLoader);            ois = new CustomObjectInputStream(bis, classLoader);        } else {            if (debug >= 1)                log("Creating standard object input stream");            ois = new ObjectInputStream(bis);        }    } catch (FileNotFoundException e) {        if (debug >= 1)            log("No persisted data file found");        return;    } catch (IOException e) {        log(sm.getString("standardManager.loading.ioe", e), e);        if (ois != null) {            try {                ois.close();            } catch (IOException f) {                ;            }            ois = null;        }        throw e;    }    // Load the previously unloaded active sessions    // 读取文件中存储的session数据    synchronized (sessions) {        try {            Integer count = (Integer) ois.readObject();            int n = count.intValue();            if (debug >= 1)                log("Loading " + n + " persisted sessions");            for (int i = 0; i < n; i++) {                StandardSession session = new StandardSession(this);                session.readObjectData(ois);                session.setManager(this);                sessions.put(session.getId(), session);                ((StandardSession) session).activate();            }        } catch (ClassNotFoundException e) {          log(sm.getString("standardManager.loading.cnfe", e), e);            if (ois != null) {                try {                    ois.close();                } catch (IOException f) {                    ;                }                ois = null;            }            throw e;        } catch (IOException e) {          log(sm.getString("standardManager.loading.ioe", e), e);            if (ois != null) {                try {                    ois.close();                } catch (IOException f) {                    ;                }                ois = null;            }            throw e;        } finally {            // Close the input stream            try {                if (ois != null)                    ois.close();            } catch (IOException f) {                // ignored            }            // Delete the persistent storage file            // 删除文件            if (file != null && file.exists() )                file.delete();        }    }    if (debug >= 1)        log("Finish: Loading persisted sessions");}   


run()调用processExpires()判断并处理session过期。

private void processExpires() {    long timeNow = System.currentTimeMillis();    Session sessions[] = findSessions();    for (int i = 0; i < sessions.length; i++) {        StandardSession session = (StandardSession) sessions[i];        if (!session.isValid())            continue;        // maxInactiveInterval<0,该session就一直有效        int maxInactiveInterval = session.getMaxInactiveInterval();        if (maxInactiveInterval < 0)            continue;        int timeIdle = // Truncate, do not round up            (int) ((timeNow - session.getLastAccessedTime()) / 1000L);        if (timeIdle >= maxInactiveInterval) {            try {                session.expire();            } catch (Throwable t) {                log(sm.getString("standardManager.expireException"), t);            }        }    }}


StandardManager.stop(),停止后台处理session过期的线程,并存储session。

public void stop() throws LifecycleException {    if (debug >= 1)        log("Stopping");    // Validate and update our current component state    if (!started)        throw new LifecycleException            (sm.getString("standardManager.notStarted"));    lifecycle.fireLifecycleEvent(STOP_EVENT, null);    started = false;    // Stop the background reaper thread    threadStop();    // Write out sessions    try {        unload();    } catch (IOException e) {        log(sm.getString("standardManager.managerUnload"), e);    }    // Expire all active sessions    Session sessions[] = findSessions();    for (int i = 0; i < sessions.length; i++) {        StandardSession session = (StandardSession) sessions[i];        if (!session.isValid())            continue;        try {            session.expire();        } catch (Throwable t) {            ;        }    }    // Require a new random number generator if we are restarted    this.random = null;}


StandardManager.createSession(),创建session。

public Session createSession() {    // Recycle or create a Session instance    Session session = null;    synchronized (recycled) {        int size = recycled.size();        if (size > 0) {            session = (Session) recycled.get(size - 1);            recycled.remove(size - 1);        }    }    if (session != null)        session.setManager(this);    else        session = new StandardSession(this);    // Initialize the properties of the new session and return it    session.setNew(true);    session.setValid(true);    session.setCreationTime(System.currentTimeMillis());    session.setMaxInactiveInterval(this.maxInactiveInterval);    String sessionId = generateSessionId();    String jvmRoute = getJvmRoute();    // @todo Move appending of jvmRoute generateSessionId()???    if (jvmRoute != null) {        sessionId += '.' + jvmRoute;        session.setId(sessionId);    }    /*    synchronized (sessions) {        while (sessions.get(sessionId) != null)        // Guarantee uniqueness            sessionId = generateSessionId();    }    */    session.setId(sessionId);    return (session);}


createSession()是什么时候被调用呢?当我们调用request.getSession()的时候。request中有container的引用,当我们调用request.getSession(),首先判断request中是否有session;若没有,获取container.manager,调用manager.findSession()查找已有session;若没有,则可以调用manager.createSession来创建。

private HttpSession doGetSession(boolean create) {    // There cannot be a session if no context has been assigned yet    if (context == null)        return (null);    // Return the current session if it exists and is valid    if ((session != null) && !session.isValid())        session = null;    if (session != null)        return (session.getSession());    // Return the requested session if it exists and is valid    Manager manager = null;    if (context != null)        manager = context.getManager();    if (manager == null)        return (null);      // Sessions are not supported    if (requestedSessionId != null) {        try {            session = manager.findSession(requestedSessionId);        } catch (IOException e) {            session = null;        }        if ((session != null) && !session.isValid())            session = null;        if (session != null) {            return (session.getSession());        }    }    // Create a new session if requested and the response is not committed    if (!create)        return (null);    if ((context != null) && (response != null) &&        context.getCookies() &&        response.getResponse().isCommitted()) {        throw new IllegalStateException          (sm.getString("httpRequestBase.createCommitted"));    }    session = manager.createSession();    if (session != null)        return (session.getSession());    else        return (null);}

HttpRequestBase、HttpRequestFacade、HttpRequestImpl的关系

HttpSession、HttpSessionFacade的关系?

sessionId什么时候返回给客户?如何返回?

0 0
原创粉丝点击