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

 更新时间:2018年06月30日 10:46:31   作者:幢一幢  
本篇文章主要介绍了如何使用Spring+redis实现对session的分布式管理,本文主要是在Spring中实现分布式session,采用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公司主要就是采用此方式。

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

相关文章

  • java8 统计字符串字母个数的几种方法总结(推荐)

    java8 统计字符串字母个数的几种方法总结(推荐)

    下面小编就为大家分享一篇java8 统计字符串字母个数的几种方法总结(推荐),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来吧
    2017-11-11
  • IDEA 中使用 Big Data Tools 连接大数据组件

    IDEA 中使用 Big Data Tools 连接大数据组件

    本文主要介绍了IDEA 中使用 Big Data Tools 连接大数据组件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • 详解JAVA常用的时间操作【实用】

    详解JAVA常用的时间操作【实用】

    本文主要介绍了JAVA一些常用的时间操作,很实用,相信大家在开发项目时会用到,下面就跟小编一起来看下吧
    2016-12-12
  • 手把手带你入门 Spring Security的具体流程

    手把手带你入门 Spring Security的具体流程

    这篇文章主要介绍了手把手带你入门 Spring Security,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • Java探索之Hibernate主键生成策略详细介绍

    Java探索之Hibernate主键生成策略详细介绍

    这篇文章主要介绍了Java探索之Hibernate主键生成策略详细介绍,具有一定参考价值,需要的朋友可以了解下。
    2017-10-10
  • Java实现局域网IP地址扫描

    Java实现局域网IP地址扫描

    这篇文章主要为大家详细介绍了Java实现局域网IP地址扫描,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • JavaSwing基础之Layout布局相关知识详解

    JavaSwing基础之Layout布局相关知识详解

    上次我们说到View的Mearsure流程,今天接着说说layout. 关于layout,很多朋友知道它是负责布局的,那么具体是怎么布局的?viewGroup和view的layout方法又有什么不同?一起来看看吧,需要的朋友可以参考下
    2021-05-05
  • 隐藏idea的.idea和.mvn文件的解决方案

    隐藏idea的.idea和.mvn文件的解决方案

    这篇文章主要介绍了隐藏idea的.idea和.mvn文件的解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • 基于swing实现窗体拖拽和拉伸

    基于swing实现窗体拖拽和拉伸

    这篇文章主要为大家详细介绍了基于swing实现窗体拖拽和拉伸,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • MyBatis游标Cursor的正确使用和百万数据传输的内存测试

    MyBatis游标Cursor的正确使用和百万数据传输的内存测试

    这篇文章主要介绍了MyBatis游标Cursor的正确使用和百万数据传输的内存测试,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01

最新评论