分布式session共享 集群Session共享

最近,在工作中遇到一个问题,问题描述:一个用户在登录成功以后会把用户信息存储在session当中,这时session所在服务器为server1,那么用户在session失效之前如果再次使用app,那么可能会被路由到server2,这时问题来了,server2没有该用户的session,所以需要用户重新登录,这时的用户体验会非常不好,所以如何实现多台server之间共享session,让用户状态得以保存。

         当然业界已经有很多成熟的解决方案,罗列如下:

1.服务器实现的session复制或session共享,这类型的共享session是和服务器紧密相关的,比如webSphere或JBOSS在搭建集群时候可以配置实现session复制或session共享,但是这种方式有一个致命的缺点,就是不好扩展和移植,比如我们更换服务器,那么就要修改服务器配置。

2.利用成熟的技术做session复制,比如12306使用的gemfire,比如常见的内存数据库如redis或memorycache,这类方案虽然比较普适,但是严重依赖于第三方,这样当第三方服务器出现问题的时候,那么将是应用的灾难。

3.将session维护在客户端,很容易想到就是利用cookie,但是客户端存在风险,数据不安全,而且可以存放的数据量比较小,所以将session维护在客户端还要对session中的信息加密。

 

       我们实现的方案可以说是第二种方案和第三种方案的合体,可以利用gemfire实现session复制共享,还可以将session维护在redis中实现session共享,同时可以将session维护在客户端的cookie中,但是前提是数据要加密。这三种方式可以迅速切换,而不影响应用正常执行。我们在实践中,首选gemfire或者redis作为session共享的载体,一旦session不稳定出现问题的时候,可以紧急切换cookie维护session作为备用,不影响应用提供服务,下面我简单介绍方案中session共享的实现方式和原理。

        这里主要讲解redis和cookie方案,gemfire比较复杂大家可以自行查看gemfire工作原理。利用redis做session共享,首先需要与业务逻辑代码解耦,不然session共享将没有意义,其次支持动态切换到客户端cookie模式。redis的方案是,重写服务器中的HttpSession和HttpServletRequest,首先实现HttpSession接口,重写session的所有方法,将session以hash值的方式存在redis中,一个session的key就是sessionID,setAtrribute重写之后就是更新redis中的数据,getAttribute重写之后就是获取redis中的数据,等等需要将HttpSession的接口一一实现。

        实现了HttpSesson,那么我们先将该session类叫做MySession(当然实践中不是这么命名的),当MySession出现之后问题才开始,怎么能在不影响业务逻辑代码的情况下,还能让原本的request.getSession()获取到的是MySession,而不是服务器原生的session。这里,我决定重写服务器的HttpServletRequet,这里先称为MyRequest,但是这可不是单纯的重写,我需要在原生的request基础上重写,于是我决定在filter中,实现request的偷梁换柱,我的思路是这样的,MyRequest的构建器,必须以request作为参数,于是我在filter中将服务器原生的request(也有可能是框架封装过的request),当做参数new出来一个MyRequest,并且MyRequest也实现了HttpServletRequest接口,其实就是对原生request的一个增强,这里主要重写了几个request的方法,但是最重要的是重写了request.getSession(),写到这里大家应该都明白为什么重写这个方法了吧,当然是为了获取MySession,于是这样就在filter中,偷偷的将原生的request换成MyRequest了,然后再将替换过的request传入chan.doFilter(),这样filter时候的代码都使用的是MyRequest了,同时对业务代码是透明的,业务代码获取session的方法仍然是request.getSession(),但其实获取到的已经是MySession了,这样对session的操作已经变成了对redis的操作。这样实现的好处有两个,第一开发人员不需要对session共享做任何关注,session共享对用户是透明的;第二,filter是可配置的,通过filter的方式可以将session共享做成一项可插拔的功能,没有任何侵入性。

         这个时候已经实现了一套可插拔的session共享的框架了,但是我们想到如果redis服务出了问题,这时我们该怎么办呢,于是我们延续redis的想法,想到可以将session维护在客户端内(加密的cookie),当然实现方法还是一样的,我们重写HttpSession接口,实现其所有方法,比如setAttribute就是写入cookie,getAttribute就是读取cookie,我们可以将重写的session称作MySession2,这时怎么让开发人员透明的获取到MySession2呢,实现方法还是在filter内偷梁换柱,在MyRequest加一个判断,读取sessionType配置,如果sessionType是redis的,那么getSession的时候获取到的是MySession,如果sessionType是coolie的,那么getSession的时候获取到的是MySession2,以此类推,用同样的方法就可以获取到MySession 3,4,5,6等等。

         这样两种方式都有了,那么我们怎实现两种session共享方式的快速切换呢,刚刚我提到一个sessionType,这是用来决定获取到session的类型的,只要变换sessionType就能实现两种session共享方式的切换,但是sessionType必须对所有的服务器都是一致的,如果不一致那将会出现比较严重的问题,我们目前是将sessionType维护在环境变量里,如果要切换sessionType就要重启每一台服务器,完成session共享的转换,但是当服务器太多的时候将是一种灾难。而且重启服务意味着服务的中断,所以这样的方式只适合服务器规模比较小,而且用户量比较少的情况,当服务器太多的时候,务必需要一种协调技术,能够让服务器能够及时获取切换的通知。基于这样的原因,我们选用zookeeper作为配置平台,每一台服务器都会订阅zookeeper上的配置,当我们切换sessionType之后,所有服务器都会订阅到修改之后的配置,那么切换就会立即生效,当然可能会有短暂的时间延迟,但这是可以接受的。

         方案大体分享完了,在此将我的收获记录下来,我们已经实现了一个版本,大家也可以发挥想象,分享不同的方案,我们一起进步,文中有不足之处,还望各位不吝赐教。

