如何使用Spring+redis实现对session的分布式管理

在Spring中实现分布式 session管理

本文主要是在Spring中实现分布式session,采用redis对session进行持久化管理,这样当应用部署的时候,不需要在Resin、Tomcat等容器里面进行分布式配置,方便加入新的节点服务器进行集群扩容,session不依赖各节点的服务器,可直接从redis获取。下面是功能的核心代码:

一、首先在web.xml里面配置

加入拦截器:

<!-- 分布式session start -->
  <filter>
    <filter-name>distributedSessionFilter</filter-name>
    <filter-class>DistributedSessionFilter</filter-class>
    <init-param>
      <!-- 必填,密钥.2种方式,1对应为bean,格式为bean:key。2字符串,格式如:afffrfgv-->
      <param-name>key</param-name>
      <param-value>xxxxxxxx</param-value>
    </init-param>
    <init-param>
      <!-- 必填,redis对应的bean,格式为bean:xx-->
      <param-name>cacheBean</param-name>
      <param-value>bean:redisPersistent</param-value>//DistributedBaseInterFace,对应于此接口,进行session的持久化操作
    </init-param>
    <init-param>
      <!-- 必填, -->
      <param-name>cookieName</param-name>
      <param-value>TESTSESSIONID</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>distributedSessionFilter</filter-name>
    <url-pattern>*.do</url-pattern>
  </filter-mapping>
  <!-- 分布式session end -->

二、拦截器的实现,核心代码如下

主要有以下的几个类:

  1. DistributedSessionFilter,
  2. DistributedSessionManager,
  3. DistributedHttpSessionWrapper,
  4. DistributedHttpServletRequestWrapper

1、DistributedSessionFilter实现Filter:

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

public class DistributedSessionFilter implements Filter {
  private static final Logger log = LoggerFactory.getLogger(DistributedSessionFilter.class);

  private String cookieName;

  //主要是对session进行管理的操作
  private DistributedSessionManager distributedSessionManager;

  private String key;
}

容器启动时候的初始化方法:

@Override
  public void init(FilterConfig config) throws ServletException {
    WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(config
        .getServletContext());
    String key = config.getInitParameter("key");
    String cookieName = config.getInitParameter("cookieName");
    String cacheBean = config.getInitParameter("cacheBean");
    // 获取bean的名称,配置是"bean:"
    String redisBeanStr = cacheBean.substring(5);
    DistributedBaseInterFace distributedCache = (DistributedBaseInterFace) wac.getBean(redisBeanStr);

    // 获取key,有2种配置方式,1对应为bean,格式为bean:key。2字符串
    if (key.startsWith("bean:")) {
      this.key = (String) wac.getBean(key.substring(5));
    } else {
      this.key = key;
    }
    this.cookieName = cookieName;
    this.distributedSessionManager = DistributedSessionManager.getInstance(distributedCache);

    //异常处理省略。。。
  }

