咱们使用单台Tomcat的时候不会有共享sesssion的疑虑,只要使用Tomcat的默认配置便可,session便可存储在Tomcat上。html
可是随着业务的扩大,增长Tomcat节点构成Tomcat集群大势所趋,分布式带来了增长更大规模并发请求的优点,可是也随之到来了一个问题,每一个Tomcat只存储来访问本身的请求产生的session,若是Tomcat-A已经为客户端C建立了会话session,那么Tomcat-B并不知道客户端已与集群中的Tomcat-A产生了会话,在访问时就会为C再建立一份session,若是是基于session的验证会话权限的接口(如用户登陆认证后才可访问的数据接口),将会致使在访问集群中不一样节点的时候重复认证。session的不共享致使原来的会话管理机制在Tomcat集群中没法工做。前端
因此,若是有一个Tomcat集群都能访问的公共session存取区就行了,基于这个概念,咱们想到了使用Redis来作这个session公共存取区,这样子的话就有一个统一管理回话的地方了。回看咱们上文提到的例子,若是Tomcat-A已经为客户端C建立了会话session,这个session信息会直接存储在公共的Redis里面,那么Tomcat-B就能够到公共session存储区里得到已为C产生的session,这样的结果是集群的公共session存取区在逻辑上就像一个tomcat的内部session存取区同样了。nginx
有了上述基本的概念,咱们就要开始真正施行了。git
Tomcat提供了一个开放的session管理和持久化的org.apache.catalina.session.ManagerBase
,继承这个抽象类并作一些简单的配置,便可让你的session管理类接管Tomcat的session读取和持久化。固然,咱们在这里使用了一个流行的开源项目:
https://github.com/jcoleman/tomcat-redis-session-manager
,它已经为咱们准备好了这样的一个管理类,只要将这个管理类配置在Tomcat中便可发挥功能。它能够帮助咱们将tomcat的session存入咱们指定的redis,甚至支持redis在sentinel模式调度的redis集群,稍后咱们也将详述这样的redis集群该如何部署。github
使用这个项目很是简单,若是在Tomcat6或Tomcat7下部署,直接使用项目release出的jar文件到Tomcat的lib
下便可,准确来讲还须要引入它的其余几个依赖(括号中为建议的):redis
tomcat-redis-session-manager-VERSION.jar(v1.2) commons-pool2-VERSION.jar(v2.2) jedis-VERSION.jar(v2.5.2)
引入后须要在tomcat下修改conf/context.xml
:apache
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" /> <Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager" host="{redis.host}" port="{redis.port}" database="{redis.dbnum}" maxInactiveInterval="60"/>
这样一来咱们的tomcat便可把session的管理交由咱们配置的redis来处理。api
须要注意的是,若是在Tomcat8下按照上述部署,会在启动时报错,笔者查看过缘由,是tomcat-redis-session-manager
最后更新的年代相隔较久,代码中使用的Tomcat api出现了过期删去的状况,在Tomcat8下会出现问题,若是想在Tomcat8下使用,须要自行对过期的api进行修改,替换成新的Tomcat api。笔者本身修改了未经严格验证的一个版本,可供使用Tomcat8的读者试用:
https://github.com/jinhaoplus/tomcat-redis-session-manager浏览器
虽然这不是本文的重点,可是使用负载均衡在搭建集群的过程当中重要性不言而喻,使用nginx默认的轮询机制,咱们能够将前端的浏览器请求转发到不一样的Tomcat实例上。
首先来说讲正向代理和反向代理,一言以蔽之:正向代理帮助内网client访问外网server用,反向代理未来自外网client的请求f转发到到内网server。
最实际的区别是使用两者时正向代理须要用户主动配置,而反向代理对用户透明,不须要用户作主动配置。
「代理」是指代人理事,即代理服务器是为其余人或机器服务的。
正向代理是替内网中的用户访问外网服务的(即代替用户去访问外网),用户和外网之间的沟通所有交由正向代理服务器完成,用户的请求不发给外网服务器而发给代理服务器让其代为处理,这个过程是隐藏用户的。
反向代理是为真正的服务节点机器服务的(即代替真正的服务节点机器去提供服务),代理服务器接收来自外界的请求,并将请求转给真正的服务节点机器,用户不与真正的机器打交道
,这个过程是隐藏真正的服务实例机器的。tomcat
nginx能够做为高效的反向代理服务器,同时起到了负载均衡的做用。若是想要使用反向代理Tomcat集群的负载,方法也很是简单,只须要在其配置nginx.conf
中将负载的Tomcat集群的实际地址加入upstream,并将locate导向配好的upstream便可:
http{ ... upstream tomcats { server <tomcat1_ip>:<tomcat1_port>; server <tomcat2_ip>:<tomcat2_port>; ... server <tomcatn_ip>:<tomcatn_port>; } ... server { listen 80; ... location / { root html; index index.html index.htm; proxy_pass http://tomcats; } } }
默认的轮询机制将每次请求都发至不一样的Tomcat实例上,以此实现负载均衡。
上文介绍的方法其实已经能够搭建一个完整的Tomcat集群了,若是系统想要一个更安全可靠的环境,那么nginx其实也能够作集群,这里略去不说,咱们想要说的是redis集群。
上面咱们已经说到redis是session的公共存储区,若是redis不幸挂掉的话将会致使致命的问题,由于无session源可取,Tomcat中有session读取的接口会直接报错。因此搭建一个redis集群仍是颇有必要的,幸亏redis对分布式HA的搭建支持得很好,原生即有一套sentinel哨兵机制便可用。
以sentinel模式启动的redis实例起到了监控者的做用,redis集群以master-slave的模式启动,消息再也不直接发给redis实例,而是发给sentinel,由sentinel同步至全部的redis实例,若是出现redismaster实例挂掉的状况,会由sentinel发现,根据配置还能够由sentinel本身组成的集群去选举产生新的master,新的master将会承担起做用,起到了灾难自动回恢复的做用。
这里来讲一下sentinel集群的配置:
咱们使用两个redis实例来组成master-slave,须要三个sentinel组成哨兵集群来监控两个redis实例,在master出现问题的时候选举产生新的master。
路径假设以下:redis1
redis2
sentinel1
sentinel2
sentinel3
配置redis1/redis.conf
为master:
bind 127.0.0.1 port 6379
配置redis2/redis.conf
为redis1的slave:
bind 127.0.0.1 port 6379 slaveof <redis1_ip> 6379
配置sentinel1/redis-sentinel.conf
:
port 26379 sentinel monitor mymaster <redis1_ip> 6379 2
指定redis1为master,slave信息会在启动后被sentinel监听到并自动写入到配置文件中,所以不须要手动写入,最后的quorum表示当有2个sentinel判断master挂掉的时候便可选举slave为新的master。
sentinel2
,sentinel3
配置相同。
这样以后启动redis和sentinel集群,便可构建一个高可用的redis集群。能够尝试一下把redis1
这个master挂掉,sentinel就会探查到而且在2个sentinel都探查到的时候即会选举产生新的master:
# +monitor master mymaster <redis1-ip> 6379 quorum 2 # +sdown master mymaster <redis1-ip> 6379
同时咱们的Tomcat配置也将改成使用sentinel集群的版本:
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" /> <Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager" sentinelMaster="mymaster" sentinels="<sentinel1-ip>:26379,<sentinel2-ip>:26379,<sentinel3-ip>:26379" maxInactiveInterval="60"/>
这个集群搭建完成后,咱们的系统将会更为稳定: