该代码只是用来学习原理的,有很多不完善之处。

代码:  git@github.com:sicw/EasySpringSession.git

一. 整体设置

1. 实现Filter,封装新的request请求

2. 在newRequest中重写getSession

3. 在getSession中,从redis获取session,或存储session到redis

二. 过滤器

1. 封装request

2. 执行完过滤器链之后要设置sessionId到cookie

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if(!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)){
throw new RuntimeException("just supports HTTP requests");
}
//封装新的请求
SessionRepositoryRequestWrapper newRequest = new SessionRepositoryRequestWrapper((HttpServletRequest) request, (HttpServletResponse) response);
try {
chain.doFilter(newRequest, response);
}finally {
//持久化session到redis
//设置sessionId到cookie
newRequest.commitSession();
}
}

三. 封装Request

封装Request的主要目的是重写他的getSession操作。

    public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
private HttpServletRequest httpServletRequest;
private HttpServletResponse httpServletResponse;
private HttpSession currentSession; public SessionRepositoryRequestWrapper(HttpServletRequest request,HttpServletResponse response) {
super(request);
httpServletRequest = request;
httpServletResponse = response;
} /**
* 在servlet中调用getSession会触发该方法
* @return session
*/
@Override
public HttpSession getSession() {
HttpSession session = getRequestedSession();
//没有sessionId或sessionId已过期
if(session == null){
session = sessionRepository.createSession();
}
currentSession = session;
return session;
} /**
* 持久化session
* 设置sessionId到Cookie
*/
public void commitSession(){
HttpSession session = currentSession;
sessionRepository.save(session);
String sessionId = session.getId();
httpSessionIdResolver.setSessionId(httpServletRequest, httpServletResponse, sessionId);
} private HttpSession getRequestedSession() {
//获取sessionId
List<String> sessionIds = httpSessionIdResolver.resolveSessionIds(httpServletRequest);
//获取session
for (String sessionId : sessionIds) {
HttpSession session = sessionRepository.findById(sessionId);
if(session != null){
return session;
}
}
return null;
}
}

四. 持久化session

这里持久化session使用的是spring-data-redis,把session通过HMSEt存储到redis中

    @Override
public RedisSession createSession() {
return new RedisSession();
}
@Override
public void save(HttpSession session) {
((RedisSession)session).saveDelta();
} @Override
public RedisSession findById(String id) {
return getSession(id, false);
} @Override
public void deleteById(String id) {
redisTemplate.delete(id);
}

五. 测试

1. 创建webapp

2. 实现SetServlet和GetServlet

@WebServlet("/get")
public class GetServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String key = req.getParameter("name");
HttpSession session = req.getSession();
String value = (String) session.getAttribute(key);
System.out.println("get ["+key+","+value+"]");
}
}
@WebServlet("/set")
public class SetServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String key = req.getParameter("name");
String value = req.getParameter("value");
HttpSession session = req.getSession();
session.setAttribute(key,value);
System.out.println("set ["+key+","+value+"]");
}
}

配置web.xml
因为我们的Filter需要设置属性,如果直接在这里配置Filter那么不能设置属性了。所以使用委派类DelegatingFilterProxy

他会根据Filter-Name去spring查找相应的bean,我们把Filter配置成bean就可以设置他的属性了。

<web-app>
<display-name>Archetype Created Web Application</display-name> <filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter> <filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 加载配置文件 -->
<context:property-placeholder location="classpath:redis.properties" /> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxTotal" value="${redis.maxTotal}" />
<property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
<property name="blockWhenExhausted" value="${redis.blockWhenExhausted}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean> <bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">
<property name="clusterNodes">
<set>
<bean id="redisNode1" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.host1}"/>
<constructor-arg name="port" value="${redis.port1}"/>
</bean>
<bean id="redisNode2" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.host2}"/>
<constructor-arg name="port" value="${redis.port2}"/>
</bean>
<bean id="redisNode3" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.host3}"/>
<constructor-arg name="port" value="${redis.port3}"/>
</bean>
<bean id="redisNode4" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.host4}"/>
<constructor-arg name="port" value="${redis.port4}"/>
</bean>
<bean id="redisNode5" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.host5}"/>
<constructor-arg name="port" value="${redis.port5}"/>
</bean>
<bean id="redisNode6" class="org.springframework.data.redis.connection.RedisNode">
<constructor-arg name="host" value="${redis.host6}"/>
<constructor-arg name="port" value="${redis.port6}"/>
</bean>
</set>
</property>
</bean> <!-- Spring-redis连接池管理工厂 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<constructor-arg name="clusterConfig" ref="redisClusterConfiguration"/>
<property name="timeout" value="${redis.timeout}" />
<property name="poolConfig" ref="poolConfig" />
<property name="usePool" value="true"/>
</bean> <!-- redis template definition -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
</bean> <bean id="redisSessionRepository" class="com.channelsoft.ccod.session.RedisSessionRepository">
<constructor-arg name="redisTemplate" ref="redisTemplate"/>
</bean>

  <!-- 在这里配置了Filter,并且设置属性 -->
<bean id="springSessionRepositoryFilter" class="com.channelsoft.ccod.filter.SessionRepositoryFilter">
<property name="sessionRepository" ref="redisSessionRepository"/>
</bean>
</beans>

redis.properties配置

