本文共 3116 字,大约阅读时间需要 10 分钟。
前面提到的标准会话管理器已经提供了基础的会话管理功能,但在持久化方面做得还是不够,或者说在某些情景下无法满足要求,例如把会话以文件或数据库形式存储到存储介质中,这些都是标准会话管理器无法做到的,于是另外一种会话管理器被设计出来——持久化会话管理器。
在分析持久化会话管理器之前不妨先了解另外一个抽象概念会话存储设备Store,引入这个概念是为了更清晰方便地实现各种会话存储方式。作为存储设备最重要的操作无非就是读写操作,读即是将会话从存储设备加载到内存中,而写则将会话写入存储设备中,所以定义了两个重要的方法load和save与之相对应。FileStore和JDBCStore只要扩展Store接口各自实现load和save方法即可分别实现以文件或数据库形式存储会话。UML类图如下所示:
文件存储设备提供的是以文件形式保存会话,在写入时会针对每个会话生成一个文件用于保存此会话的相关信息,每个会话文件名被定义为sessionId+”.session”的格式,例如“326257DA6DB76F8D2E38F2C4540D1DEA.session”,而存放目录路径则由ServletContext.TEMPDIR变量指定,一般默认目录路径为”%CATALINA_HOME%/work/Catalina/localhost/web’name/”,其实就是”tomcat安装根目录+work+engineName+hostName+contextName”。所以假如有一万个会话则会有一万个会话文件。为了方便操作写入直接使用jdk自带的java.io.ObjectOutputStream对会话对象进行序列化并写入文件,所以有一点需要注意的是所有会话中的对象必须实现Serializable接口。
类似的,加载会话是通过传入一个sessionId,拼装成sessionId+”.session”格式的文件名去找对应的会话文件,然后使用jdk自带的java.io.ObjectInputStream将会话对象载入内存中,其实就是一个反序列化过程。
配置文件可以按如下配置:
<Store className="org.apache.catalina.session.FileStore" directory="sessiondir"/>
如果配置了directory,则将以”%CATALINA_HOME%/work/Catalina/localhost/web’name/sessiondir”为存放目录,当然如果配置为绝对路径则以你配置的绝对路径为存放目录。
以FileStore为存储设备使用时看起来在文件操作IO上效率相当低,因为对每个文件操作都是打开-操作-关闭,并未使用任何优化措施,所以tomcat在选择使用此方式时这里很可能会成为影响整体性能的一个点,必须要做好充分的性能测试。
JDBC存储设备提供的是以数据库形式存放会话,后端可以是任意厂商的数据库,只要有对应的数据库驱动程序即可。既然要存放数据肯定就要先在数据库中创建一张会话表,表的结构必须要tomcat与mysql双方约定好,例如tomcat默认的表名为”tomcat$sessions”,表字段一共有6个,分别为”app”、”id”、”data”、”valid”、”maxinactive”、”lastaccess”,app字段用于区分哪个web应用,id字段即会话标识,data字段用于存放会话对象字节串,valid字段表示此会话是否有效,maxinactive字段表示最大存活时间,lastaccess字段表示最后访问时间。其中需要注意的是data字段,由于它的大小直接影响会话对象的大小,所以需要根据实际设置它的类型,如果是mysql可以考虑设置为Blob(65k)或MediumBlob(16m)。
这样一来,会话的加载和保存其实就转化为对数据库的读写操作了,而获取数据库连接的逻辑是先判断tomcat容器中是否有数据源,如果有则从数据源中直接获取一条连接使用,但是如果没有的话则会自己通过驱动去创建连接,需要注意的是从数据源中获取的连接在使用完后会放回数据源中,但自己通过驱动创建的连接使用完则不会关闭,这个很好理解,因为数据源是一个池,重新获取连接很快,而自建的连接重新创建一般需要秒级别的消耗,明显会造成大问题。
下面以mysql数据库为例配置一个JDBC存储设备:
<Store className="org.apache.catalina.session.JDBCStore"
connectionURL="jdbc:mysql://localhost:3306/web_session?user=user&password=password"
driverName="com.mysql.jdbc.Driver"
sessionAppCol="app_name"
sessionDataCol="session_data"
sessionIdCol="session_id"
sessionLastAccessedCol="last_access"
sessionMaxInactiveCol="max_inactive"
sessionTable="tomcat_sessions"
sessionValidCol="valid_session" />
其中关于会话表及其字段的一些属性可以不必配置,直接采用tomcat默认的即可,但驱动程序及连接url则一定要配置。
以JDBCStore为存储设备时从表面看起来并不会有明显的IO性能问题,因为它使用数据源获取连接,是一种池化技术,就算不存在数据源也是采用长久连接模式,一般数据流不是非常大的话都不会存在性能问题。
整个介绍完存储设备store后接着看持久化会话管理器,其实持久化会话管理器主要实现的就是三种逻辑下的对会话进行持久化操作,①当会话对象数量超过指定阀值时则将超出的会话对象转换出(保存到store中并把内存中的此对象删除)到store中;②当会话空闲时间超过指定阀值时则将此会话对象换出;③当会话空闲时间超过指定阀值时则将此会话进行备份(保存到store中并且内存还存在此对象)。
实现上面的逻辑只需对所有会话集合进行遍历即可,把符合条件的通过store保存。由于有些会话被持久化到store中,所以通过id查找会话时需先从内存中查找再往store查找。
下面是一个配置例子,会话数大于1000时则将空闲时间大于60秒的会话转移到store直到会话数量控制在1000,超过120秒空闲的会话被换出到store,超过180秒空闲的会话将备份到store。
<Manager className="org.apache.catalina.session.PersistentManager"
maxActiveSessions="1000"
minIdleSwap="60"
maxIdleSwap="120"
maxIdleBackup="180">
<Store className="org.apache.catalina.session.FileStore" directory="sessiondir"/>
</Manager>
所以在了解了两种Store后对持久化会话管理器的实现原理机制就相当清楚了,其实就是提供两种会话保存方式并提供管理这些会话的操作,它提高了tomcat状态处理相关方面的容错能力得到提升。