进行实际的请求拦截:

  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
      throws ServletException, IOException {
    DistributedHttpServletRequestWrapper distReq = null;
    try {
      //请求处理
      distReq = createDistributedRequest(servletRequest, servletResponse);
      filterChain.doFilter(distReq, servletResponse);
    } catch (Throwable e) {
      //省略。。。
    } finally {
      if (distReq != null) {
        try {
          //处理完成request后,处理session(主要是保存session会话)
          dealSessionAfterRequest(distReq.getSession());
        } catch (Throwable e2) {
          //省略。。。
        }
      }
    }
  }

  //分布式请求
  private DistributedHttpServletRequestWrapper createDistributedRequest(ServletRequest servletRequest,
      ServletResponse servletResponse) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    String userSid = CookieUtil.getCookie(cookieName, request);
    String actualSid = distributedSessionManager.getActualSid(userSid, request, key);
    if (StringUtil.isBlank(actualSid)) {
      if (StringUtil.isNotBlank(userSid)) {
        log.info("userSid[{}]验证不通过", userSid);
      }
      // 写cookie
      String[] userSidArr = distributedSessionManager.createUserSid(request, key);
      userSid = userSidArr[0];
      CookieUtil.setCookie(cookieName, userSid, request, response);
      actualSid = userSidArr[1];
    }
    actualSid = "sid:" + actualSid;
    DistributedHttpSessionWrapper distSession = null;
    try {
      Map<String, Object> allAttribute = distributedSessionManager.getSession(actualSid, request.getSession()
          .getMaxInactiveInterval());
      distSession = new DistributedHttpSessionWrapper(actualSid, request.getSession(), allAttribute);
    } catch (Throwable e) {
      // 出错,删掉缓存数据
      log.error(e.getMessage(), e);
      Map<String, Object> allAttribute = new HashMap<String, Object>();
      distSession = new DistributedHttpSessionWrapper(actualSid, request.getSession(), allAttribute);
      distributedSessionManager.removeSession(distSession);
    }
    DistributedHttpServletRequestWrapper requestWrapper = new DistributedHttpServletRequestWrapper(request,
        distSession);
    return requestWrapper;

  }

  // request处理完时操作session
  private void dealSessionAfterRequest(DistributedHttpSessionWrapper session) {
    if (session == null) {
      return;
    }
    if (session.changed) {
      distributedSessionManager.saveSession(session);
    } else if (session.invalidated) {
      distributedSessionManager.removeSession(session);
    } else {
      distributedSessionManager.expire(session);
    }
  }

2、DistributedSessionManager,主要处理分布式session,核心代码:

class DistributedSessionManager {
  protected static final Logger log = LoggerFactory.getLogger(DistributedSessionManager.class);

  private static DistributedSessionManager instance = null;

  //redis处理session的接口,自己根据情况实现
  private DistributedBaseInterFace distributedBaseInterFace;

  private static byte[] lock = new byte[1];

  private DistributedSessionManager(DistributedBaseInterFace distributedBaseInterFace) {
    this.distributedBaseInterFace = distributedBaseInterFace;
  }

  public static DistributedSessionManager getInstance(DistributedBaseInterFace redis) {
    if (instance == null) {
      synchronized (lock) {
        if (instance == null) {
          instance = new DistributedSessionManager(redis);
        }
      }
    }
    return instance;
  }

  //获取session
  public Map<String, Object> getSession(String sid,int second) {
    String json = this.distributedBaseInterFace.get(sid,second);
    if (StringUtil.isNotBlank(json)) {
      return JsonUtil.unserializeMap(json);
    }
    return new HashMap<String, Object>(1);
  }

  //保存session
  public void saveSession(DistributedHttpSessionWrapper session) {
    Map<String, Object> map=session.allAttribute;
    if(MapUtil.isEmpty(map)){
      return;
    }
    String json = JsonUtil.serializeMap(map);
    this.distributedBaseInterFace.set(session.getId(), json, session.getMaxInactiveInterval());
  }

  //删除session
  public void removeSession(DistributedHttpSessionWrapper session) {
    distributedBaseInterFace.del(session.getId());
  }

  public void expire(DistributedHttpSessionWrapper session) {
    distributedBaseInterFace.expire(session.getId(), session.getMaxInactiveInterval());
  }

  /**
   * 创建cookie的sid
   */
  public String[] createUserSid(HttpServletRequest request, String key) {
    //...
  }

  public String getActualSid(String userSid, HttpServletRequest request, String key) {
    //...
  }
}

3、DistributedHttpSessionWrapper 实现了 HttpSession,进行分布式session包装,核心代码:

public class DistributedHttpSessionWrapper implements HttpSession {

  private HttpSession orgiSession;

  private String sid;

  boolean changed = false;

  boolean invalidated = false;

  Map<String, Object> allAttribute;

  public DistributedHttpSessionWrapper(String sid, HttpSession session, Map<String, Object> allAttribute) {
    this.orgiSession = session;
    this.sid = sid;
    this.allAttribute = allAttribute;
  }

  @Override
  public String getId() {
    return this.sid;
  }

