日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

tomcat架构分析 (Session管理)【转】

發(fā)布時(shí)間:2025/4/5 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tomcat架构分析 (Session管理)【转】 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

原文地址:https://www.iteye.com/blog/gearever-1546423

Session管理是JavaEE容器比較重要的一部分,在app中也經(jīng)常會(huì)用到。在開(kāi)發(fā)app時(shí),我們只是獲取一個(gè)session,然后向 session中存取數(shù)據(jù),然后再銷毀session。那么如何產(chǎn)生session,以及session池如何維護(hù)及管理,這些并沒(méi)有在app涉及到。這 些工作都是由容器來(lái)完成的。?
Tomcat中主要由每個(gè)context容器內(nèi)的一個(gè)Manager對(duì)象來(lái)管理session。對(duì)于這個(gè)manager對(duì)象的實(shí)現(xiàn),可以根據(jù)tomcat提供的接口或基類來(lái)自己定制,同時(shí),tomcat也提供了標(biāo)準(zhǔn)實(shí)現(xiàn)。?
在tomcat架構(gòu)分析(容器類)中已經(jīng)介紹過(guò),在每個(gè)context對(duì)象,即web app都具有一個(gè)獨(dú)立的manager對(duì)象。通過(guò)server.xml可以配置定制化的manager,也可以不配置。不管怎樣,在生成context對(duì) 象時(shí),都會(huì)生成一個(gè)manager對(duì)象。缺省的是StandardManager類,其類路徑為:

引用 org.apache.catalina.session.StandardManager


Session對(duì)象也可以定制化實(shí)現(xiàn),其主要實(shí)現(xiàn)標(biāo)準(zhǔn)servlet的session接口:

引用 javax.servlet.http.HttpSession


Tomcat也提供了標(biāo)準(zhǔn)的session實(shí)現(xiàn):

引用 org.apache.catalina.session.StandardSession


本文主要就是結(jié)合消息流程介紹這兩個(gè)類的實(shí)現(xiàn),及session機(jī)制。?
Session方面牽涉的東西還是蠻多的,例如HA,session復(fù)制是其中重要部分等,不過(guò)本篇主要從功能方面介紹session管理,有時(shí)間再說(shuō)說(shuō)擴(kuò)展。?
Session管理主要涉及到這幾個(gè)方面:

  • 創(chuàng)建session
  • 注銷session
  • 持久化及啟動(dòng)加載session



