改进基于filter的远程分布式session存储

本文介绍了一种利用Memcached存储HTTP会话的方案,并详细阐述了如何通过使用xmemcached客户端提高效率,以及如何采用CAS协议确保并发场景下数据的一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<!--StartFragment-->

 

参考了网上一些资料,实现了session通过filter存储到memcached服务器上.

(参见: http://ooft.javaeye.com/blog/537841 )

实现方式:

a)    通过MemcachedSessionFilter过滤器拦截cookie,取得的sessionId,通过sessionId构造新的HttpServletRequestWrapper对象,传给后面的应用.

b)    继承重构HttpServletRequestWrapper,HttpSessionWrapper类,覆盖原来和session存取相关的方法呢,都通过SessionService类来实现.

c)    SessionService连接memcached服务,以sessionId作为key,存取的对象是一个map.map的内容即为session的内容.

d)    读取数据时,先通过sessionId从memcached服务器上获取整个map对象,再以session的attributeName作为key从map中获取数据对象.写数据的方式与读数据类似,先获取map,然后修改map中的值,然后将整个map保存到memcached服务器上.

改进内容:

(1)    使用xmemcached客户端代替java memcached client.

XMemcached同样是基于java nio的客户端,java nio相比于传统阻塞io模型来说,有效率高(特别在高并发下)和资源耗费相对较少的优点。

 

(2)    对修改和删除session属性时,使用cas方法实现,防止在并发时出现数据覆盖的问题.

 

原来的代码:

Java代码 复制代码
  1. public void saveSession(String id, Map session) {   
  2.   
  3.  long s1= System.currentTimeMillis();   
  4.   
  5.  MemCachedClient mc = this.getMemCachedClient();   
  6.   
  7.  mc.replace(id, session);   
  8.   
  9.  System.out.print("saveSession");   
  10.   
  11.  System.out.println(System.currentTimeMillis() -s1);   
  12.   
  13. }  
public void saveSession(String id, Map session) {

 long s1= System.currentTimeMillis();

 MemCachedClient mc = this.getMemCachedClient();

 mc.replace(id, session);

 System.out.print("saveSession");

 System.out.println(System.currentTimeMillis() -s1);

}

 

 

 

  其中保存进去的session是一个通过get方法从memcached上获取的map对象,将map中的值修改以后,将map对象替换服务器上的对象.如果在多台机器并发的情况下,很可能出现一个线程写进去的数据被其它的覆盖.

 

改进后的代码:

   Memcached是通过cas协议实现原子更新,所谓原子更新就是compare and set,原理类似乐观锁,每次请求存储某个数据同时要附带一个cas值,memcached比对这个cas值与当前存储数据的cas值是否相等,如果相等就让新的数据覆盖老的数据,如果不相等就认为更新失败,这在并发环境下特别有用。

Java代码 复制代码
  1. public void saveSession(String id,String key, Object arg1) {   
  2.     long s1= System.currentTimeMillis();   
  3.     final String k=key;   
  4.     final Object o=arg1;   
  5.   
  6.     try {              
  7.            mc.cas(id,0,new CASOperation<Map>(){   
  8.         //尝试更新5次   
  9.         public int getMaxTries() {   
  10.             return 5;   
  11.         }                  
  12.         public Map getNewValue(long currentCAS, Map map) {   
  13.             map.put(k, o);   
  14.             return map;   
  15.         }});   
  16.            System.out.print("saveSession");        
  17.     }      
  18.     finally{   
  19.         System.out.println(System.currentTimeMillis() -s1);   
  20.     }   
  21. }  
### 使用 SpringCache 将 HTTP Session 数据存储到 Redis 的实现方法 为了将 HTTP Session 数据通过 SpringCache 存储到 Redis 中,可以利用 `Spring Session` 和 `Spring Cache` 提供的功能来完成这一目标。以下是具体的实现方式: #### 1. 添加依赖项 在项目中引入必要的 Maven 或 Gradle 依赖项以支持 Spring Data Redis 和 Spring Session。 ```xml <!-- Maven --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> ``` 上述依赖提供了对 Redis 缓存的支持以及基于 Redis 的会话管理功能[^2]。 --- #### 2. 配置 Redis 连接工厂 创建并配置用于连接 Redis 实例的工厂类。通常可以通过 `LettuceConnectionFactory` 来实现这一点。 ```java @Configuration public class RedisConfig { @Bean public LettuceConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory(); } } ``` 此部分负责建立与 Redis 的通信通道[^3]。 --- #### 3. 注入 RedisCacheManager 并初始化缓存管理器 通过注入 `RedisTemplate` 创建一个 `RedisCacheManager` 负责管理缓存数据的操作逻辑。 ```java import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @EnableCaching @Configuration public class CacheConfig { @Autowired private RedisTemplate<String, Object> redisTemplate; @Bean(name = "redisCacheManager") @Primary public RedisCacheManager redisCacheManager() { RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); cacheManager.setDefaultExpiration(3600); // 设置默认过期时间为 3600 秒 (可选) return cacheManager; } } ``` 这里启用了全局缓存机制并通过 `@EnableCaching` 开启注解驱动的方式使用缓存。 --- #### 4. 自定义 Spring Session 的 Redis 参数 调整 Spring Session 对应于 Redis 的行为参数,例如命名空间前缀、刷新模式等。 ```yaml spring: session: store-type: redis redis: namespace: myapp:sessions # 定义 Redis Key 的命名空间 flush-mode: IMMEDIATE # 不等待事务提交立即同步修改 save-mode: ALWAYS # 所有读写都会触发保存动作 ``` 这些 YAML 文件中的属性控制着如何处理 Redis 上的数据结构及其生命周期[^4]。 --- #### 5. Web 应用程序上下文中启用过滤器链路 最后一步是在 web.xml 或 Java Config 类里注册名为 `springSessionRepositoryFilter` 的 Servlet Filter 组件,从而拦截请求并将用户的会话状态持久化到远程数据库服务上。 ```xml <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> ``` 这段 XML 片段确保每次客户端访问服务器资源时都能自动关联对应的分布式会话记录[^5]。 --- ### 总结说明 以上步骤展示了完整的流程图谱,从基础环境搭建直至高级特性定制均有所涉及。最终达成的效果便是让应用程序能够高效地借助外部 NoSQL 解决方案维持跨设备间的一致性和高可用性需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值