简介
集群实现session共享目前主流共有以下几种方式,分别是 ip_hash,基于数据库的Session共享,基于缓存的Session共享,基于cookie的Session共享,以下是各个方式的优缺点。
tomcat-redis-session
认识tomcat
tomcat的这个单词的意思是“公猫”,因为它的开发者姆斯·邓肯·戴维森希望用一种能够自己照顾自己的动物代表这个软件,于是命名为tomcat,它的Logo兼吉祥物也被设计成了一只公猫形象。Tomcat是1999年Apache 软件基金会与Sun等其他公司一起合作的Jakarta(雅加达)项目中的一个子项目,作为服务器的容器支持基于Java语言编写的程序在服务器上运行,这样的程序被称为Servlet,因为它是运行在“Server”上的“Applet(Applet是采用Java编程语言编写的小应用程序)”。理论上讲这样一个容器并不是一个完整的服务器软件,因为它只能运行Java程序而不能生成HTML页面数据,也不能处理并发事务。但它集成了HTTP服务器程序,也就可以单独作为一个服务器软件来部署以处理HTTP请求,但tomcat核心技术并不在于此,所以除了用于开发过程中的调试以及那些对速度和事务处理只有很小要求的用户,很少会将Tomcat单独作为Web服务器。通常开发者会让tomcat与其他对Web服务器一起协同工作,比如Apache HTTP Server。Apache HTTP Server负责接受所有来自客户端的HTTP请求,然后将Servlets和JSP的请求转发给Tomcat来处理。Tomcat完成处理后,将响应传回给Apache,最后Apache将响应返回给客户端。于是在tomcat中运行Java程序也就是Servlet的那个模块因为体现了tomcat最核心特点而引起了大家的重视,而这个模块的名字叫做Catalina。
Catalina是美国西海岸靠近洛杉矶22英里的一个小岛,因为其风景秀丽而著名。Servlet运行模块的最早开发者Craig McClanahan因为喜欢Catalina岛故以Catalina命名他所开这个模块,尽管他从来也没有去过那里。另外在开发的早期阶段,Tomcat是被搭建在一个叫Avalon的服务器框架上,而Avalon则是Catalina岛上的一个小镇的名字,于是想一个与小镇名字相关联的单词也是自然而然。还有一个原因来自于Craig McClanahan养的猫,他养的猫在他写程序的时候喜欢在电脑周围闲逛。但这与Catalina有什么关系呢?我想可能是Catalina岛是个悠闲散步的好地方,猫的闲逛让Craig McClanahan想起了那里。
tomcat 系统架构
一、Tomcat顶层架构
先上一张Tomcat的顶层结构图(图A),如下:
tomcat中最顶层的容器是Server,代表着整个服务器,从上图中可以看出,一个Server可以包含至少一个Service,用于具体提供服务。
Service主要包含两个部分:Connector和Container。从上图中可以看出 Tomcat 的心脏就是这两个组件,他们的作用如下:
1、Connector用于处理连接相关的事情,并提供Socket与Request和Response相关的转化; 2、Container用于封装和管理Servlet,以及具体处理Request请求;
一个Tomcat中只有一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但是可以有多个Connectors,这是因为一个服务可以有多个连接,如同时提供Http和Https链接,也可以提供向相同协议不同端口的连接,示意图如下(Engine、Host、Context下边会说到):
多个 Connector 和一个 Container 就形成了一个 Service,有了 Service 就可以对外提供服务了,但是 Service 还要一个生存的环境,必须要有人能够给她生命、掌握其生死大权,那就非 Server 莫属了!所以整个 Tomcat 的生命周期由 Server 控制。
另外,上述的包含关系或者说是父子关系,都可以在tomcat的conf目录下的server.xml
配置文件中看出,下图是删除了注释内容之后的一个完整的server.xml
配置文件(Tomcat版本为8.0)
详细的配置文件文件内容可以到Tomcat官网查看:http://tomcat.apache.org/tomcat-8.0-doc/index.html
上边的配置文件,还可以通过下边的一张结构图更清楚的理解:
二、Tomcat顶层架构小结:
(1)Tomcat中只有一个Server,一个Server可以有多个Service,一个Service可以有多个Connector和一个Container;
(2) Server掌管着整个Tomcat的生死大权;
(4)Service 是对外提供服务的;
(5)Connector用于接受请求并将请求封装成Request和Response来具体处理;
(6)Container用于封装和管理Servlet,以及具体处理request请求;
知道了整个Tomcat顶层的分层架构和各个组件之间的关系以及作用,对于绝大多数的开发人员来说Server和Service对我们来说确实很远,而我们开发中绝大部分进行配置的内容是属于Connector和Container的,所以接下来介绍一下Connector和Container。
三、Connector和Container的微妙关系
由上述内容我们大致可以知道一个请求发送到Tomcat之后,首先经过Service然后会交给我们的Connector,Connector用于接收请求并将接收的请求封装为Request和Response来具体处理,Request和Response封装完之后再交由Container进行处理,Container处理完请求之后再返回给Connector,最后在由Connector通过Socket将处理的结果返回给客户端,这样整个请求的就处理完了!
Connector最底层使用的是Socket来进行连接的,Request和Response是按照HTTP协议来封装的,所以Connector同时需要实现TCP/IP协议和HTTP协议!
Tomcat既然处理请求,那么肯定需要先接收到这个请求,接收请求这个东西我们首先就需要看一下Connector!
四、Connector架构分析
Connector用于接受请求并将请求封装成Request和Response,然后交给Container进行处理,Container处理完之后在交给Connector返回给客户端。
因此,我们可以把Connector分为四个方面进行理解:
(1)Connector如何接受请求的?
(2)如何将请求封装成Request和Response的?
(3)封装完之后的Request和Response如何交给Container进行处理的?
(4)Container处理完之后如何交给Connector并返回给客户端的?首先看一下Connector的结构图(图B),如下所示:
其中ProtocolHandler由包含了三个部件:Endpoint、Processor、Adapter。
(1)Endpoint用来处理底层Socket的网络连接,Processor用于将Endpoint接收到的Socket封装成Request,Adapter用于将Request交给Container进行具体的处理。
(2)Endpoint由于是处理底层的Socket网络连接,因此Endpoint是用来实现TCP/IP协议的,而Processor用来实现HTTP协议的,Adapter将请求适配到Servlet容器进行具体的处理。
(3)Endpoint的抽象实现AbstractEndpoint里面定义的Acceptor和AsyncTimeout两个内部类和一个Handler接口。Acceptor用于监听请求,AsyncTimeout用于检查异步Request的超时,Handler用于处理接收到的Socket,在内部调用Processor进行处理。
至此,我们应该很轻松的回答(1)(2)(3)的问题了,但是(4)还是不知道,那么我们就来看一下Container是如何进行处理的以及处理完之后是如何将处理完的结果返回给Connector的?
五、Container架构分析
Container用于封装和管理Servlet,以及具体处理Request请求,在Connector内部包含了4个子容器,结构图如下(图C):
(1)Engine:引擎,用来管理多个站点,一个Service最多只能有一个Engine;
(2)Host:代表一个站点,也可以叫虚拟主机,通过配置Host就可以添加站点;
(3)Context:代表一个应用程序,对应着平时开发的一套程序,或者一个WEB-INF目录以及下面的web.xml文件;
(4)Wrapper:每一Wrapper封装着一个Servlet;
六、Container如何处理请求的
Container处理请求是使用Pipeline-Value管道来处理的!
Pipeline-Value是责任链模式,责任链模式是指在一个请求处理的过程中有很多处理者依次对请求进行处理,,处理完之后每个处理者负责做自己相应的处理将处理后的请求返回,再让下一个处理着继续处理。
我们知道Container包含四个子容器,而这四个子容器对应的BaseValue分别在:StandardEngineValue、StandardHostValue、StandardContextValue、StandardWrapperValue。
Pipeline的处理流程图如下(图D):
(2)在Engine的管道中依次会执行EngineValue1、EngineValue2等等,最后会执行StandardEngineValue,在StandardEngineValue中会调用Host管道,然后再依次执行Host的HostValue1、HostValue2等,最后在执行StandardHostValue,然后再依次调用Context的管道和Wrapper的管道,最后执行到StandardWrapperValue。
(3)当执行到StandardWrapperValue的时候,会在StandardWrapperValue中创建FilterChain,并调用其doFilter方法来处理请求,这个FilterChain包含着我们配置的与请求相匹配的Filter和Servlet,其doFilter方法会依次调用所有的Filter的doFilter方法和Servlet的service方法,这样请求就得到了处理!
(4)当所有的Pipeline-Value都执行完之后,并且处理完了具体的请求,这个时候就可以将返回的结果交给Connector了,Connector在通过Socket的方式将结果返回给客户端。
tomcat-redis-session使用
1、开源项目地址:
https://github.com/jcoleman/tomcat-redis-session-manager
2、下载代码之后需要进行重新编译,生成所需要的jar,任意创建maven项目,将src下的代码拷贝到具体位置,如下:
3、然后打开terminal,执行mvn clean 和mvn install 将编译好的代码打包为:tomcat-redis-session-1.0-SNAPSHOT.jar
4、将**tomcat-redis-session-1.0-SNAPSHOT.jar、jedis-2.7.2.jar、commons-pool2-2.0.jar** 三个jar包分别放在tomcat1和tomcat2实例下的lib目录下。
5、修改tomcat实例下conf/contex.xml文件
<?xml version='1.0' encoding='utf-8'?> <Context><WatchedResource>WEB-INF/web.xml</WatchedResource><!-- tomcat-redis-session共享配置 --><Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" /><Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"host="192.168.1.149"port="6379"database="0"maxInactiveInterval="60" /> </Context>
tomcat-redis-session原理
一、概要原理
tomcat-redis-session-manager是一个对用户完全透明的分布式session存储框架,用户只需要在tomcat中进行简单的配置,就可以使用,我们的业务代码是完全和单实例的时候的代码是一样的的,也就是写代码的时候完全不用担心你写的是一个多tomcat实例的代码,完全透明。
如何对框架的原理进行简单的理解,我们首先要知道,在请求过程中的session操作,首先要解析请求中的sessionId信息,然后将sessionId存储到request的参数列表中。然后再从 request获取session的时候,如果存在sessionId那么就根据Id从session池中获取session,如果sessionId不存在或者session失效,那么则新建session并且将session信息放入session池,供下次使用。
如果我们想自己写一个类似于tomcat-redis-session-manager的项目,我们应该知道Tomcat的Session管理机制,在默认的情况下Tomcat的Session管理,如果不进行设置的话是由Tomcat自带的StandardManager类进行控制的,我们可以根据这个类自定义一个Manager,**主要重写的就是org.apache.catalina.session.ManagerBase里边的具体写的操作**,这也是tomcat-redis-session-manager的基本原理,将tomcat的session存储位置指向了Redis
二、详细原理
本文目的在介绍tomcat中session相关的架构以及session的查询。
在Servlet开发中,Session代表用户会话,开发人员经常使用Session来临时存储一些信息,那么Session到底是什么,Tomcat中是如何对Session进行管理的,我们今天到源码中查看下。
查看相关资料,我们先看下Session
相关的类图
从图上可以看到Session对应的接口有两个Session
和HttpSession
,StandardSession
实现了这2个接口,StandardSessionFacade
实现了HttpSession
,StandardSessionFacede
包含了StandardSession
。看起来有点饶,我们详细讲解下每个类的含义。
- HttpSession
我们都知道Tomcat是支持Servlet规范的web容器,所以内部会包含一些Servlet的内容。HttpSession
就是在Servlet规范中标准的Session定义。注意HttpSession
的全路径是javax.servlet.http.HttpSession
- Session
Session
接口是Catalina内部的Session的标准定义。全路径org.apache.catalina.Session
- StandardSession
Catalina内部Session
接口的标准实现,基础功能的提供类,需要重点分析,全路径org.apache.catalina.session.StandardSession
- StandardSessionFacade
StandardSession
的外观类,也是Catalina内部的类,之所以存在这个类是因为不想直接让开发人员操作到StandardSession
,这样可以保证类的安全。该类的全路径org.apache.catalina.session.StandardSession
。
在Catalina中,Session由Session管理器组件负责管理,例如创建和销毁Session管理器是org.apache.catalina.Manager
接口的实例。我们来看看Manager
管理器相关的类图。
Manager
Catalina中session管理器概念的顶层接口。其中定义了一些对session的基本操作,如创建,查找,添加,移除以及对特定session对象的属性的修改。除了基本操作以外还定义了一些特殊的例如session的序列化反序列化等等。
ManagerBase
抽象类,对Manager
接口作了基本实现,方便继承类的复写以及实现,就像其他xxxBase
类起到了一样的作用。
StandardManager
Catalina中默认的Session管理器的实现类,具体功能我们下面分析。
PersistentManagerBase
Session管理器中打标持久化Session的父类,虽然StandardManager
也可以将Session持久化,但是只是将Session持久化为一个文件(后面会说到),PersistentManagerBase
类和StandardManager
类的区别在于前者的存储器的表现形式可以有多种,比如数据库,文件等(具体后面讨论)
PersistentManager
在PersistentManager
基础上增加了两个属性。
DistributedManager
从PersistentManagerBase
和BackupManager
类中抽象出来的一个接口,这个接口表示了两者一个共同的属性:不会持有所有session的对象,但是能找到所有的session。例如PersistentManagerBase
可以将session同时存放在内存和持久化介质中。
ClusterManager
分布式集群session处理器父类,定义了一些基本方法例如获取所有tomcat集群的机器,获取其他集群的信息等基本功能。
ClusterManagerBase
抽象类,对ClusterManager
作了基本实现。
BackupManager
集群间session复制策略的一种实现,会话数据只有一个备份节点,这个备份节点的位置集群中所有节点都可见。
DeltaManager
集群建session复制策略的一种实现,采用的方式是只复制差异部分,是分布式集群session同步中最好的同步方式。
了解一些基本信息后,我们来查看我们最常用的getSession()
方法,从而来了解session运作的方式。
在Servlet中我们使用HttpServletRequest
的getSession()
方法来获取session对象,而真正执行getSession
方法的其实是org.apache.catalina.connector.RequestFacade
对象
public class RequestFacade implements HttpServletRequest
RequestFacade
对象实现了HttpServletRequest
内部封装了org.apache.catalina.connector.Request
对象,我们查看getSession()
方法:
@Override
public HttpSession getSession() {
if (request == null) {
throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
}
return getSession(true);
}
@Override
public HttpSession getSession(boolean create) {
if (request == null) {
throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
}
if (SecurityUtil.isPackageProtectionEnabled()){
return AccessController.doPrivileged(new GetSessionPrivilegedAction(create));
} else {
return request.getSession(create);
}
}
可以看出最终调用的还是org.apache.catalina.connector.Request
对象的getSession()
方法,源码如下:
@Override
public HttpSession getSession(boolean create) {
Session session = doGetSession(create);
if (session == null) {
return null;
}
return session.getSession();
}
下面我们看doGetSession()
方法,由于源码过长,删减了部分内容,保留了核心代码。
protected Session doGetSession(boolean create) {
//判断context
if (context == null) {
return (null);
}
//判断session是否有效
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
return (session);
}
//获取跟context绑定的sessionManager 这里默认返回的是StandardManager
Manager manager = context.getManager();
if (manager == null) {
return null; // Sessions are not supported
}
if (requestedSessionId != null) {
try {
//根据requestSessionId 查询指定的session
//111111111111
session = manager.findSession(requestedSessionId);
} catch (IOException e) {
session = null;
}
//判断获取到的session是否有效
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
//session有效 增加访问时间和次数
session.access();
return (session);
}
}
//如果没有找到已存在的session并且 要求创建新session
//获取此次request对应的sessionid
String sessionId = getRequestedSessionId();
//。。。略部分代码
//222222222222
session = manager.createSession(sessionId);
//创建cookie
Cookie cookie =ApplicationSessionCookieConfig.createSessionCookie(context, session.getIdInternal(),isSecure());
//将cookie设置到response 中
response.addSessionCookieInternal(cookie);
if (session == null) {
return null;
}
//增加访问时间和次数
session.access();
return session;
}
代码很简单,思路也很清晰,很容易理解,这里有3处需要关注下。
在代码1的地方,调用了manager.findSession()
,requestedSessionId
是个String类型的字符串(稍后说如何创建一个这样的字符串)。在Catalina中默认的是StandardManager
,所以查看StandardManager
的findSession()
方法,最后在其父类ManagerBase
中找到相关方法:
@Override
public Session findSession(String id) throws IOException {
if (id == null) {
return null;
}
return sessions.get(id);
}
/**
* The set of currently active Sessions for this Manager, keyed by
* session identifier.
*/
protected Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();
从源码可以得出结论,一个类型的session管理器,他都有一个总的session容器,就是一个ConcurrentHashMap
实例,key是sessionId
,value是对应的session对象。
在代码2的地方由于在已有的session的map中没有找到session,所以需要创建一个新的session,createSession()
源码如下(在ManagerBase
类中):
@Override
public Session createSession(String sessionId) {
//判断可容纳session数量
if ((maxActiveSessions >= 0) &&
(getActiveSessions() >= maxActiveSessions)) {
rejectedSessions++;
throw new TooManyActiveSessionsException(
sm.getString("managerBase.createSession.ise"),
maxActiveSessions);
}
//创建一个全新的session对象
Session session = createEmptySession();
//设置基本属性
session.setNew(true);
session.setValid(true);
session.setCreationTime(System.currentTimeMillis());
session.setMaxInactiveInterval(((Context) getContainer()).getSessionTimeout() * 60);
String id = sessionId;
//设置sessionId 唯一标识负
if (id == null) {
//如果id为空 新生成一个sessionId
id = generateSessionId();
}
session.setId(id);
//session数量+1
sessionCounter++;
SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
synchronized (sessionCreationTiming) {
sessionCreationTiming.add(timing);
sessionCreationTiming.poll();
}
return (session);
}
需要关注下创建新的session对象其实就是new 了一个StandardSession
对象,还有generateSessionId()
方法是生成一个唯一的sessionId,有兴趣的可以自行查看。最后在代码session.setId(id)
中往下查看可以看到:
sessions.put(session.getIdInternal(), session);
也就是把新创建的session放入到管理器的session容器中(ConcurrentHashMap)对象。
在doGetSession
方法中最后需要关注的就是requestedSessionId
是如何生成的,我们先看下定义
/**
* The requested session ID (if any) for this request.
*/
protected String requestedSessionId = null;
可以看出是个字符串类型的标识符并且和request有关,由于tomcat的请求流程我们还没梳理到,所以这里直接给出生成的方法。
首先,这个所谓的requestedSessionId
对应的就是我们每次请求都有一个唯一session标识符,为了以后同一个用户再次请求的时候可以使用同一个session(会话)。既然跟request有关,那么就可以查看request相关方法,而之前在讲解启动的时候提到过Connector
是专门来处理请求的,而对应http请求的Connector
是Http11Processor
。首先用户发送一个http请求传递给Http11Processor
,经由Http11Processor
解析封装在 org.apache.coyote.Request
然后传递给CoyoteAdapter
,coyoteAdapter
是一个适配器,将coyote框 架封装的org.apache.coyote.Request
适配给org.apache.catalina.connector.Request
,而解析的过程就在CoyoteAdapter
中。
解析requestSessionId
分为两步,一个是从请求url中解析,另一步是从cookie中解析,之所以需要解析两次是因为有的用户会禁用cookie,而禁用cookie的时候sessionId则会带在请求url中(其实就是我们熟知的jsessionId
)
从请求url中解析(只保留了核心代码)
首先如果sessionId保留在url中是以如下形式
http://xxx.com?a=1&b=1&c=1;k1=v1;k2=v2;jsessionId=xxxx
可以看出requestSessionId
是保存在;
之后的,之所以解释下 是方便理解。
代码比较简单,以上就是requestSessionId
是如何设置的。
看到这里,doGetSession()
方法我们就看完了,可以看到它返回的是一个StandardSession
对象,最后在getSession()
方法中返回的是StandardSession
对象的getSession()
方法,查看下源码。
@Override
public HttpSession getSession() {
Session session = doGetSession(true);
if (session == null) {
return null;
}
return session.getSession();
}
/**
* Return the <code>HttpSession</code> for which this object
* is the facade.
*/
@Override
public HttpSession getSession() {
if (facade == null){
if (SecurityUtil.isPackageProtectionEnabled()){
final StandardSession fsession = this;
facade = AccessController.doPrivileged(
new PrivilegedAction<StandardSessionFacade>(){
@Override
public StandardSessionFacade run(){
return new StandardSessionFacade(fsession);
}
});
} else {
facade = new StandardSessionFacade(this);
}
}
return (facade);
}
可以看出最后getSession()
方法返回的并不是StandardSession
对象,而是StandardSession
对象的外观类StandardSessionFacade
。这也和我们一开始介绍session类图的时候描述的一致。
spring session
spring session使用
Spring Session提供了一套创建和管理Servlet HttpSession的方案。Spring Session提供了集群Session(Clustered Sessions)功能,默认采用外置的Redis来存储Session数据,以此来解决Session共享的问题。
一、特性
Spring Session提供以下特性:
API和用于管理用户会话的实现;
HttpSession - 允许以应用程序容器(即Tomcat)中性的方式替换HttpSession;
Clustered Sessions - Spring Session让支持集群会话变得不那么繁琐,并且不和应用程序容器金习性绑定到。
Multiple Browser Sessions - Spring会话支持在单个浏览器实例中管理多个用户的会话。
RESTful APIs - Spring Session允许在headers 中提供会话ID以使用RESTful API。
二、基于XML配置方式的Spring Session案例实现
基于SSM框架的一个小案例,Git OS项目代码地址:http://git.oschina.net/xuliugen/spring-session-demo
项目展示:
(1)基本环境需求
进行使用Spring Session的话,首先的是已经安装好的有一个 Redis服务器!
(2)添加项目依赖(最基本的依赖使用)
添加了必要的依赖之后,我们需要创建相应的Spring配置。Spring配置是要创建一个Servlet过滤器,它用Spring Session支持的HttpSession实现来替换容器本身HttpSession实现。这一步也是Spring Session的核心。
因此,如果有自己的Redis配置,请修改,例如下边的配置:
添加如下配置让Spring Session不再执行config命令:
(6)Spring MVC controller代码用于测试:
访问链接:http://localhost:8080/spring/session/setSession.do?name=test&value=123456
spring session原理
我们知道Tomcat再启动的时候首先会去加载web.xml
文件,Tomcat启动的时候web.xml
被加载的顺序:context-param -> listener -> filter -> servlet
。
我们在使用Spring Session的时候,我们配置了一个filter,配置代码如下:
DelegatingFilterProxy
这个类:
DelegatingFilterProxy
类将通过springSessionRepositoryFilter
这个名称去查找Spring容器中配置的Bean并将其转换为过滤器,对于调用DelegatingFilterProxy
的每个请求,将调用springSessionRepositoryFilter这个过滤器。
如果未指定init-param
参数的话,DelegatingFilterProxy
就会把filter-name
作为要查找的Bean对象,这也是DelegatingFilterProxy
类的作用。可以看出每一个请求都会经过该filter,经过该filter的请求也会相应的经过springSessionRepositoryFilter
这个过滤器,那么我们就接着看一下springSessionRepositoryFilter
这个过滤器。
springSessionRepositoryFilter过滤器的创建
RedisHttpSessionConfiguration 这个类加了Configuration注解,作为配置文件注入。
RedisHttpSessionConfiguration的作用是创建名为springSessionRepositoryFilter 的Spring Bean,继承自Filter。springSessionRepositoryFilter替换容器默认的HttpSession支持为Spring Session,将Session实例存放在Redis中。
(1)RedisHttpSessionConfiguration 继承关系如下:
(3)RedisHttpSessionConfiguration通过@Bean的方式将RedisMessageListenerContainer、RedisTemplate、RedisOperationsSessionRepository
等注入到Spring容器中。
(4)RedisHttpSessionConfiguration继承了SpringHttpSessionConfiguration这个类,这个类很重要,SpringHttpSessionConfiguration通过@Bean的方式将springSessionRepositoryFilter注入到容器中:
pringSessionRepositoryFilter这个过滤器就是前边DelegatingFilterProxy查找的过滤器!
(6)可以看出他是SessionRepositoryFilter类型的,SessionRepositoryFilter的作用就是替换容器默认的javax.servlet.http.HttpSession支持为org.springframework.session.Session。
SessionRepositoryFilter的主要方法和属性如下:
(8)因为SessionRepositoryRequestWrapper继承了HttpServletRequestWrapper,而HttpServletRequestWrapper实现了HttpServletRequest接口,在SessionRepositoryRequestWrapper又重写了HttpServletRequest接口中的一些方法,所以才会有:getSession、changeSessionId等这些方法。
到此,我们应该大致明白了,原有的request请求和response都被重新进行了包装。我们也就明白了原有的HttpSeesion是如何被Spring Session替换掉的。
tomcat-redis-session与 spring session 比较
名称 | 优点 | 缺点 | 总结 |
tomcat-redis-session | 目前掌上医讯使用的都是tomcat容器,修改起来比较方便 | 配置相对还是有一点繁琐的,需要人为的去修改Tomcat的配置,需要耦合Tomcat等Servlet容器的代码 | 结合,掌上医讯现有项目和环境来说,tomcat-redis-session,实现起来比较容易,也比较方便, 但从长远角度考虑,使用spring session比较好,优点比较突出,但是需要调研,jfinal项目与之是否存在jar冲突的可能性。 |
spring session | spring Session不依赖于Servlet容器,而是Web应用代码层面的实现,直接在已有项目基础上加入spring Session框架来实现Session统一存储在Redis中。如果你的Web应用是基于Spring框架开发的,只需要对现有项目进行少量配置,即可将一个单机版的Web应用改为一个分布式应用,由于不基于Servlet容器,所以可以随意将项目移植到其他容器。 | 对于基于spring项目,配置起来比较方便,因为该方案是基于spring实现的,而对于jfinal项目jar包是否存在冲突还有待于进一步调研。 |