上一篇博文说到了nginx session共享问题。由于 nginx 是随机分配请求,假设一个用户登录时访问网站登录时被分配到 192.168.43.3:8080 上,然后进行了登录操作,此时该服务器上就会有该用户登录的 session 信息,然后登陆后重定向到网站首页或个人中心时,此时如果被分配到 192.168.43.3:8081 上,那么这台服务器上没有该用户 session 信息,于是又会变成未登录状态,所以由于 nginx 的负载均衡会导致 session 共享的问题。

  1. 不使用session,换用cookie
    session是存放在服务器端的,cookie是存放在客户端的,我们可以把用户访问页面产生的session放到cookie里面,就是以cookie为中转站。你访问web服务器A,产生了session然后把它放到cookie里面,当你的请求被分配到B服务器时,服务器B先判断服务器有没有这个session,如果没有,再去看看客户端的cookie里面有没有这个session,如果也没有,说明session真的不存,如果cookie里面有,就把cookie里面的sessoin同步到服务器B,这样就可以实现session的同步了。
    说明:这种方法实现起来简单,方便,也不会加大数据库的负担,但是如果客户端把cookie禁掉了的话,那么session就无从同步了,这样会给网站带来损失;cookie的安全性不高,虽然它已经加了密,但是还是可以伪造的。

  2. session存在数据库(MySQL等)中
    java可以配置将session保存在数据库中,这种方法是把存放session的表和其他数据库表放在一起,如果mysql也做了集群了话,每个mysql节点都要有这张表,并且这张session表的数据表要实时同步。
    说明:用数据库来同步session,会加大数据库的IO,增加数据库的负担。而且数据库读写速度较慢,不利于session的适时同步。

  3. ip_hash 策略
    nginx 提供了 ip_hash 策略,可以保持用户 ip 进行 hash 值计算固定分配到某台服务器上,然后只要是该 ip 则会保持分配到该服务器上,保证用户访问的是同一台服务器,那么 session 问题就不存在了。这也是解决 session 共享的一种方式,也称为黏性 session。但是假设一台 tomcat 服务器挂了的话,那么 session 也会丢失。所以比较好的方案是抽取 session。

  4. upstream_hash
    为了解决ip_hash的一些问题,可以使用upstream_hash这个第三方模块,这个模块多数情况下是用作url_hash的,但是并不妨碍将它用来做session共享。没试过真心的不明白

  5. session存在memcache或者redis中
    此种方式将将用户的登录信息存储到redis中,因为是基于内存的读取,因此效率不会是响应效率的瓶颈,cookie中存储着jsessionid,不需要加密或处理,只需要存储redis中的key保存统一客户通过cookie中的key可以准确的登录信息或是其他有效的信息,此种方式,cookie的存储不需要加密计算成本,其次redis将信息存储到缓存中,存取效率高,后面会详细介绍此种方式实现过程。

  6. 基于tomcat容器session
    此种方式在根本上实现共享session,他的实际情况是通过tomcat管理配置将一个tomct下的session复制到其他的tomcat的session池中,实现真实上的session共享;此种方式需要兼容tomcat配置及需要对其进行扩展,依赖性太强。

一:Redis 环境搭建

redis 依赖 gcc,先安装:

1
yum install -y gcc-c++

下载 redis,我使用的是 redis-3.2.11.tar.gz,上传至 linux /usr/local/redis-src / 中,解压

进入解压后目录 redis-3.2.11,执行 make 命令进行编译

安装到目录 /usr/local/redis

执行:

1
make PREFIX=/usr/local/redis install

安装完成之后将 redis 配置文件拷贝到安装目录下,redis.conf 是 redis 的配置文件,redis.conf 在 redis 源码目录, port 默认 6379。
执行命令:

1
cp /root/wyj/tools/redis/redis-3.2.11/redis.conf /usr/local/redis/

在 redis 安装目录启动和关闭 redis:

