当前位置:首页 > 编程教程 > java技术文章 > 如何使用Spring+redis实现对session的分布式管理

Spring+redis对session的分布式管理方法

  • 发布时间:
  • 作者:码农之家
  • 点击:144

这篇文章主要知识点是关于Spring、redis、session、分布式管理、Spring Boot项目利用Redis实现session管理实例 的内容,如果大家想对相关知识点有系统深入的学习,可以参阅以下电子书

精通JavaScript
精通JavaScript高清第2版
  • 类型:JavaScript大小:33.5 MB格式:PDF作者:陈贤安
立即下载

如何使用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公司主要就是采用此方式。

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

Spring Boot项目利用Redis实现session管理实例

在现代网络服务中,session(会话)不得不说是非常重要也是一定要实现的概念,因此在web后台开发中,对session的管理和维护是必须要实现的组件。这篇文章主要是介绍如何在Spring Boot项目中加入redis来实现对session的存储与管理。

1. 利用Spring Initializr来新建一个spring boot项目

Spring Boot项目利用Redis实现session管理实例

2. 在pom.xml中添加redis和session的相关依赖。项目生成的时候虽然也会自动生成父依赖,但是1.5.3版本的spring boot的redis相关依赖有可能不能够正常工作,笔者自行在maven repository找到了比较稳定的版本如下方代码所示

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
  <version>1.5.2.RELEASE</version>
</dependency>

<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-data-redis</artifactId>
  <version>1.3.0.RELEASE</version>
</dependency>

3. 在application.properties中添加redis数据库的相关配置。这里为了演示使用方法只添加了相对简单的配置,即设置了session存储的数据库类型、使用的数据库号、数据库地址和端口号。实战中还会对使用非默认数据库、数据库大小、数据库最大连接数、生存时长、是否写回磁盘等许多参数进行配置

# Redis配置
spring.session.store-type=redis
spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6379

4. 编写一个测试的controller来验证是否能够正确地读写session。这里的controller中,我判断了当前获取的session是否是新生成的。如果是,则输出成功创建一个session对象,并返回session的id,然后在session中添加一个字段。如果session不是新生成的,即是已经存在的session,则输出session是已经存在的并返回session的id,然后再输出session中初次创建session保存的key所对应的value

@SpringBootApplication
@EnableRedisHttpSession
@RestController
public class DemoApplication {

  private Logger logger = LoggerFactory.getLogger(this.getClass());

  public static void main(String[] args) {
    SpringApplication app = new SpringApplication(DemoApplication.class);
    app.setWebEnvironment(true);
    app.run(args);
  }

  @GetMapping("/hello")
  public ResponseEntity<?> hello(HttpSession session) {
    if (session.isNew()) {
      logger.info("Successfully creates a session ,the id of session :" + session.getId());
      session.setAttribute("key", "hello");
    } else {
      logger.info("session already exists in the server, the id of session :"+ session.getId());
      logger.info(session.getAttribute("key").toString());
    }
    return new ResponseEntity<>("Hello World", HttpStatus.OK);
  }

}

5. 测试代码

首先运行Redis客户端

redis-cli

查看当前数据库内容

127.0.0.1:6379> keys * 
(empty list or set)

运行spring boot项目

第一次浏览器访问localhost:8080/hello,如下图所示则成功运行

Spring Boot项目利用Redis实现session管理实例

查看log可以看到

2017-06-12 00:26:12.601 INFO 9580 — [nio-8080-exec-1] 
ication$$EnhancerBySpringCGLIB$$de942542 : Successfully creates a session ,the id of session :4368a535-9bfa-406b-975c-e58c2bca1e75

再次访问localhost:8080/hello时,查看log可以看到能够正确地从redis中取出存放在session中的某个key对应的值

2017-06-12 00:30:43.533 INFO 9580 — [nio-8080-exec-5] 
ication$$EnhancerBySpringCGLIB$$de942542 : session already exists in the server, the id of session :4368a535-9bfa-406b-975c-e58c2bca1e75 
2017-06-12 00:30:43.533 INFO 9580 — [nio-8080-exec-5] 
ication$$EnhancerBySpringCGLIB$$de942542 : hello

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

以上就是本次给大家分享的关于java的全部知识点内容总结,大家还可以在下方相关文章里找到相关文章进一步学习,感谢大家的阅读和支持。

