对于分布式使用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
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!!!
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:
sessionid
看redis中,可以看到失效时间,sessionid等
sessionid
共享session
另外找一个机器,照着这个配置再来一遍,自动启用session共享,因为sessionid都存在了同一个redis中。奏是这么简单。挽起袖子开始干吧。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:http://www.jianshu.com/p/b5a96142fdd9