原文:https://blog.csdn.net/sxiaobei/article/details/57086489

 

集群间实现Session共享

置顶2017年08月24日 17:21:02

阅读数:5420

上一篇,同一tomcat不同项目下session共享方案:http://blog.csdn.net/qinmengdecluntan/article/details/72832648

一、引言

针对企业,为了应对庞大的用户访问压力,目前大多数大型网站服务器都采用集群部署的方式;针对个人,仅一台服务器而言,也会安装多个tomcat进行错时更新,保证更新后台业务时服务不断开,即模拟了集群的运行方式。在此集群中,我们就不得不考虑一个用户鉴权的问题,即在不同服务上如何保证用户均已登录,并能获取相同的用户登录信息。

二、Java Web推荐的(公认的)用户鉴权机制

说此部分之前先了解几个概念: 
1.请求,即Request,指客户端向服务器发送的信息,通常是通信的发起方; 
2.响应,即Response,指服务器对请求的应答,通常是通信的回复方; 
3.会话,即Session,服务器可将请求<->响应这一个完整的过程称为一次会话,并为这次会话生成一个唯一的标识符,即sessionId,用来表示这次会话,Session储存在服务器端; 
4.Cookie,客户端保存在本地终端的数据,即Cookie储存在客户端

Java Web的共用的用户鉴权机制是采用Session-Cookie技术实现原理是:用户登录时,请求到达服务器,服务器调用通过getSession()方法判断session是否存在,如果不存在,则新建session,并通过其算法为session生成一个随机数作为sessionId,开发者可在session中储存一些用户信息;第二次请求时,如获取用户信息,getSession()方法判断session存在,则取出session,而不是新建,从而从session中获取到用户的相关信息。

客户端请求时,可以将cookie信息储存于request的head中发送给服务器; 
服务器响应时,可以将cookie信息置于response中回传给客户端。 
如下图代表,名称为test的cookie其值为aaa: 
分布式session共享 集群Session共享_第1张图片

那么getSession()里究竟做了什么?

1.第一次用户请求,客户端本地没有任何数据,即其cookie为空,朝服务器发送request,getSession()中会解析request,发现其约定的cookie为null,则认为没有session,所以会重新创建一个session对象;

