详解集群内Session高可用的实现原理
在这个互联网高度发达的时代,许多应用的用户动辄成百上千万,甚至上亿。为了支持海量用户的访问,应用服务器集群这种水平扩展的方式是最常用的。这种情形下,就会涉及到许多单机环境下完全不需要考虑的问题,这其中session的创建、共享和存储是最常见之一。
在单机环境中,Session的创建和存储都是由同一个应用服务器实例来完成,而存储也仅是内存中,最多会在正常的停止服务器的时候,把当前活动的Session钝化到本地,再次启动时重新加载。
而多个实例之间,Session数据是完全隔离的。而为了实现Session的高可用,多实例间数据共享是必然的,下面我们以Redis 的SessionManager实现多Tomcat实例Session共享的配置为例,我们来梳理下一般session共享的流程:
添加具体要使用的manager的Jar文件及其依赖
redis session manager依赖jedis, commons-pool, commons-pool2
对应版本的redis session manager的jar文件
在TOMCAT_HOME/conf/context.xml中增加如下配置
<Valve className="com.radiadesign.catalina.session.RedisSessionHandlerValve" />
<Manager className="com.radiadesign.catalina.session.RedisSessionManager"
host="localhost"
port="6379" database="0"
maxInactiveInterval="30" />
其中host和port等替换为对应的配置信息
启动多个Tomcat实例,以自带的examples应用为例进行验证
访问examples应用的
servlets/servlet/SessionExample,在页面中添加数据到session中,并查看页面上对应的session信息
访问另一个实例上相同应用的页面,查看session信息,两者应该是一致的
使用redis-cli查看redis中存储的对应数据,相应的sessionId对应的数据已经保存了下来
以上是一个基本的配置过程,而在这些配置与验证的步骤中,第二步是核心逻辑实现。 前面的文章,曾介绍过Tomcat的Valve,在请求处理时,Pipeline中的各个Valve的invoke方法会依次执行。Tomcat的AccessLogValve介绍
此处的session处理,就是以一个自定义Valve的形式进行的。关于Session的文章,前面也写过几篇,会附在结尾处。
以下是RedisSessionhandlerValve的invoke方法,我们看,主要是在Valve执行后进行Session的存储或移除。
public void invoke(Request request, Response response) {
try {
getNext().invoke(request, response);
} finally {
final Session session = request.getSessionInternal(false);
storeOrRemoveSession(session);
manager.afterRequest();
}
}
而session的保存和移除又是通过manager执行的。 manager.save(session); manager.remove(session);
这里,manager就是前面定义的RedisSessionManager。默认单实例情况下,我们使用的都是StandardManager,对比一下两者,标准的Manager对于session的创建和删除,都会调到其父类ManagerBase中相应的方法,
public void add(Session session) {
sessions.put(session.getIdInternal(), session);
int size = getActiveSessions();
if( size > maxActive ) {
synchronized(maxActiveUpdateLock) {
if( size > maxActive ) {
maxActive = size;
}
}
}
}
public void remove(Session session, boolean update) {
if (session.getIdInternal() != null) {
sessions.remove(session.getIdInternal());
}
}
我们来看,由于其只保存在内存的Map中protected Map<String, Session> sessions = new ConcurrentHashMap<>(),每个Tomcat实例都对于不同的map,多个实例间无法共享数据。
对应到RedisSessionManager对于session的处理,都是直接操作redis,基本代码是下面这个样:
public void save(Session session) throws IOException {
Jedis jedis = null;
Boolean error = true;
try {
RedisSession redisSession = (RedisSession) session;
Boolean sessionIsDirty = redisSession.isDirty();
redisSession.resetDirtyTracking();
byte[] binaryId = redisSession.getId().getBytes();
jedis = acquireConnection();
if (sessionIsDirty || currentSessionIsPersisted.get() != true) {
jedis.set(binaryId, serializer.serializeFrom(redisSession));
}
currentSessionIsPersisted.set(true);
jedis.expire(binaryId, getMaxInactiveInterval());
} }
移除时的操作是这样的
public void remove(Session session, boolean update) {
Jedis jedis = null;
Boolean error = true;
log.trace("Removing session ID : " + session.getId());
try {
jedis = acquireConnection();
jedis.del(session.getId());
error = false;
} finally {
if (jedis != null) {
returnConnection(jedis, error);
}
}
}
而此时,多个Tomcat实例都读取相同的Redis,session数据是共享的,其它实例的初始请求过来时,由于会执行findSession的操作,此时会从Redis中加载session,
public Session findSession(String id) throws IOException {
RedisSession session;
if (id == null) {
session = null;
currentSessionIsPersisted.set(false);
} else if (id.equals(currentSessionId.get())) {
session = currentSession.get();
} else {
session = loadSessionFromRedis(id); // 看这里,会从redis中load
if (session != null) {
currentSessionIsPersisted.set(true);
}
}
currentSession.set(session);
currentSessionId.set(id);
return session;
}
从而可以保证在一个实例被切换后,另外的实例可以继续响应同一个session的请求。
以上即为Redis实现session共享高可用的一些关键内容。有兴趣的朋友可以看下通过Memcached实现高可用,也是这个原理。顺着这个思路,如果你有将Session存储在其它地方的需求时,完全可以写一个出来,自己动手,丰衣足食。
总结一下,我们是通过自定义的Valve来实现请求后session的拦截,同时,使用自定义的SessionManager,来满足不同的session创建与存储的需求。而至于是存储在Redis/Memcached中,还是存储在DB中,只是位置的区别。原理,是一致的。
详解集群内Session高可用的实现原理的更多相关文章
- LVS集群和Keepalived高可用实战
第四十章LVS集群和Keepalived高可用实战 一.ARP协议 1.概念 地址解析协议,即ARP(AddressResolutionProtocol),是根据IP地址获取物理MAC地址的一个TCP ...
- EMQ集群搭建实现高可用和负载均衡(百万级设备连接)
一.EMQ集群搭建实现高可用和负载均衡 架构服务器规划 服务器IP 部署业务 作用 192.168.81.13 EMQTTD EMQ集群 192.168.81.22 EMQTTD EMQ集群 192. ...
- 深入解析 SQL Server 高可用镜像实现原理
作者:郭忆 本文由 网易云 发布. SQL Server 是 windows 平台 .NET 架构下标配数据库解决方案,与 Oracle.MySQL 共同构成了 DB-Engines Ranking ...
- 深入解析SQL Server高可用镜像实现原理
本文来自网易云社区 SQL Server 是windows平台.NET架构下标配数据库解决方案,与Oracle.MySQL共同构成了DB-Engines Ranking的第一阵营,在国内外企业市场中有 ...
- Redis从出门到高可用--Redis复制原理与优化
Redis从出门到高可用–Redis复制原理与优化 单机有什么问题? 1.单机故障; 2.单机容量有瓶颈 3.单机有QPS瓶颈 主从复制:主机数据更新后根据配置和策略,自动同步到备机的master/s ...
- Redis高可用之主从复制原理演进分析
Redis高可用之主从复制原理演进分析 在很久之前写过一篇 Redis 主从复制原理的简略分析,基本是一个笔记类文章. 一.什么是主从复制 1.1 什么是主从复制 主从复制,从名字可以看出,至少需要 ...
- redis详解(四)-- 高可用分布式集群
一,高可用 高可用(High Availability),是当一台服务器停止服务后,对于业务及用户毫无影响. 停止服务的原因可能由于网卡.路由器.机房.CPU负载过高.内存溢出.自然灾害等不可预期的原 ...
- ActiveMQ(七)_伪集群和主从高可用使用
一.本文目的 介绍如何在同一台虚拟机上搭建高可用的Activemq服务,集群数量包含3个Activemq,当Activemq可用数>=2时,整个集群可用. 本 ...
- ActiveMQ(七)_伪集群和主从高可用使用(转)
本文转自: https://www.cnblogs.com/gossip/p/5977489.html 一.本文目的 介绍如何在同一台虚拟机上搭建高可用的Activemq服务,集群数量 ...
随机推荐
- 函数还能这样玩儿~实现类似add(1)(2)(3)的函数
人生的第一份前端工作找到了,感谢大神主子们给半路出家自学的我这么多的机会,很高兴正式踏上客观又乐趣满满的程序员之路,哇咔咔咔. 分享一个准备面试时遇到的一个有趣的问题: 要求实现类似add(1)( ...
- 召回率与准确率[ZZ]
最近一直在做相关推荐方面的研究与应用工作,召回率与准确率这两个概念偶尔会遇到,知道意思,但是有时候要很清晰地向同学介绍则有点转不过弯来. 召回率和准确率是数据挖掘中预测.互联网中的搜索引擎等经常涉及的 ...
- VS番茄助手安装(vs2015+vs2010):卸载之前的vs助手再安装新版本
1 卸载之前的vs助手 vs2010: vs2015: 2 安装新版本
- ThinkPHP调试模式与日志记录
1.可以在config.php中进行设置,默认为关闭状态. 'APP_DEBUG' => true 打开\ThinkPHP\Common\debug.php文件可以查看debug的默认设置 ...
- php5下载,apache2.4与php5整合
step1 php5下载 百度“php下载”,找到php官网 2. 点进去,选择windows downloads 3. 同样,X86对应32位,X64对应64位,Non Thread Safe是非线 ...
- Ajax跨域访问问题-方法大全
Case I. Web代理的方式 (on Server A) 即用户访问A网站时所产生的对B网站的跨域访问请求均提交到A网站的指定页面,由该页面代替用户页面完成交互,从而返回合适的结果.此方案可以解决 ...
- 字符串copy
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<string. ...
- 学习Swift--方法
方法 方法是与某些特定类型相关联的函数.类.结构体.枚举都可以定义实例方法:实例方法为给定类型的实例封装了具体的任务与功能.类.结构体.枚举也可以定义类型方法:类型方法与类型本身相关联.类型方法与 O ...
- Entity Framework 实践系列 —— 搞好关系 - 两情相悦(双向一对一)【转载】
Entity Framework 实践系列 —— 搞好关系 - 两情相悦(双向一对一) 自从搞好了单向一对一关系,装满代码的心中塞进了挥之不去的情丝 —— 单相思.谁都知道音乐世界离不开情感,可谁又知 ...
- 利用servlet做转发,实现js跨域解决同源问题
做前端开发,避免不了跨域这个问题,跨域具体什么概念,不赘述,博客里太多.简单说下,我们用js发请求,不管post还是get,如果发请求的对象和当前web页面不在同一域名下,浏览器的同源策略会限制发请求 ...