Session分布式共享

应用场景

我们知道HTTP协议本身是无状态的,因此在使用HTTP协议进行通信的过程中,需要借助Session机制进行状态的保持。然而在大型网站中,我们的服务器数量通常不止一台,可能是几十台甚至几百台之多,用户发起的HTTP请求通常要经过像Ngnix之类的负载均衡器之后,再路由到具体的服务器上,由于Session默认是存储在单机服务器内存中的,因此在分布式环境下同一个用户发送的多次HTTP请求可能会先后落到不同的服务器上,导致后面发起的HTTP请求无法拿到之前的HTTP请求存储在服务器中的Session数据,从而使得Session机制在分布式环境下失效。
cookie:cookie是本地客户端用来存储少量数据信息的,保存在客户端,用户能够很容易的获取,安全性不高,存储的数据量小。

  • Cookie的Domain和Path属性标识了这个Cookie是哪个网站发送给浏览器的;
  • Cookie的Expire属性标识了Cookie的有效时间。
  • 如果不设置过期时间,则表示这个Cookie生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。即会话cookie,会话cookie保存在内存里;设置了expire的cookie保存在硬盘上,保存在硬盘上的cookie可以在不同的浏览器进程间共享。
  • 存储在服务器的内存中,tomcat的StandardManager类将session存储在内存中,也可以持久化到file,数据库,memcache,redis等。
  • 客户端只保存sessionid到cookie中,而不会保存session,session销毁只能通过invalidate或超时,关掉浏览器并不会关闭session。

session:session是服务器用来存储部分数据信息,保存在服务器,用户不容易获取,安全性高,储存的数据量相对大,存储在服务器,会占用一些服务器资源。

  • 当浏览器第一次发送请求时,服务器自动生成一个hashtable和一个sessionID,然后通过响应发送到浏览器。tomcat生成的sessionid叫做jsessionid。
  • 当浏览器第二次发送请求时,将sessionID放在请求中一并发送到服务器上,然后取出sessionID,找到hashtable 对比。
  • session在访问tomcat服务器HttpServletRequest的getSession(true)的时候创建,tomcat的ManagerBase类提供创建sessionid的方法:随机数+时间+jvmid;

分布式解决方案

(1)Session复制

  • 支持session复制的web服务器上,通过修改服务器配置同步到其他web服务器,使各web服务器session保持一致。
  • session同步的原理是在同一个局域网里面通过发送广播来异步同步session的,一旦服务器多了,并发上来了,session需要同步的数据量就大了,需要将其他服务器上的session全部同步到本服务器上,会带来一定的网路开销,在用户量特别大的时候,会出现内存不足的情况。
  • Tomcat内部已经支持分布式架构开发管理机制,可以对tomcat修改配置来支持session复制,在集群中的几台服务器之间同步session对象,使每台服务器上都保存了所有用户的session信息,这样任何一台本机宕机都不会导致session数据的丢失,而服务器使用session时,也只需要在本机获取即可。
  • 在Tomcat安装目录下的config目录中的server.xml文件中,将注释打开,tomcat必须在同一个网关内,要不然收不到广播,同步不了session。distributable。
  • 使用tomcat内置的session同步(同步可能会产生延迟)。

(2)Session粘贴

  • 同一个用户的每次请求分发到某一web服务器,只要这一web服务器存储了session数据,便能实现会话跟踪。
  • 可以基于nginx的ip-hash策略,可以对客户端和服务器进行绑定,同一个客户端就只能访问该服务器,无论客户端发送多少次请求都被同一个服务器处理
  • 容易造成单点故障,如果有一台服务器宕机,那么该台服务器上的session信息将会丢失
    前端不能有负载均衡,如果有,session绑定将会出问题。
  • 使用Nginx中的ip绑定策略,同一个ip只能在指定的同一个机器访问(不支持负载均衡)。

(3)Session集中管理

  • 使用缓存技术,如使用redis存储session数据,集中管理所有的session,所有web服务器都能从redis存储介质中找到对应的session,实现session共享。
  • spring为我们封装好了spring-session,直接引入依赖即可。数据保存在redis中,无缝接入,不存在任何安全隐患。redis自身可做集群,搭建主从,同时方便管理。
  • 使用spring-session以及集成好的解决方案,存放在Redis中。
  • @EnableRedisHttpSession:开启Session共享功能。
  • maxInactiveIntervalInSeconds: 设置 Session 失效时间。使用 Redis Session 之后,原 Spring Boot 的 server.session.timeout 属性不再生效。
  • 当请求进来的时候,SessionRepositoryFilter会先拦截到请求,将request和response对象转换成 SessionRepositoryRequestWrapper和SessionRepositoryResponseWrapper。后续当第一次调用request的getSession方法时,会调用到SessionRepositoryRequestWrapper的getSession方法。这个方法的逻辑是先从request的属性中查找,如果找不到;再查找一个key值是"SESSION"的cookie,通过这个cookie拿到sessionId去redis中查找,如果查不到,就直接创建一个RedisSession对象,同步到Redis中(同步的时机根据配置来)。