2.创建session后会将此session的id放入response中,回传给客户端,客户端则保存response中的cookie;

3.再次请求,服务器getSession()又会重新解析request获取cookie,发现了其中的sessionId,那么根据此sessionId去服务器的中去找,则得到了上次创建的session对象,那么则认为鉴权成功。

如此,便完成了鉴权的整个流程,Java逻辑代码(伪代码)如下:

 
  1. public HttpSession getSession() {

  2. //从request中解析cookie

  3. HttpSession session = null;

  4. Cookie[] cookies = getRequest().getCookies();

  5. if (cookies != null) {

  6. for (Cookie cookie : cookies) {

  7. if (cookie.getName().equals("JSESSIONID")) {

  8. String sessionId = cookie.getValue();

  9. session = //根据sessionId获取内存中的session对象

  10. }

  11. }

  12. }

  13. if (session == null) {

  14. session = //创建一个新的session对象

  15. }

  16.  
  17. //通过response将cookie返回

  18. Cookie cookie = new Cookie("JSESSIONID", session.getId());

  19. getResponse().addCookie(cookie);

  20.  
  21. return session;

  22. }

如上,java中将sessionId在cookie中保存的名称叫做“JSESSIONID”,即“Java Session Id”之意,打开浏览器可以看到类型的信息,如图: 
分布式session共享 集群Session共享_第2张图片

三、集群间如何实现session共享

按照前文所说的session-cookie机制,session是保存在每台服务器的,但在集群中,拥有多台服务器,每台各自为政,势必会造成在这台服务器中登录,获取session成功,但是到另一台服务器上,又会获取不到session,造成鉴权失败,这样对用户来说是极不友好的,那么怎么解决这个问题呢?

通过我们以上的分析,即可得出几种处理方式: 
A.找一块公共的空间用来储存session,而不是将session储存在集群节点的某台服务器上,此时,每一台服务器都能访问这块空间,从而实现session共享;

B.仍在每台服务器上保存session信息,不作修改,但采用另一种同步机制,实时同步没一台服务器的session信息;

C.构建一种全新的鉴权机制,不采用session-cookie机制,但要去除此鉴权机制对单个服务器的依赖。

综上所述,列举几种的具体实现方案:


1.持久化session到数据库,即使用数据库来储存session。数据库正好是我们普遍使用的公共储存空间,一举两得,推荐使用mysql数据库,轻量并且性能良好。

优点:就地取材,符合大多数人的思维,使用简单,不需要太多额外编码工作 
缺点:对mysql性能要求较高,访问mysql需要从连接池中获取连接,又因为大部分请求均需要进行登录鉴权,所以操作数据库非常频繁,当用户量达到一定程度之后,极易造成数据库瓶颈,不适用于处理高并发的情况。


2.使用redis共享session。redis是一个key-value的储存系统。可以简单的将其理解为一个数据库,与传统数据库的区别是,它将数据储存于内存中,并自带有内存到硬盘的序列化策略,即按策略将内存中的数据同步到磁盘,避免数据丢失,是目前比较流行的解决方案。

优点:无需增加数据库的压力,因为数据存储于内存中,所以读取非常快,高性能,并能处理多种类型的数据。 
缺点:额外增加一些编码,以便操作redis。


3.使用memcache同步session,memcache可以实现分布式,可将服务器中的内存组合起来,形成一个“内存池”,以此充当公共空间,保存session信息。

优点:数据储存在内存中,读取非常快,性能好; 
缺点:memcache把内存分成很多种规格的存储块,有大有小,不能完全利用内存,会产生内存碎片,浪费资源,如果储存块不足,还会产生内存溢出。


4.通过脚本或守护进程在多台服务器之间同步session

优点:实现了session共享; 
缺点:对个人来说实现较为复杂,速度不稳定,有延时性,取决于现实中服务运行状态,偶然性较大,如果用于访问过快,可能出现session还没同步成功的情况。


