详解springboot中redis的使用和分布式session共享问题

时间:2022-08-30 09:11:38

对于分布式使用nginx+tomcat实现负载均衡,最常用的均衡算法有ip_hash、轮训、根据权重、随机等。不管对于哪一种负载均衡算法,由于nginx对不同的请求分发到某一个tomcat,tomcat在运行的时候分别是不同的容器里,因此会出现session不同步或者丢失的问题。

实际上实现session共享的方案很多,其中一种常用的就是使用tomcat、jetty等服务器提供的session共享功能,将session的内容统一存储在一个数据库(如mysql)或缓存(如redis)中。

本文旨在解决分布式系统的session如何共享问题,大致思路:session放入redis。其他解决方案:持久化、放cache等都可以,但是自从有了redis,这完全可以变的简简单单。

本文大致分两步:

1.springboot中如何使用redis。
2.redis如何解决session共享

pom依赖

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--redis配置开始-->
<dependency>
 <groupid>org.springframework.boot</groupid>
 <artifactid>spring-boot-starter-redis</artifactid>
</dependency>
<dependency>
 <groupid>org.springframework.data</groupid>
 <artifactid>spring-data-redis</artifactid>
 <version>${spring-data-redis.version}</version>
</dependency>
<dependency>
 <groupid>redis.clients</groupid>
 <artifactid>jedis</artifactid>
 <version>${jedis.version}</version>
</dependency>
<dependency>
 <groupid>org.springframework.session</groupid>
 <artifactid>spring-session-data-redis</artifactid>
</dependency>
<!--redis配置结束-->

添加redis配置类

该配置类同样可以配置缓存失效时间等。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.mos.quote.config;
 
import com.fasterxml.jackson.annotation.jsonautodetect;
import com.fasterxml.jackson.annotation.propertyaccessor;
import com.fasterxml.jackson.databind.objectmapper;
import org.springframework.cache.cachemanager;
import org.springframework.cache.annotation.cachingconfigurersupport;
import org.springframework.cache.annotation.enablecaching;
import org.springframework.cache.interceptor.keygenerator;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.data.redis.cache.rediscachemanager;
import org.springframework.data.redis.connection.redisconnectionfactory;
import org.springframework.data.redis.core.redistemplate;
import org.springframework.data.redis.core.stringredistemplate;
import org.springframework.data.redis.serializer.jackson2jsonredisserializer;
 
/**
 * @author administrator
 */
@configuration
@enablecaching
public class redisconfig extends cachingconfigurersupport {
  @bean
  public keygenerator keygenerator(){
    return (target, method, params) -> {
      stringbuilder sb = new stringbuilder();
      sb.append(target.getclass().getname());
      sb.append(method.getname());
      for (object obj : params) {
        sb.append(obj.tostring());
      }
      return sb.tostring();
    };
  }
 
  @bean
  public redistemplate<string, string> redistemplate(redisconnectionfactory factory) {
    stringredistemplate template = new stringredistemplate(factory);
    jackson2jsonredisserializer jackson2jsonredisserializer = new jackson2jsonredisserializer(object.class);
    objectmapper om = new objectmapper();
    om.setvisibility(propertyaccessor.all, jsonautodetect.visibility.any);
    om.enabledefaulttyping(objectmapper.defaulttyping.non_final);
    jackson2jsonredisserializer.setobjectmapper(om);
    template.setvalueserializer(jackson2jsonredisserializer);
    template.afterpropertiesset();
    return template;
  }
 
  @bean
  public cachemanager cachemanager(redistemplate<string,string> redistemplate) {
    rediscachemanager rcm = new rediscachemanager(redistemplate);
    //设置缓存过期时间
    rcm.setdefaultexpiration(600000);
    return rcm;
  }
}

配置redis服务

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# redis数据库索引(默认为0
spring.redis.database=0
# redis服务器地址
spring.redis.host=192.168.1.118
# redis服务器连接端口
spring.redis.port=6381
# redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0

单元测试

1、set值(字符串)

?
1
2
3
4
5
@test
public void put(){
  stringredistemplate.opsforvalue().set("test001","test001");
  assert.assertequals("test001", stringredistemplate.opsforvalue().get("test001"));
}

往redis放一个key为test001、value为test001的值,然后查看redis

详解springboot中redis的使用和分布式session共享问题

key=test001

2、set值(object)

?
1
2
3
4
5
6
7
8
9
10
11
@test
 public void testobj() throws exception {
   sysuser user=new sysuser();
   user.setid("123456");
   user.setname("张三");
   valueoperations<string, sysuser> operations=redistemplate.opsforvalue();
   operations.set("user1", user);
   operations.set("user2", user,5, timeunit.seconds);
   thread.sleep(6000);
   assert.assertfalse(redistemplate.haskey("user2"));
 }

往redis分别放key为user1和user2的对象,user2设置5秒失效,线程等待6秒再完成,期望结果:redis中有user1,没有user2,bingo!!!

详解springboot中redis的使用和分布式session共享问题

objtest

解决session共享

使用spring-session-data-redis实现session共享,pom中引入该依赖(上文已添加),添加sessionconfig配置类。
对,没看错,只需要这个就够了。最长有效时间根据自己情况随意配置即可。

?
1
2
3
4
5
6
7
8
9
10
11
12
package com.mos.quote.config;
 
import org.springframework.context.annotation.configuration;
import org.springframework.session.data.redis.config.annotation.web.http.enableredishttpsession;
 
/**
 * @author administrator
 */
@configuration
@enableredishttpsession(maxinactiveintervalinseconds = 3000)
public class sessionconfig {
}

测试

写一个简单controller,如下

?
1
2
3
4
5
6
7
8
@requestmapping("testsessiontimeout")
  public string testsessiontimeout(string id,httpsession session,model model){
    area area = areaservice.getbyid(id);
    system.out.println("sessionid-------->"+session.getid());
    model.addattribute("area",json.tojsonstring(area));
    session.setattribute("area",json.tojsonstring(area));
    return "demo/test1";
  }

这里可以看到sessionid:

详解springboot中redis的使用和分布式session共享问题

sessionid

看redis中,可以看到失效时间,sessionid等

详解springboot中redis的使用和分布式session共享问题

sessionid

共享session

另外找一个机器,照着这个配置再来一遍,自动启用session共享,因为sessionid都存在了同一个redis中。奏是这么简单。挽起袖子开始干吧。

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

原文链接:http://www.jianshu.com/p/b5a96142fdd9