启动:
redis
这种启动方式叫做前端启动,必须保持在当前窗口,如果 ctrl + c 退出,那么 redis 也就退出了,不建议使用

那么后端启动:

首先修改 redis.conf 中 daemonize 的值,打开可以看到默认是 no,修改为 daemonize yes,启动即可。也可以在该配置文件中修改 redis 默认端口 6379 为其他值。
redis启动

1
./bin/redis-cli shutdown

至此,redis 服务器搭建完成。

二:下载相关jar

  • 环境为 tomcat7 + jdk1.6 :
    在所有需要共享 session 的服务器的 tomcat 中目录下:

    lib 目录中添加以下五个 jar 包,注意版本最好一致,不然极容易出现错误,下边的测试是可用的:

  • 环境为 tomcat7 + jdk1.7 或 1.8 :
    在所有需要共享 session 的服务器的 tomcat 中目录下:

    lib 目录中添加以下五个 jar 包,其中tomcat-redis-session-manager.jar需要重新下载编译打包,不然会报错

三:配置Tomcat

根据我这测试,是 jkd1.8+tomcat7,在 137 和 139 两台 tomcat 中加入 jar 包且进行如上配置:

上传 jar 包
加jar包1
加jar包2
修改 content.xml
tomcatcontent
启动 redis 服务,重新启动所有 tomcat,启动 nginx,刷新 nginx 页面, 两台 tomcat 页面可以看到 sessionid 值不变,关闭某台 tomcat,nginx 中 sessionid 不变,说明 session 是共享的。

请注意!!!!

context.xml 配置说明:

1
2
3
4
5
6
7
8
9
10
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
//这里是redis服务器地址
host="localhost"
//这里是redis端口,redis默认端口是6379
port="6379"
//这里是redis数据库中的标识,标识第0个,默认使用0即可
database="0"
//需要注意的是这里由于redis过期时间默认设置为60,单位是秒,session过期时间为30分钟,所以需要设置为1800对应30分钟
maxInactiveInterval="1800"/>

四:项目搭建中遇到的问题

将相关commons-pool2-2.4.1.jar,tomcat-redis-session-manager-1.2-tomcat-7-java-7.jar,jedis-2.6.2.jar,放到comcat的lib目录下,启动后报错:
报错1

查看下载的包tomcat-redis-session-manager-1.2-tomcat-7-java-7.jar或tomcat-redis-session-manager-1.2-tomcat-7.jar相关包的里面并没有类:com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve。
https://github.com/jcoleman/tomcat-redis-session-manager 直接下载源码,发现源码里面存在相应的类。同时源码(tomcat-redis-session-manager)依赖了tomcat其他的包:tomcat-juli.jar,而tomcat默认是没有这些包的,从 https://mirrors.cnnic.cn/apache/tomcat/tomcat-7/v7.0.82/bin/extras/ 下载tomcat-juli-adapters.jartomcat-juli.jar两个包,放在apache-tomcat-7.0.82\lib目录下,同时将tomcat-juli.jar放在apache-tomcat-7.0.82\bin目录下同时将编译tomcat-redis-session-manager的源码,通过相应的依赖包common-pool2.2,jedis以及tomcat-juli.jar编译,并打成自己的jar包。
打包详情如下:
导出jar包
点击 http://download.csdn.net/download/wangyuanjun008/10214996 下载
总结
这篇文章写下来可真是费了些力气,中间出了好多错,不过一个一个有耐心的解决掉,最后出来的结果还是令人挺有成就感的。毕竟心里的一块大石算是落了。以后有空再尝试一下其他几种方法。

PS : 修改配置文件的时候,一定要先备份再修改,不然出了问题都不能恢复。

参考:
Nginx 反向代理,负载均衡,redis session 共享,keepalived 高可用
搭建Nginx(负载均衡)+Redis(Session共享)+Tomcat集群
tomcat7和redis的sessoin共享问题处理
tomcat集群基于redis共享session解决方案