創(chuàng)建session?
在具體說(shuō)明session的創(chuàng)建過(guò)程之前,先看一下BS訪問(wèn)模型吧,這樣理解直觀一點(diǎn)。?

  • browser發(fā)送Http request;
  • tomcat內(nèi)核Http11Processor會(huì)從HTTP request中解析出“jsessionid”(具體的解析過(guò)程為先從request的URL中解析,這是為了有的瀏覽器把cookie功能禁止后,將 URL重寫(xiě)考慮的,如果解析不出來(lái),再?gòu)腸ookie中解析相應(yīng)的jsessionid),解析完后封裝成一個(gè)request對(duì)象(當(dāng)然還有其他的 http header);
  • servlet中獲取session,其過(guò)程是根據(jù)剛才解析得到的jsessionid(如果有的話),從session池 (session maps)中獲取相應(yīng)的session對(duì)象;這個(gè)地方有個(gè)邏輯,就是如果jsessionid為空的話(或者沒(méi)有其對(duì)應(yīng)的session對(duì)象,或者有 session對(duì)象,但此對(duì)象已經(jīng)過(guò)期超時(shí)),可以選擇創(chuàng)建一個(gè)session,或者不創(chuàng)建;
  • 如果創(chuàng)建新session,則將session放入session池中,同時(shí)將與其相對(duì)應(yīng)的jsessionid寫(xiě)入cookie通過(guò)Http response header的方式發(fā)送給browser,然后重復(fù)第一步。

  • 以上是session的獲取及創(chuàng)建過(guò)程。在servlet中獲取session,通常是調(diào)用request的getSession方法。這個(gè)方法 需要傳入一個(gè)boolean參數(shù),這個(gè)參數(shù)就是實(shí)現(xiàn)剛才說(shuō)的,當(dāng)jsessionid為空或從session池中獲取不到相應(yīng)的session對(duì)象時(shí),選 擇創(chuàng)建一個(gè)新的session還是不創(chuàng)建。?
    看一下核心代碼邏輯;

    Java代碼 ??
  • protected?Session?doGetSession(boolean?create)?{??
  • ??
  • ????????……??
  • ????????//?先獲取所在context的manager對(duì)象??
  • ????????Manager?manager?=?null;??
  • ????????if?(context?!=?null)??
  • ????????????manager?=?context.getManager();??
  • ????????if?(manager?==?null)??
  • ????????????return?(null);??????//?Sessions?are?not?supported??
  • ??????????
  • ????????//這個(gè)requestedSessionId就是從Http?request中解析出來(lái)的??
  • ????????if?(requestedSessionId?!=?null)?{??
  • ????????????try?{??
  • ????????????????//manager管理的session池中找相應(yīng)的session對(duì)象??
  • ????????????????session?=?manager.findSession(requestedSessionId);??
  • ????????????}?catch?(IOException?e)?{??
  • ????????????????session?=?null;??
  • ????????????}??
  • ????????????//判斷session是否為空及是否過(guò)期超時(shí)??
  • ????????????if?((session?!=?null)?&&?!session.isValid())??
  • ????????????????session?=?null;??
  • ????????????if?(session?!=?null)?{??
  • ????????????????//session對(duì)象有效,記錄此次訪問(wèn)時(shí)間??
  • ????????????????session.access();??
  • ????????????????return?(session);??
  • ????????????}??
  • ????????}??
  • ??
  • ????????//?如果參數(shù)是false,則不創(chuàng)建新session對(duì)象了,直接退出了??
  • ????????if?(!create)??
  • ????????????return?(null);??
  • ????????if?((context?!=?null)?&&?(response?!=?null)?&&??
  • ????????????context.getCookies()?&&??
  • ????????????response.getResponse().isCommitted())?{??
  • ????????????throw?new?IllegalStateException??
  • ??????????????(sm.getString("coyoteRequest.sessionCreateCommitted"));??
  • ????????}??
  • ??
  • ????????//?開(kāi)始創(chuàng)建新session對(duì)象??
  • ????????if?(connector.getEmptySessionPath()???
  • ????????????????&&?isRequestedSessionIdFromCookie())?{??
  • ????????????session?=?manager.createSession(getRequestedSessionId());??
  • ????????}?else?{??
  • ????????????session?=?manager.createSession(null);??
  • ????????}??
  • ??
  • ????????//?將新session的jsessionid寫(xiě)入cookie,傳給browser??
  • ????????if?((session?!=?null)?&&?(getContext()?!=?null)??
  • ???????????????&&?getContext().getCookies())?{??
  • ????????????Cookie?cookie?=?new?Cookie(Globals.SESSION_COOKIE_NAME,??
  • ???????????????????????????????????????session.getIdInternal());??
  • ????????????configureSessionCookie(cookie);??
  • ????????????response.addCookieInternal(cookie);??
  • ????????}??
  • ????????//記錄session最新訪問(wèn)時(shí)間??
  • ????????if?(session?!=?null)?{??
  • ????????????session.access();??
  • ????????????return?(session);??
  • ????????}?else?{??
  • ????????????return?(null);??
  • ????????}??
  • ????}??

  • 盡管不能貼出所有代碼,但是上述的核心邏輯還是很清晰的。從中也可以看出,我們經(jīng)常在servlet中這兩種調(diào)用方式的不同;?
    新創(chuàng)建session

    引用 request.getSession(); 或者request.getSession(true);


    不創(chuàng)建session

    引用 request.getSession(false);


    接下來(lái),看一下StandardManager的createSession方法,了解一下session的創(chuàng)建過(guò)程;

    Java代碼 ??
  • public?Session?createSession(String?sessionId)?{??
  • 是個(gè)session數(shù)量控制邏輯,超過(guò)上限則拋異常退出??
  • ????if?((maxActiveSessions?>=?0)?&&??
  • ????????(sessions.size()?>=?maxActiveSessions))?{??
  • ????????rejectedSessions++;??
  • ????????throw?new?IllegalStateException??
  • ????????????(sm.getString("standardManager.createSession.ise"));??
  • ????}??
  • ????return?(super.createSession(sessionId));??
  • }??

  • 這個(gè)最大支持session數(shù)量maxActiveSessions是可以配置的,先不管這個(gè)安全控制邏輯,看其主邏輯,即調(diào)用其基類的createSession方法;

    Java代碼 ??
  • public?Session?createSession(String?sessionId)?{??
  • ??????????
  • ????????//?創(chuàng)建一個(gè)新的StandardSession對(duì)象??
  • ????????Session?session?=?createEmptySession();??
  • ??
  • ????????//?Initialize?the?properties?of?the?new?session?and?return?it??
  • ????????session.setNew(true);??
  • ????????session.setValid(true);??
  • ????????session.setCreationTime(System.currentTimeMillis());??
  • ????????session.setMaxInactiveInterval(this.maxInactiveInterval);??
  • ????????if?(sessionId?==?null)?{??
  • ????????????//設(shè)置jsessionid??
  • ????????????sessionId?=?generateSessionId();??
  • ????????}??
  • ????????session.setId(sessionId);??
  • ????????sessionCounter++;??
  • ????????return?(session);??
  • ????}??

  • 關(guān)鍵是jsessionid的產(chǎn)生過(guò)程,接著看generateSessionId方法;

    Java代碼 ??
  • protected?synchronized?String?generateSessionId()?{??
  • ??
  • ????????byte?random[]?=?new?byte[16];??
  • ????????String?jvmRoute?=?getJvmRoute();??
  • ????????String?result?=?null;??
  • ??
  • ????????//?Render?the?result?as?a?String?of?hexadecimal?digits??
  • ????????StringBuffer?buffer?=?new?StringBuffer();??
  • ????????do?{??
  • ????????????int?resultLenBytes?=?0;??
  • ????????????if?(result?!=?null)?{??
  • ????????????????buffer?=?new?StringBuffer();??
  • ????????????????duplicates++;??
  • ????????????}??
  • ??
  • ????????????while?(resultLenBytes?<?this.sessionIdLength)?{??
  • ????????????????getRandomBytes(random);??
  • ????????????????random?=?getDigest().digest(random);??
  • ????????????????for?(int?j?=?0;??
  • ????????????????j?<?random.length?&&?resultLenBytes?<?this.sessionIdLength;??
  • ????????????????j++)?{??
  • ????????????????????byte?b1?=?(byte)?((random[j]?&?0xf0)?>>?4);??
  • ????????????????????byte?b2?=?(byte)?(random[j]?&?0x0f);??
  • ????????????????????if?(b1?<?10)??
  • ????????????????????????buffer.append((char)?('0'?+?b1));??
  • ????????????????????else??
  • ????????????????????????buffer.append((char)?('A'?+?(b1?-?10)));??
  • ????????????????????if?(b2?<?10)??
  • ????????????????????????buffer.append((char)?('0'?+?b2));??
  • ????????????????????else??
  • ????????????????????????buffer.append((char)?('A'?+?(b2?-?10)));??
  • ????????????????????resultLenBytes++;??
  • ????????????????}??
  • ????????????}??
  • ????????????if?(jvmRoute?!=?null)?{??
  • ????????????????buffer.append('.').append(jvmRoute);??
  • ????????????}??
  • ????????????result?=?buffer.toString();??
  • ????????//注意這個(gè)do…while結(jié)構(gòu)??
  • ????????}?while?(sessions.containsKey(result));??
  • ????????return?(result);??
  • ????}??

  • 這里主要說(shuō)明的不是生成jsessionid的算法了,而是這個(gè)do…while結(jié)構(gòu)。把這個(gè)邏輯抽象出來(lái),可以看出;?

    如圖所示,創(chuàng)建jsessionid的方式是由tomcat內(nèi)置的加密算法算出一個(gè)隨機(jī)的jsessionid,如果此jsessionid已經(jīng)存在,則重新計(jì)算一個(gè)新的,直到確保現(xiàn)在計(jì)算的jsessionid唯一。?
    好了,至此一個(gè)session就這么創(chuàng)建了,像上面所說(shuō)的,返回時(shí)是將jsessionid以HTTP response的header:“Set-cookie”發(fā)給客戶端。?
    注銷session

    • 主動(dòng)注銷
    • 超時(shí)注銷


    Session創(chuàng)建完之后,不會(huì)一直存在,或是主動(dòng)注銷,或是超時(shí)清除。即是出于安全考慮也是為了節(jié)省內(nèi)存空間等。例如,常見(jiàn)場(chǎng)景:用戶登出系統(tǒng)時(shí),會(huì)主動(dòng)觸發(fā)注銷操作。?
    主動(dòng)注銷?
    主動(dòng)注銷時(shí),是調(diào)用標(biāo)準(zhǔn)的servlet接口:

    引用 session.invalidate();


    看一下tomcat提供的標(biāo)準(zhǔn)session實(shí)現(xiàn)(StandardSession)

    Java代碼 ??
  • public?void?invalidate()?{??
  • ????????if?(!isValidInternal())??
  • ????????????throw?new?IllegalStateException??
  • ????????????????(sm.getString("standardSession.invalidate.ise"));??
  • ????????//?明顯的注銷方法??
  • ????????expire();??
  • ????}??

  • Expire方法的邏輯稍后再說(shuō),先看看超時(shí)注銷,因?yàn)樗鼈冋{(diào)用的是同一個(gè)expire方法。?
    超時(shí)注銷?
    Tomcat定義了一個(gè)最大空閑超時(shí)時(shí)間,也就是說(shuō)當(dāng)session沒(méi)有被操作超過(guò)這個(gè)最大空閑時(shí)間時(shí)間時(shí),再次操作這個(gè)session,這個(gè)session就會(huì)觸發(fā)expire。?
    這個(gè)方法封裝在StandardSession中的isValid()方法內(nèi),這個(gè)方法在獲取這個(gè)request請(qǐng)求對(duì)應(yīng)的session對(duì)象時(shí) 調(diào)用,可以參看上面說(shuō)的創(chuàng)建session環(huán)節(jié)。也就是說(shuō),獲取session的邏輯是,先從manager控制的session池中獲取對(duì)應(yīng) jsessionid的session對(duì)象,如果獲取到,就再判斷是否超時(shí),如果超時(shí),就expire這個(gè)session了。?
    看一下tomcat提供的標(biāo)準(zhǔn)session實(shí)現(xiàn)(StandardSession)

    Java代碼 ??
  • public?boolean?isValid()?{??
  • ????????……??
  • ????????//這就是判斷距離上次訪問(wèn)是否超時(shí)的過(guò)程??
  • ????????if?(maxInactiveInterval?>=?0)?{???
  • ????????????long?timeNow?=?System.currentTimeMillis();??
  • ????????????int?timeIdle?=?(int)?((timeNow?-?thisAccessedTime)?/?1000L);??
  • ????????????if?(timeIdle?>=?maxInactiveInterval)?{??
  • ????????????????expire(true);??
  • ????????????}??
  • ????????}??
  • ????????return?(this.isValid);??
  • ????}??

  • Expire方法?
    是時(shí)候來(lái)看看expire方法了。

    Java代碼 ??
  • public?void?expire(boolean?notify)?{???
  • ??
  • ????????synchronized?(this)?{??
  • ????????????......??
  • ????????????//設(shè)立標(biāo)志位??
  • ????????????setValid(false);??
  • ??
  • ????????????//計(jì)算一些統(tǒng)計(jì)值,例如此manager下所有session平均存活時(shí)間等??
  • ????????????long?timeNow?=?System.currentTimeMillis();??
  • ????????????int?timeAlive?=?(int)?((timeNow?-?creationTime)/1000);??
  • ????????????synchronized?(manager)?{??
  • ????????????????if?(timeAlive?>?manager.getSessionMaxAliveTime())?{??
  • ????????????????????manager.setSessionMaxAliveTime(timeAlive);??
  • ????????????????}??
  • ????????????????int?numExpired?=?manager.getExpiredSessions();??
  • ????????????????numExpired++;??
  • ????????????????manager.setExpiredSessions(numExpired);??
  • ????????????????int?average?=?manager.getSessionAverageAliveTime();??
  • ????????????????average?=?((average?*?(numExpired-1))?+?timeAlive)/numExpired;??
  • ????????????????manager.setSessionAverageAliveTime(average);??
  • ????????????}??
  • ??
  • ????????????//?將此session從manager對(duì)象的session池中刪除??
  • ????????????manager.remove(this);??
  • ????????????......??
  • ????????}??
  • ????}??

  • 不需要解釋,已經(jīng)很清晰了。?
    這個(gè)超時(shí)時(shí)間是可以配置的,缺省在tomcat的全局web.xml下配置,也可在各個(gè)app下的web.xml自行定義;

    Xml代碼 ??
  • <session-config>??
  • ????<session-timeout>30</session-timeout>??
  • </session-config>??

  • 單位是分鐘。?
    Session持久化及啟動(dòng)初始化?
    這個(gè)功能主要是,當(dāng)tomcat執(zhí)行安全退出時(shí)(通過(guò)執(zhí)行shutdown腳本),會(huì)將session持久化到本地文件,通常在tomcat的部 署目錄下有個(gè)session.ser文件。當(dāng)啟動(dòng)tomcat時(shí),會(huì)從這個(gè)文件讀入session,并添加到manager的session池中去。?
    這樣,當(dāng)tomcat正常重啟時(shí), session沒(méi)有丟失,對(duì)于用戶而言,體會(huì)不到重啟,不影響用戶體驗(yàn)。?
    看一下概念圖吧,覺(jué)得不是重要實(shí)現(xiàn)邏輯,代碼就不說(shuō)了。?

    Session持久化可以實(shí)現(xiàn)當(dāng)tomcat重新啟動(dòng)后,當(dāng)前IE使用的session仍然有效而不用重新登錄,有兩步需要做,session持久 化很有用,尤其在eclipse中重新增加類后,tomcat重新加載后,IE頁(yè)面不用再登錄,之前的session依舊保持,調(diào)試的時(shí)候很有用
    1.配置conf/server.xml
    在server.xml的根路徑或虛擬目錄中增加一段,如虛擬目錄調(diào)度所中:
    <Context path="/dds" docBase="D:/01_XZY/98_供電局調(diào)度所/02_JSP/HRDGDZC/ROOT" debug="0" reloadable="true" >
    <Manager className="org.apache.catalina.session.PersistentManager" debug="0" saveOnRestart="true" maxActiveSessions="-1" minIdleSwap="-1" maxIdleSwap="-1" maxIdleBackup="-1" >
    <Store className="org.apache.catalina.session.FileStore" directory="seskep"/>
    </Manager>
    </Context>
    這樣之后,普通的session對(duì)象(像字符串類的)就能實(shí)現(xiàn)持久化了,但如果session使用了對(duì)象,則必須實(shí)現(xiàn)對(duì)象類的可序列化
    參數(shù)說(shuō)明:saveOnRestart-服務(wù)器關(guān)閉時(shí),是否將所有的session保存到文件中;
    maxActiveSessions-可處于活動(dòng)狀態(tài)的session數(shù);
    minIdleSwap/maxIdleSwap-session處于不活動(dòng)狀態(tài)最短/長(zhǎng)時(shí)間(s),sesson對(duì)象轉(zhuǎn)移到File Store中;
    maxIdleBackup-超過(guò)這一時(shí)間,將session備份。(-1表示沒(méi)有限制)
    directory="seskep"? 會(huì)在tomcat的work目錄下建立一個(gè)目錄,用來(lái)存儲(chǔ)session,這里建立的目錄是
    D:/JAVA/TOMCAT4/work/Standalone/localhost/dds/seskep

    2.類的序列化
    如果session中存儲(chǔ)了類xzy登錄屬性的實(shí)例,則類xzy登錄屬性必須能夠序列化,才能實(shí)現(xiàn)session持久化
    實(shí)現(xiàn)implements java.io.Serializable接口就可以了

    public class xzy登錄屬性 implements java.io.Serializable?
    {
    ? public String UserName=null;
    ? public String 姓名=null;
    ? public String 單位=null;
    ? public String 部門(mén)=null;
    ? public String 職務(wù)=null;
    ? public String 權(quán)限設(shè)置=null;
    ? static final public long serialVersionUID=372938;
    }
    serialVersionUID這個(gè)東西估計(jì)每個(gè)類中寫(xiě)個(gè)不同的數(shù)值就可以了,好像是版本保持的.
    經(jīng)過(guò)測(cè)試IE登錄后頁(yè)面后,Session再重新啟動(dòng),刷新IE頁(yè)面的時(shí)候session對(duì)象中的值能夠像重啟前一樣的讀出來(lái).


    總結(jié)?
    由此可以看出,session的管理是容器層做的事情,應(yīng)用層一般不會(huì)參與session的管理,也就是說(shuō),如果在應(yīng)用層獲取到相應(yīng)的 session,已經(jīng)是由tomcat提供的,因此如果過(guò)多的依賴session機(jī)制來(lái)進(jìn)行一些操作,例如訪問(wèn)控制,安全登錄等就不是十分的安全,因?yàn)槿?果有人能得到正在使用的jsessionid,則就可以侵入系統(tǒng)。

    轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/articles/11452811.html

    總結(jié)

    以上是生活随笔為你收集整理的tomcat架构分析 (Session管理)【转】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。