构建集中式会话的分析与实践(一)

前言

我们都知道,在传统的单机环境中,设置一个用户的会话信息(session),一般用request.getSession()获取session对象,然后就可以setAttribute(key, value)和getAttribute(key)方法操作会话了,而会话时间在servlet.xml中配置。这种基于内存的会话管理方式在简单的单机环境中使用比较普遍。

那么,在分布式环境中,又有哪些处理会话的方式呢?

分布式环境中常用的会话管理方式

一、Session Replication

即session复制,当请求落在某个机器上,将会话存在本地并复制到其他的集群节点上。

这种方式我们需要考虑复制传递会话的机制,如果是异步,那么会发生延迟;如果同步,那么又会收到网络状况的影响,性能和可用性难以保障。

一般的实现方式为配置tomcat实现session复制。

二、Session Sticky

即粘性session,根据一定的路由策略将用户请求和对应的服务器关联,使得每次请求发生的时候都能落在正确的服务器上。

这种方式避免了会话复制,同时带来了一定程度的不可用性,容易造成单点故障。

一般的实现方式是使用负载均衡器(如nginx等)进行路由、编码自定义路由等。

三、集中式会话

即将应用集群的所有节点的会话操作都指向同一个会话集群。

这种方式增加了会话的可靠性,但实现起来略复杂。

常见的实现方式有数据库、memcached、redis等存储介质来存储会话信息。

四、Cookie based


那么,本文主要说明较为流行的第三种集中式会话管理的实现方式。

在开始之前,我们有必要了解一下基于web的会话实现的一般思路:

一般我们通过在用户登录的时候,在浏览器客户端种植cookie,而后每次请求都会携带会话相关的cookie(可以理解为token或者ticket)到服务端,通过后端程序验证会话的有效性以及获取信息。

为了方便,我们通过spring子项目之一spring-session来进行分析,选择当下比较流行的Spring Session With Spring Boot。

Spring Session项目

项目地址:https://docs.spring.io/spring-session/

文档地址:https://docs.spring.io/spring-session/docs/current/reference/html5/

https://docs.spring.io/spring-session/docs/current/reference/html5/guides/boot-redis.html

原理:替代传统的Tomcat的HttpSession,Spring Session将会话信息持久化到Redis。当一个session被创建的时候,Spring Session创建一个包含session id名叫SESSION的Cookie在浏览器客户端。

Spring Session实现了HttpSession接口,优点主要有2点:

1、Clustered Sessions(集中式会话)

2、RESTful APIs(通过设置HTTP Header中cookie名为“SESSION”与客户端进行交互)。

下面我想说的主要是session在Redis中的存储结构:

文档地址:https://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository

直接看9.7.4. Storage Details,操作会话的关键命令如下:

HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000
maxInactiveInterval 1800
lastAccessedTime 1404360000000
sessionAttr:attrName someAttrValue
sessionAttr2:attrName someAttrValue2 EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100 APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe "" EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800 SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe EXPIRE spring:session:expirations1439245080000 2100

  • Redis存储session的数据结构主要是Hash结构;
  • session的超时时间并没有直接跟踪在session key本身,而是由一个特殊的session expires key替代,另外注意这里用的是APPEND命令替代SET命令;
  • 存储session的过期时间稍微长一些,因为“This means there is no need to check the expiration before using a session.”,也就是说程序执行需要时间,延迟的这5分钟主要是留给程序执行的时间,保证存储session的key的过期时间不违背判断是否过期的key的过期时间;
  • 最后两行主要是在一些场景下比如:当使用Spring Session的WebSocket时,删除key或者失效时,WebSocket的连接也需要断掉。由于redis的后台任务的优先级比较低,可能不会触发key的失效,所以每个key的有效期也会追踪到最近的分钟,也就允许我们使用一个后台任务根据配置信息通过“TTL”命令去挨个触发这些可能已失效的key。(注意,这里我用了“可能”是因为我们去删除key的时候可能由于竞争条件导致判断key是否失效出现失误,也因此不用“DEL”命令直接去触发)

我的观点

Spring Session重写了Servlet容器的request、session、filter等,使得开发只需要继续遵循servlet api,为开发提供了极大地便利;并且支持的功能以及实现方式比较丰富(而我们主要关注于Redis的实现)。另外,在会话失效时间的控制上并没有处理的很好,Redis Keyspace Notifications(原文地址:https://redis.io/topics/notifications)策略并不是十分严密:

Basically expired events are generated when the Redis server deletes the key and not when the time to live theoretically reaches the value of zero.

翻译:一般来说,失效通知事件发生在redis服务端删除key,而不是当失效时间理论上达到0值。

(下篇传送门:构建集中式会话的分析与实践(二)

转载于:https://my.oschina.net/wnjustdoit/blog/1607937

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值