5.使用NFS共享session。NFS是Network File Server共享服务器的简称,最早由Sun公司为解决Unix网络主机间的目录共享而研发。选择一台公共的NFS做共享服务器,储存所有session数据,每台服务器所需的session均从此处获取。

优点:较好的实现了session共享; 
缺点:成本较高,对于个人来说难以实现。NFS依托于复杂的安全机制和文件系统,因此并发效率不高。


6.使用Cookie共享session。此方案可以说是独辟蹊径了,将分布式思想用到了极致。如上文分析所说,session-cookie机制中,session与cookie相互关联,以cookie做中转站,用来找到对应的session,其中session存放在服务器。那么如果将session中的内容存放在cookie中呢,那么则省略了服务器保存session的过程,后台只需要根据cookie中约定的标识进行鉴权校验即可。

优点:完美的贯彻分布式的理念,将每个用户都利用起来,无需耗费额外的服务器资源; 
缺点:受http协议头长度限制,cookie中存储的信息不宜过多;为了保持cookie全局有效,所以其一般依赖在根域名下,所以基本上所有的http请求都需要传递cookie中的这些标记信息,所以会占用一些服务器的带宽;鉴权信息全存储于cookie中,cookie存在于客户端,服务器并没有储存相关信息,cookie存在着泄露的可能,或则其他人揣摩出规则后可以进行伪装,其安全性比其他方案差,故需要对cookie中信息进行加密解密,来增强其安全性。


在此,我们将选择方案2使用redis来具体实现集群下的session共享。

四、搭建测试环境

1.为模拟集群环境,需要两台服务器或在一台服务器上安装两个tomcat; 
2.使用nginx做集群纷发; 
3.安装redis充当公共的空间存储session; 
4.框架中编写session储存业务,因为需要使用java操作redis,redis提供了驱动包jedis,故需要掌握jedis进行操作。

五、详细部署

5.1 安装多个tomcat 
怎么安装tomcat此处不作说明,只说明安装额外的tomcat,本人原安装的tomcat目录为apache-tomcat-7.0.77

1.拷贝apache-tomcat-7.0.77为apache-tomcat-7.0.77_2 
2.修改apache-tomcat-7.0.77_2下conf中server.xml文件端口号 
,共三处,将每处在原端口号port之上加1,确保两个tomcat不会共用端口,如下:

 
  1.  
  2. connectionTimeout="20000"

  3. redirectPort="8443" />

  4.  

5.2 更改nginx配置,模拟集群 
修改nginx配置文件nginx.conf文件,在server闭包外添加upstream,由上可知两个tomcat端口号分别为8080,8081

 
  1. #建立集群

  2. upstream not_alone {

  3. server localhost:8080;

  4. server localhost:8081;

  5. }

  6.  
  7. # 转发请求到tomcat下mate项目

  8. location / {

  9. proxy_pass http://not_alone/mate/;

  10. }

5.2 redis安装与配置

1.下载,官网:https://redis.io/download 
2.安装,以4.0.1版本为例

 
  1. $ wget http://download.redis.io/releases/redis-4.0.1.tar.gz

  2. $ tar xzf redis-4.0.1.tar.gz

  3. $ cd redis-4.0.1

  4. $ make

3.启动

$ src/redis-server
  •  

4.关闭

ctrl + c

5.配置后台启动(redis默认是前台启动,启动成功后界面就持续停止在那个界面上,这对服务器操作很不方便) 
分布式session共享 集群Session共享_第3张图片

 
  1. #修改其配置文件

  2. vim redis.conf

  3.  
  4. 将daemonize no改为daemonize yes

  5.  
  6. #保存退出

  7. :wq!

如下图: 
分布式session共享 集群Session共享_第4张图片

6.后台启动

src/redis-server redis.conf

如图: 
这里写图片描述

7.关闭 
杀掉redis进程,如图: 
这里写图片描述