您可能感兴趣的文章:

  • Spring借助Redis设计访问计数器实例讲解
  • Spring分布式管理 相关电子书
    学习笔记
    网友NO.583367

    Hibernate用ThreadLocal模式(线程局部变量模式)管理Session

    Hibernate ThreadLocal 它会为每个线程维护一个私有的变量空间。实际上, 其实现原理是在JVM 中维护一个Map,这个Map的key 就是当前的线程对象,而value则是 线程通过Hibernate ThreadLocal.set方法保存的对象实例。当线程调用Hibernate ThreadLocal.get方法时, Hibernate ThreadLocal会根据当前线程对象的引用,取出Map中对应的对象返回。 这样,Hibernate ThreadLocal通过以各个线程对象的引用作为区分,从而将不同线程的变量隔离开来。 一、ThreadLocal模式 (线程局部变量模式) 管理Session的理解 (1)在利用Hibernate开发的时候如何合理的管理Session,避免Session的频繁创建和销毁,对于提高系统的性能来说是非常重要的! (2)我们知道Session是由SessionFactory负责创建的,而SessionFactory的实现是线程安全的,多个并发的线程可以同时访问一个SessionFactory并从中获取Session实例,但是遗憾的是……

    网友NO.254691

    详解nodejs express下使用redis管理session

    Session实现原理 实现请求身份验证的方式很多,其中一种广泛接受的方式是使用服务器端产生的Session ID结合浏览器的Cookie实现对Session的管理,一般来说包括以下4个步骤: 1.服务器端的产生Session ID 2.服务器端和客户端存储Session ID 3.从HTTP Header中提取Session ID 4.根据Session ID从服务器端的Hash中获取请求者身份信息 使用Express和Redis对Session管理的实现 var session = require('express-session');var RedisStrore = require('connect-redis')(session);var config={"cookie" : { "maxAge" : 1800000}, "sessionStore" : { "host" : "192.168.0.13", "port" : "6379", "pass" : "123456", "db" : 1, "ttl" : 1800, "logErrors" : true}app.use(session({ name : "sid", secret : 'Asecret123-', resave : true, rolling:true, saveUninitialized : false, cookie : config.cookie, store : new RedisStrore(config.sessionStore)})); 实现堆栈 express-session实例化后调用代码(https://github.com/expressjs/session) if (!r……

    网友NO.204601

    模仿J2EE的session机制的App后端会话信息管理实例

    此文章只将思想,不提供具体完整实现(博主太懒,懒得整理),有疑问或想了解的可以私信或评论 背景 在传统的java web 中小型项目中,一般使用session暂存会话信息,比如登录者的身份信息等。此机制是借用http的cookie机制实现,但是对于app来说每次请求都保存并共享cookie信息比较麻烦,并且传统的session对集群并不友好,所以一般app后端服务都使用token来区分用户登录信息。 j2ee的session机制大家都很了解,使用非常方便,在传统java web应用中很好用,但是在互联网项目中或用得到集群的一些项目就有些问题,比如序列化问题,同步的延时问题等等,所以我们需要一个使用起来类似session的却能解决得了集群等问题的一个工具。 方案 我们使用cache机制来解决这个问题,比较流行的redis是个nosql内存数据库,而且带有cache的失效机制,很适合做会话数据的存……

    网友NO.134901

    Spring Boot项目利用Redis实现session管理实例

    在现代网络服务中,session(会话)不得不说是非常重要也是一定要实现的概念,因此在web后台开发中,对session的管理和维护是必须要实现的组件。这篇文章主要是介绍如何在Spring Boot项目中加入redis来实现对session的存储与管理。 1. 利用Spring Initializr来新建一个spring boot项目 2. 在pom.xml中添加redis和session的相关依赖。项目生成的时候虽然也会自动生成父依赖,但是1.5.3版本的spring boot的redis相关依赖有可能不能够正常工作,笔者自行在maven repository找到了比较稳定的版本如下方代码所示 dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId/dependencydependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId version1.5.2.RELEASE/version/dependencydependency groupIdorg.springframework.session/groupId artifactIdspring-session-data-redis/artifactId version1.3.……

    <
    1
    >

    Copyright 2018-2020 www.xz577.com 码农之家

    投诉 / 推广 / 赞助:QQ:520161757