#Common Config
redis.password=""
redis.maxIdle=400
redis.maxTotal=6000
redis.maxWaitMillis=1000
redis.blockWhenExhausted=true
redis.testOnBorrow=true
redis.timeout=100000
defaultCacheExpireTime=60 #Cluster Config
redis.host1=10.130.29.83
redis.port1=6379
redis.host2=10.130.29.83
redis.port2=6380
redis.host3=10.130.29.83
redis.port3=6381
redis.host4=10.130.29.83
redis.port4=6382
redis.host5=10.130.29.83
redis.port5=6383
redis.host6=10.130.29.83
redis.port6=6384

最后配置容器端口,比如Tomcat创建两个配置端口分别为8080,8081,启动容器。
通过url访问:
http://localhost:8080/set?name=k1&value=v1

http://localhost:8081/get?name=k1

一个简单的分布式session框架的更多相关文章

  1. 用go和zk实现一个简单的分布式server

    golang的zk客户端 最近打算写个简单的配置中心,考虑到实现便捷性,语言选择了go,由于其中计划用到zk,就调研了下golang的zk客户端,并实现了个简单的分布式server.最终找到了两个,地 ...

  2. 超详细,新手都能看懂 !使用SpringBoot+Dubbo 搭建一个简单的分布式服务

    来自:JavaGuide Github 地址:https://github.com/Snailclimb/springboot-integration-examples 目录: 使用 SpringBo ...

  3. Node.js简单介绍并实现一个简单的Web MVC框架

    编号:1018时间:2016年6月13日16:06:41功能:Node.js简单介绍并实现一个简单的Web MVC框架URL :https://cnodejs.org/topic/4f16442cca ...

  4. ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁

    作者:Grey 原文地址: ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 当多个进 ...

  5. Tornado自定义分布式session框架

    一.session框架处理请求执行的流程: 1.服务器端生成随机的cookie字符串 2.浏览器发送请求,服务器将cookie返回给浏览器. 3.服务器在生成一个字典.字典的key为cookie,va ...

  6. 从零构建一个简单的 Python Web框架

    为什么你想要自己构建一个 web 框架呢?我想,原因有以下几点: 你有一个新奇的想法,觉得将会取代其他的框架 你想要获得一些名气 你遇到的问题很独特,以至于现有的框架不太合适 你对 web 框架是如何 ...

  7. Directx11学习笔记【十一】 画一个简单的三角形--effect框架的使用

    这里不再介绍effect框架的具体使用,有关effect框架使用可参考http://www.cnblogs.com/zhangbaochong/p/5475961.html 实现的功能依然是画一个简单 ...

  8. 一个简单的通讯服务框架(大家发表意见一起研究)JAVA版本

    最近研究下java语言,根据一般使用的情况,写了个连接通讯服务的框架: 框架结构 C-Manager-S; 把所有通讯内容抽取成三个方法接口:GetData,SetData,带返还的Get; 所有数据 ...

  9. 一个简单的CPP处理框架

    好久没有在csdn上写过东西了,这么多年,一方面是工作忙,下班到家也没有开过电脑了,要陪小孩玩: 下面分享一段代码,是用CPP做的一个简单的消息(协议)处理框架: 是通过成员函数指针+map来实现的: ...

随机推荐

  1. .net实现md5加密 sha1加密 sha256加密 sha384加密 sha512加密 des加密解密

    写项目时,后台一直用md5加密,一天群里人问,除了MD5还有其它的加密方法吗?当时只知道还有个SHA,但怎么实现什么的都不清楚,于是当网上找了下,把几种常见的加密方法都整理了下,用winform写了个 ...

  2. Linux之搭建远程数据库[Ubuntu:全过程]

    1.mariadb在Linux中首次进入mysql (因为此时还没有创建任何用户,mysql的root并不等效于linux中的root用户) sudo apt-get install mysql-se ...

  3. 面试经验合集-Java后端<一>

    面试一:CDKHXJSYJS   时间:2018-12-29 周六 地点:航天科技大厦32楼   一 技术题目 <回忆版> 1.上下转型 2.Java异常:分类.处理.设计 3.二叉排序树 ...

  4. Notepad++ --v7.5.8 (64bit) 安装目录显示插件(Explorer)

    https://blog.csdn.net/qq_24153697/article/details/83036761 最近想自己做一个小项目,用Notepad做IDE,但是发现已安装的Notepad没 ...

  5. C#控件数组批量生成控件

    在编写C#窗体应用程序的时候,有时候需要生成好多个功能相似的同一种控件(比如数字键盘按键.单选框等),这时候使用窗体编辑器,费时费力,不便于修改.因此可以采用批量生成控件的形式. 以批量生成按钮为例 ...

  6. java反射常用类

    测试实体类 public class TestClass { public String classInfo; public String getClassInfo() { return classI ...

  7. Java的三大特性

    一.封装性 含义:对外不可见,保护属性和方法不被外部多看见 实现:通过关键字private声明,用get.set方法为外部访问. 引用的传递: static关键字:修饰属性(全局属性):修饰方法(直接 ...

  8. sessionStorage数组、对象的存储和读取

    一个对象的demo如下: var obj = { name:"name", age:18, love:"美女" } sessionStorage.setItem ...

  9. JAVA进阶9

    间歇性混吃等死,持续性踌躇满志系列-------------第9天 1.使用throw语句抛出异常 在通常情况下,程序发生错误时系统会自动抛出异常,而有时希望程序自动抛出异常,可以使用throw语句来 ...

  10. 51nod 1228、1258 序列求和

    这里一次讲两题...貌似都是板子? 所以两题其实可以一起做 [雾 noteskey 总之就是伯努利数的两道入门题啦,就是第二道有点鬼畜了,居然要任意模数的!(好吧是 1e9+7 但也没什么区别了) 伯 ...