8.为redis配置系统服务,本人使用的系统是CentOS 7,需要配置使用systemctl进行管理。 
/lib/systemd/system目录下创建文件redis.service,并编辑:

 
  1. #表示服务信息

  2. [Service]

  3. Type=forking

  4. #注意:需要和redis.conf配置文件中的信息一致

  5. PIDFile=/var/run/redis_6379.pid

  6. #启动服务的命令

  7. #redis-server安装的路径 和 redis.conf配置文件的路径

  8. ExecStart=/server/soft/redis-4.0.1/src/redis-server /server/soft/redis-4.0.1/redis.conf

  9. #重新加载命令

  10. ExecReload=/bin/kill -s HUP $MAINPID

  11. #停止服务的命令

  12. ExecStop=/bin/kill -s QUIT $MAINPID

  13. PrivateTmp=true

  14.  
  15. #安装相关信息

  16. [Install]

  17. #以哪种方式启动

  18. WantedBy=multi-user.target

  19. #multi-user.target表明当系统以多用户方式(默认的运行级别)启动时,这个服务需要被自动运行。

更多redis systemctl详细配置,请看:http://blog.csdn.net/u011389474/article/details/72303156

配置成功,启动完成后,通过服务可知其运行状态,如图: 
分布式session共享 集群Session共享_第5张图片

至此,redis已全部安装部署完成。

六、编写代码实现功能

为了测试简便,后台web框架我选择的是JFinal,JFinal是中国开源社区中广受好评的后台轻量级极速web框架,因其操作简单,设计灵活而被大多数开发者所喜爱,有兴趣的朋友可以试试,用一次之后你就会喜欢它的,JFinal社区:http://www.jfinal.com/

这里用JFianl的另一个好处就是JFinal核心库中自带Redis插件,集成了jedis的各种使用方法,这样就不用自己去编写了,省了很大的代码量。Jedis基本操作:http://www.cnblogs.com/edisonfeng/p/3571870.html

为帮助理解代码,Jfinal中连接redis,只需要在主配置文件中编写:

 
  1. /**

  2. * 插件配置

  3. */

  4. @Override

  5. public void configPlugin(Plugins me) {

  6. /*

  7. * Redis配置:连接本地的mate redis库,端口号默认

  8. */

  9. RedisPlugin rp = new RedisPlugin("mate", "localhost");

  10. me.add(rp);

  11. }

redis存取数据:

 
  1. Cache cache = Redis.use();

  2.  
  3. //存

  4. cache.set(key, value);

  5.  
  6. //取

  7. Object value = cache.get(key);

  8.  
  9. //设置redis过期时间

  10. cache.pexpire(key, time);

正式代码如下,我们将会自定义session,每个sesison对象都是唯一的,需要给每个session分配一个唯一id,id生成算法,则可以借用UUID实现,UUID相关介绍:https://baike.baidu.com/item/UUID/5921266?fr=aladdin 
自定义随机数工具类:

 
  1. /**

  2. * 随机数工具类

  3. * @author alone

  4. */

  5. public class RandomUtils {

  6. /**

  7. * 获取唯一的可辨识资讯UUID

  8. * UUID为128位二进制,每4位二进制=16进制,其添加了四个'-',故总长为36位

  9. * @param 是否删除标记

  10. * @return UUID

  11. */

  12. public static String getUUID(boolean rmtag) {

  13. String uuid = UUID.randomUUID().toString();

  14. if (rmtag) {

  15. uuid = uuid.replace("-", "");

  16. }

  17. return uuid;

  18. }

  19. }