  @Override
  public void setAttribute(String name, Object value) {
    changed = true;
    allAttribute.put(name, value);
  }

  @Override
  public Object getAttribute(String name) {
    return allAttribute.get(name);
  }

  @Override
  public Enumeration<String> getAttributeNames() {
    Set<String> set = allAttribute.keySet();
    Iterator<String> iterator = set.iterator();
    return new MyEnumeration<String>(iterator);
  }

  private class MyEnumeration<T> implements Enumeration<T> {
    Iterator<T> iterator;

    public MyEnumeration(Iterator<T> iterator) {
      super();
      this.iterator = iterator;
    }

    @Override
    public boolean hasMoreElements() {
      return iterator.hasNext();
    }

    @Override
    public T nextElement() {
      return iterator.next();
    }

  }

  @Override
  public void invalidate() {
    this.invalidated = true;
  }

  @Override
  public void removeAttribute(String name) {
    changed = true;
    allAttribute.remove(name);
  }

  @Override
  public long getCreationTime() {
    return orgiSession.getCreationTime();
  }

  @Override
  public long getLastAccessedTime() {
    return orgiSession.getLastAccessedTime();
  }

  @Override
  public int getMaxInactiveInterval() {
    return orgiSession.getMaxInactiveInterval();
  }

  @Override
  public ServletContext getServletContext() {
    return orgiSession.getServletContext();
  }

  @Override
  public Object getValue(String arg0) {
    return orgiSession.getValue(arg0);
  }

  @Override
  public String[] getValueNames() {
    return orgiSession.getValueNames();
  }

  @Override
  public boolean isNew() {
    return orgiSession.isNew();
  }

  @Override
  public void putValue(String arg0, Object arg1) {
    orgiSession.putValue(arg0, arg1);
  }

  @Override
  public void removeValue(String arg0) {
    orgiSession.removeValue(arg0);
  }

  @Override
  public void setMaxInactiveInterval(int arg0) {
    orgiSession.setMaxInactiveInterval(arg0);
  }

  @Override
  public HttpSessionContext getSessionContext() {
    return orgiSession.getSessionContext();
  }

4、DistributedHttpServletRequestWrapper 实现了 HttpServletRequestWrapper,包装处理过的session和原始request,核心代码:

public class DistributedHttpServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {
  private HttpServletRequest orgiRequest;
  private DistributedHttpSessionWrapper session;

  public DistributedHttpServletRequestWrapper(HttpServletRequest request, DistributedHttpSessionWrapper session) {
    super(request);
    if (session == null){
      //异常处理。。
    }
    if (request == null){
      //异常处理。。
    }
    this.orgiRequest = request;
    this.session = session;
  }

  public DistributedHttpSessionWrapper getSession(boolean create) {
    orgiRequest.getSession(create);
    return session;
  }

  public DistributedHttpSessionWrapper getSession() {
    return session;
  }

}

5、另外,定义DistributedBaseInterFace接口,用来处理session入redis进行持久化操作:

public interface DistributedBaseInterFace {

  /**
   * 根据key获取缓存数据
   * @param key
   * @param seconds
   */
  public String get(String key,int seconds);

  /**
   * 更新缓存数据
   * @param key
   * @param json
   * @param seconds
   */
  public void set(String key, String json,int seconds);

  /**
   * 删除缓存
   * @param key
   */
  public void del(String key);

  /**
   * 设置过期数据
   * @param key
   * @param seconds
   */
  public void expire(String key,int seconds);

注:本文只是在Spring中采用redis的方式对session进行管理,还有其他诸多的实现方式,比如在容器里面配置等,设计路由算法让session依赖于集群中的各个节点服务器,,,,,,但redis这种方式在实际应用中还是比较广泛的,LZ公司主要就是采用此方式。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java使用Redisson分布式锁实现原理

    1. 基本用法 <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.8.2</version> </dependency> Config config = new Config(); config.useClusterServers() .setScanInterval(2000) /

  • 使用redis分布式锁解决并发线程资源共享问题