(4)基于Cookie管理

  • 用户每次发送请求,将session保存到cookie中发送到服务器。客户端通过http协议和服务器进行cookie交互,通常用来存储一些不敏感信息。
  • 数据存储在客户端,存在安全隐患;cookie存储大小、类型存在限制;数据存储在cookie中,如果一次请求cookie过大,会给网络增加更大的开销。

(5)使用token代替session

spring session+redis实现共享

(1)安装redis服务

  • redis官方下载地址:https://github.com/microsoftarchive/redis/releases
  • 修改密码:打开redis.windows.conf文件,找到# requirepass foobared这一行去掉注释,foobared替换成自己的密码,保存。
  • 运行服务:redis-server.exe redis.windows.conf
  • 运行客户端:redis-cli.exe -h 127.0.0.1 -a 密码

(2)安装nginx

  • 链接:https://pan.baidu.com/s/1BZ1kgJbt-zdcZtKOGlBvbQ 提取码:ig72
  • 启动:start nginx
  • 关闭:nginx -s stop
  • 修改配置文件重启动:nginx -s reload

(3)添加依赖

  • spring-boot-starter-data-redis依赖于spring-data-redis 和 lettuce 。Spring
    Boot 1.0 默认使用的是 Jedis 客户端,2.0 替换成 Lettuce,但如果你从 Spring Boot 1.5.X
    切换过来,几乎感受不大差异,这是因为 spring-boot-starter-data-redis 为我们隔离了其中的差异性。
  • Lettuce 是一个可伸缩线程安全的 Redis 客户端,多个线程可以共享同一个 RedisConnection,它利用优秀 netty NIO 框架来高效地管理多个连接。
  • Spring Session 提供了一套创建和管理 Servlet HttpSession 的方案。Spring Session 提供了集群 Session(Clustered Sessions)功能,默认采用外置的 Redis 来存储 Session 数据,以此来解决 Session 共享的问题。
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--spring session 与redis应用基本环境配置,需要开启redis后才可以使用,不然启动Spring boot会报错 -->
<dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
</dependency>

(3)配置文件


spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
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
 
spring.session.store-type=redis

(4)session配置类

  • maxInactiveIntervalInSeconds: 设置 Session 失效时间,使用 Redis Session 之后,原 Spring Boot 的 server.session.timeout 属性不再生效。
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)
public class SessionConfig {
}

(5)测试

  • 添加测试方法获取 sessionid
@RequestMapping("/uid")
String uid(HttpSession session) {
    UUID uid = (UUID) session.getAttribute("uid");
    if (uid == null) {
        uid = UUID.randomUUID();
    }
    session.setAttribute("uid", uid);
    return session.getId();
}
  • 登录 Redis 输入 keys ‘sessions
  • expirations:失效时间
  • sessions:sessionId
t<spring:session:sessions:db031986-8ecc-48d6-b471-b137a3ed6bc4
t(spring:session:expirations:1472976480000

(6)服务器共享
按照上面的步骤在另一个项目中再次配置一次,启动后自动就进行了 Session 共享。

Tomcat内置Session复制方案

(1)修改server.xml,在Host节点下添加如下Cluster节点

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8">
    <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true" />
    <Channel className="org.apache.catalina.tribes.group.GroupChannel">
        <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.4" 
                    port="45564" frequency="500" dropTime="3000" />
        <!-- 这里如果启动出现异常,则可以尝试把address中的"auto"改为"localhost" -->
        <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto" port="4000" 
                  autoBind="100" selectorTimeout="5000" maxThreads="6" />
        <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
            <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender" />
        </Sender>
        <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" />
        <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor" />
    </Channel>
    <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter="" />
    <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" />
    <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" 
              deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false" />
    <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener" />
</Cluster>

nginx Ip绑定策略

每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,从而解决session共享的问题。

upstream backserver {
ip_hash;
server 192.168.0.14:88;
server 192.168.0.15:80;
}

参考:GO GO GO GO

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值