自定义RedisSession类,将替代原来的HttpSession:

 
  1. package com.alone.mate.common;

  2.  
  3. import java.io.Serializable;

  4. import java.util.HashMap;

  5. import java.util.Map;

  6.  
  7. import com.alone.mate.utils.RandomUtils;

  8. import com.jfinal.plugin.redis.Cache;

  9. import com.jfinal.plugin.redis.Redis;

  10.  
  11. /**

  12. * 自定义ResidSession解决集群会话共享

  13. * @author alone

  14. */

  15. @SuppressWarnings("serial")

  16. public class RedisSession implements Serializable {

  17.  
  18. private String id;

  19. private SessionType type;

  20. private long createTime;

  21. private long destroyTime;

  22. private Map attrs;

  23.  
  24. /**

  25. * 会话类型,不同类型的会话其有效期不同

  26. * @author alone

  27. */

  28. public enum SessionType {

  29.  
  30. /**

  31. * 移动端,类型为1,会话有效期为一周

  32. */

  33. MOBILE(1, 1000 * 60 * 60 * 24 * 7),

  34. /**

  35. * 网页端,类型为2,会话有效期为半小时

  36. */

  37. BROWSER(2, 1000 * 60 * 30);

  38.  
  39. private int type;

  40. private int value;

  41.  
  42. private SessionType(int type, int value) {

  43. this.type = type;

  44. this.value = value;

  45. }

  46.  
  47. public int getType() {

  48. return type;

  49. }

  50.  
  51. public int getValue() {

  52. return value;

  53. }

  54.  
  55. public static SessionType getSessionType(int type) {

  56. for (SessionType st : SessionType.values()) {

  57. if (st.type == type) {

  58. return st;

  59. }

  60. }

  61. return null;

  62. }

  63. }

  64.  
  65. public RedisSession(int sessionType) {

  66. this.id = RandomUtils.getUUID(true);

  67. this.type = SessionType.getSessionType(sessionType);

  68. this.createTime = System.currentTimeMillis();

  69. this.destroyTime = this.createTime + this.type.value;

  70. this.attrs = new HashMap<>();

  71. }

  72.  
  73. public Object getAttribute(String key) {

  74. return attrs.get(key);

  75. }

  76.  
  77. public void setAttribute(String key, Object value) {

  78. attrs.put(key, value);

  79. Cache cache = Redis.use();

  80. cache.set(this.getId(), this);

  81. cache.pexpire(this.getId(), this.getDestroyTime() - System.currentTimeMillis());//set后会将生存时间清零,需要重新设置有效期

  82. }

  83.  
  84. public void removeAttribute(String key) {

  85. attrs.remove(key);

  86. Cache cache = Redis.use();

  87. cache.set(this.getId(), this);

  88. }

  89.  
  90. public String getId() {

  91. return id;

  92. }

  93.  
  94. public SessionType getType() {

  95. return type;

  96. }

  97.  
  98. public long getCreateTime() {

  99. return createTime;

  100. }

  101.  
  102. public long getDestroyTime() {

  103. return destroyTime;

  104. }

  105.  
  106. public void setDestroyTime(long destroyTime) {

  107. this.destroyTime = destroyTime;

  108. }

  109. }

仿造getSession()实现逻辑在控制器基类BaseController中自定义getSession()方法,获取RedisSession:

 
  1. public class BaseController extends Controller {

  2.  
  3. private static final Logger logger = Logger.getLogger(BaseController.class);

  4. private String sessionId;

  5.  
  6. /**

  7. * 获取RedisSession

  8. * @param sessionType 会话类型

  9. * @return

  10. */

  11. @SuppressWarnings("null")

  12. public RedisSession getSession(int sessionType) {

  13. boolean isReload = false;

  14. long now = System.currentTimeMillis();

  15. RedisSession session = null;

  16. Cache cache = Redis.use();

  17. if (sessionId == null) {

  18. Cookie[] cookies = getRequest().getCookies();

  19. if (cookies != null) {

  20. for (Cookie cookie : cookies) {

  21. if (cookie.getName().equals("JSESSIONID")) {//查看请求中是否有对应的Cookie记录

  22. sessionId = cookie.getValue();//本地记录此次请求的sessionId,防止在初次请求时后台多次获取session,获取的session均不同

  23. }

  24. }

  25. }

  26. }

  27. if (sessionId != null) {

  28. session = cache.get(sessionId);//如果有,从redis中取出对应的Session

  29. if (session != null) {

  30. if (session.getType() == RedisSession.SessionType.BROWSER) {

  31. session.setDestroyTime(now + RedisSession.SessionType.BROWSER.getValue());//若会话类型为浏览器则刷新其会话有效期

  32. isReload = true;

  33. logger.info("刷新会话时间,JSESSIONID:" + session.getId() + ",延长:" + RedisSession.SessionType.BROWSER.getValue()/1000/60/60.0 + "小时");

  34. }

  35. if (session.getDestroyTime() < now) {//若会话过期,从redis中删除

  36. cache.del(session.getId());

  37. session = null;

  38. logger.info("删除过期会话,JSESSIONID:" + session.getId());

  39. }

  40. }

  41. }

  42.  
  43. if (session == null) {

  44. session = new RedisSession(sessionType);//若请求中没有对应Cookie记录,创建新的session

  45. sessionId = session.getId();//本地记录此次请求的sessionId,防止在初次请求时后台多次获取session,获取的session均不同

  46. isReload = true;

  47. logger.info("创建新会话,JSESSIONID:" + session.getId() + ",有效时间:" + (session.getDestroyTime() - now)/1000/60/60.0 + "小时");

  48. }

  49. if (isReload) {//session生命周期发生变化,需要重新redis中存储数据

  50. cache.set(session.getId(), session);//将session存入redis中

  51. cache.pexpire(session.getId(), session.getDestroyTime() - now);//设定redis数据储存有效期

  52. }

  53.  
  54. Cookie cookie = new Cookie("JSESSIONID", session.getId());

  55. cookie.setPath("/");

  56. cookie.setHttpOnly(true);

  57. getResponse().addCookie(cookie);//将cookie返回

  58.  
  59. return session;

  60. }

  61. }