    前言 众所周知, 在多线程中,因为共享全局变量,会导致资源修改结果不一致,所以需要加锁来解决这个问题,保证同一时间只有一个线程对资源进行操作 但是在分布式架构中,我们的服务可能会有n个实例,但线程锁只对同一个实例有效,就需要用到分布式锁----redis setnx 原理 修改某个资源时, 在redis中设置一个key,value根据实际情况自行决定如何表示 我们既然要通过检查key是否存在(存在表示有线程在修改资源,资源上锁,其他线程不可同时操作,若key不存在,表示资源未被线程占用,允许线程

  • Redis实现分布式锁的几种方法总结

    Redis实现分布式锁的几种方法总结 分布式锁是控制分布式系统之间同步访问共享资源的一种方式.在分布式系统中,常常需要协调他们的动作.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁. 我们来假设一个最简单的秒杀场景:数据库里有一张表,column分别是商品ID,和商品ID对应的库存量,秒杀成功就将此商品库存量-1.现在假设有1000个线程来秒杀两件商品,500个线程秒杀第一个商品,

  • 浅谈分布式锁的几种使用方式(redis、zookeeper、数据库)

    Q:一个业务服务器,一个数据库,操作:查询用户当前余额,扣除当前余额的3%作为手续费 synchronized lock dblock Q:两个业务服务器,一个数据库,操作:查询用户当前余额,扣除当前余额的3%作为手续费 分布式锁 我们需要怎么样的分布式锁? 可以保证在分布式部署的应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行. 这把锁要是一把可重入锁(避免死锁) 这把锁最好是一把阻塞锁(根据业务需求考虑要不要这条) 这把锁最好是一把公平锁(根据业务需求考虑要不要这条) 有高可用

  • Python实现的redis分布式锁功能示例

    本文实例讲述了Python实现的redis分布式锁功能.分享给大家供大家参考,具体如下: #!/usr/bin/env python # coding=utf-8 import time import redis class RedisLock(object): def __init__(self, key): self.rdcon = redis.Redis(host='', port=6379, password="", db=1) self._lock = 0 self.lock

  • redis实现分布式的方法总结

    一 为什么使用 Redis 在项目中使用 Redis,主要考虑两个角度:性能和并发.如果只是为了分布式锁这些其他功能,还有其他中间件 Zookpeer 等代替,并非一定要使用 Redis. 性能: 如下图所示,我们在碰到需要执行耗时特别久,且结果不频繁变动的 SQL,就特别适合将运行结果放入缓存.这样,后面的请求就去缓存中读取,使得请求能够迅速响应. 特别是在秒杀系统,在同一时间,几乎所有人都在点,都在下单...执行的是同一操作———向数据库查数据. 根据交互效果的不同,响应时间没有固定标准.在

  • 如何使用Spring+redis实现对session的分布式管理

    在Spring中实现分布式 session管理 本文主要是在Spring中实现分布式session,采用redis对session进行持久化管理,这样当应用部署的时候,不需要在Resin.Tomcat等容器里面进行分布式配置,方便加入新的节点服务器进行集群扩容,session不依赖各节点的服务器,可直接从redis获取.下面是功能的核心代码: 一.首先在web.xml里面配置 加入拦截器: <!-- 分布式session start --> <filter> <filter-

  • php redis实现对200w用户的即时推送

    怎么实现对200w用户的即时推送,这个推送可以理解为调用第三方的接口,push,sms之类的东西. 当时先写了一个demo 直接读取DB然后单个推送,结果..可想而知 于是设计一套基于redis+php多进程的方案,用着还不错而去扩展性蛮高的,故分享之. ============================================= 具体的逻辑如下:(无视我的字体) 其实这里还可以优化的,我的设想是如果用户数据再多一些的话,可以在redis里对数据进行分割采取多List,每一个Lis

  • 深入Spring Boot实现对Fat Jar jsp的支持

    spring boot 对于jsp支持的限制 对于jsp的支持,Spring Boot官方只支持了war的打包方式,不支持fat jar.参考官方文档: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-jsp-limitations 这里spring boot官方说是tomcat的问题,实际上是sp

  • 基于spring-boot和docker-java实现对docker容器的动态管理和监控功能[附完整源码下载]

    docker简介 Docker 是一个开源的应用容器引擎,和传统的虚拟机技术相比,Docker 容器性能开销极低,因此也广受开发者喜爱.随着基于docker的开发者越来越多,docker的镜像也原来越丰富,未来各种企业级的完整解决方案都可以直接通过下载镜像拿来即用.因此docker变得越来越重要. 本文目的 本文通过一个项目实例来介绍如果通过docker对外接口来实现对docker容器的管理和监控. 应用场景: 对服务器资源池通过docker进行统一管理,按需分配资源和创建容器,达到资源最大化利

  • Spring mvc 分步式session的实例详解

    Spring mvc 分步式session的实例详解 Session代表服务器与浏览器的一次会话过程,它的信息是保存在服务器端的.在Servlet中,session指的是HttpSession类的对象.服务器在创建session后,会把sessionid以cookie的形式回写给客户端.只要客户端的浏览器不关,每一次访问服务器都会带上这个sessionid.这样就可以在每次请求的时候获取到session的信息. 下面以spring MVC以例来说明如果创建分步式session. 1.login

  • Struts2实现对action请求对象的拦截操作方法

    Struts2的核心功能是action,对于开发人员来说,使用Struts2主要就是编写action,action类通常都要实现com.opensymphony.xwork2.Action接口,并实现该接口中的execute()方法. 该方法如下:   public String execute() throws Exception Struts2并不是要求所有编写的action类都要实现Action接口,也可以直接编写一个普通的Java类作为action,只要实现一个返回类型为String的无

  • SpringBoot集成redis与session实现分布式单点登录

    目录 单点登录 SSO(Single Sign On) 什么是单点登录? 实现方式 开发技术 单点登录实现流程 实现案例 看效果 前言: 由于考虑到cookie的安全性问题,就有了下面这个版本的sso 单点登录 SSO(Single Sign On) 什么是单点登录? 单点登录的英文名叫做:Single Sign On(简称SSO),指在同一帐号平台下的多个应用系统中,用户只需登录一次,即可访问所有相互信任的系统.简而言之,多个系统,统一登陆. 我们可以这样理解,在一个服务模块登录后,其他模块无

  • C# 使用Free Spire.Presentation 实现对PPT插入、编辑、删除表格

    现代学习和办公当中,经常会接触到对表格的运用,像各种单据.报表.账户等等.在PPT演示文稿中同样不可避免的应用到各种数据表格.对于在PPT中插入表格,我发现了一个新方法,不过我用到了一款免费的.NET组件--Free Spire.Presentation,在C#中添加该产品DLL文件,可以简单快速地实现对演示文稿的表格插入.编辑和删除等操作.有需要的话可以在下面的网址下载:https://www.e-iceblue.cn/Downloads/Free-Spire-Presentation-NET

  • 简单注解实现集群同步锁(spring+redis+注解)

    互联网面试的时候,是不是面试官常问一个问题如何保证集群环境下数据操作并发问题,常用的synchronized肯定是无法满足了,或许你可以借助for update对数据加锁.本文的最终解决方式你只要在方法上加一个@P4jSyn注解就能保证集群环境下同synchronized的效果,且锁的key可以任意指定.本注解还支持了锁的超时机制. 本文需要对Redis.spring和spring-data-redis有一定的了解.当然你可以借助本文的思路对通过注解对方法返回数据进行缓存,类似com.googl

  • 原生JavaScript来实现对dom元素class的操作方法(推荐)

    jQuery操作class的方式非常强大 写了一个利用原生js来实现对dom元素class的操作方法 1.addClass:为指定的dom元素添加样式 2.removeClass:删除指定dom元素的样式 3.toggleClass:如果存在(不存在),就删除(添加)一个样式 4.hasClass:判断样式是否存在 下面为一toggleClass的测试例子 [html] view plain copy <style type="text/css"> div.testClas

随机推荐