说明: 
以上代码中,设想服务器给移动端和网页端同时提供服务,为了优化,但我希望移动端不需要频繁登录,就像微信一样,我将这个时间暂设一周;而网页端的话,session生存周期较短,只有半个小时,并且每次鉴权都刷新其可用时间,移动端只倒计时就可以了,一周登录一次就可以了。redis自带有过期策略,可以很好的实现这一点,同时为了保险起见,也手动验证了一下如过期,进行删除。为了避免初次请求时,多次调用getSession()生成多个session,故在创建session成功后记录其sessionId,再次调用getSession()时可对其进行验证。

七、结果测试

1.在Controller中编写两个接口,一为登录接口,登录成功,储存用户uid;二为验证登录接口,获取登录信息:

 
  1. public void redisLogin() {

  2. RedisSession session = getSession(RedisSession.SessionType.BROWSER.getType());

  3. session.setAttribute("uid", 1);

  4. renderText("登录成功,sessionId:" + session.getId());

  5. }

  6.  
  7.  
  8. public void redisCheckLogin() {

  9. RedisSession session = getSession(RedisSession.SessionType.BROWSER.getType());

  10. int uid = (int) session.getAttribute("uid");

  11. renderText("sessionId:" + session.getId() + ", uid: " + uid);

  12. }

2.配置nginx分别跳转到不同tomcat下的不同接口

 
  1. #测试登录接口跳到8080

  2. location = /tomcat1 {

  3. proxy_pass http://localhost:8080/mate/test/redisLogin;

  4. }

  5.  
  6. #校验登录接口跳到8081

  7. location = /tomcat2 {

  8. proxy_pass http://localhost:8081/mate/test/redisCheckLogin;

  9. }

3.开启redis,nginx,两个tomcat下运行同样的项目,在浏览器中调用接口进行测试。

调用tomcat1的登录接口 
这里写图片描述 
日志: 
分布式session共享 集群Session共享_第6张图片

调用tomcat2的登录接口 
这里写图片描述 
日志: 
分布式session共享 集群Session共享_第7张图片

可以看到,两个tomcat中的信息完全一样,很好的达到了我们预计的效果。

到这里,本篇的内容也已经到了尾声,写的有点啰嗦,不过总算交代了来龙去脉,虽然有点累,但好歹写完了。未来还有很多工作要做,路漫漫其修远兮,吾将上下而求索。

https://blog.csdn.net/qinmengdeCluntan/article/details/77532883?locationNum=5&fps=1

你可能感兴趣的:(分布式)