部署负载均衡时,使用一台Nginx作为前端服务器,多台Tomcat作为后端服务器。Nginx 会根据负载策略把请求分发给Tomcat服务器。默认Tomcat服务器的session(共享会话)是不可跨越服务器的,若是同一个用户的不同请求被分发到不同的Tomcat服务器,就会造成 session 变量丢失,需要用户重新登录。在讲解Redisson之前,我先来分析以下两种方案的Session会话共享。
方案一:Nginx原生 Upstream ip_hash
ip_hash 是根据请求的IP进行分发,对于同一个IP的请求会转发到同一台Tomcat服务器。
1.Nginx 配置如下:《Nginx 1.3 集群负载均衡器 反向代理安装配置优化》
upstream webservertomcat {
ip_hash;
server 10.10.204.63:8023 weight=1 max_fails=2 fail_timeout=2;
server 10.10.204.64:8023 weight=1 max_fails=2 fail_timeout=2;
#server 127.0.0.1:8080 backup;
}
server {
listen 80;
server_name www.myname.com myname.com;
location / {
proxy_pass //webservertomcat;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
add_header Access-Control-Allow-Origin *;
}
}
以上配置生产环境不建议使用,原因有二:
1.Nginx可能不是最前端的服务器,如Squid或CDN作为前端缓存,则Nginx实际是拿Squid或CDN服务器的IP地址,达不到根据客户端请求IP分流的效果。
2.有些机构是使用动态虚拟IP或有多个出口IP,用户访问会切换IP,则无法将某个用户的请求固定到同一个Tomcat服务器。
方案二:Nginx_Upstream_jvm_route
Nginx_upstream_jvm_route 是一个第三方 nginx 扩展模块,该模块通过 session cookie 实现 session 会话粘性,如果在cookie和url中并没有session,则这只是个简单的round-robin 负载均衡。
实现方法:用户初次请求分发到后端Server时;会把响应的Server标识添加到名称为JSESSIONID的cookie中,再次请求时;jvm_route看到session中有后端服务器的名称,它就把请求转到对应的服务器上。模块地址://code.google.com/archive/p/nginx-upstream-jvm-route/
1.Nginx配置如下:
upstream tomcats_jvm_route {
server 10.10.204.63:8023 srun_id=tomcat1;
server 10.10.204.64:8023 srun_id=tomcat2;
jvm_route $cookie_JSESSIONID|sessionid reverse;
}
2.在多个Tomcat的server.xml添加配置如下:
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat2">
3.配置后,请求的名称为JSESSIONID的cookie末尾会被添加服务器标识,如下:
JSESSIONID=33775B80D8E0DB5AEAD61F86D6666C45.tomcat2;
4.生产环境不建议使用,原因:
4.1根据tomcat的特性,当server.xml配置文件中加了jvmRoute值后,会给sessionid加上jvmRoute值的后缀,根据这一特性 nginx_upstream_jvm_route对每次访问请求中的sessionId的值自动匹配对应的server。这样就会每次都访问都会到同一个Tomcat Server,这样就解决了访问不同tomcat节点session发生变化的问题。但是这种方式会出现一个问题,当一直被访问的Tomcat服务器宕机后,负载就会将用户分配到其他Server,这样就会造成session发生变化,从而就需要重新登录。
方案三:Tomcat Redis Session Manager
使用 Redis 共享 session,也就是本节中重点讲解的部分。以上两种方案均是将Session存储在Tomcat容器中,当请求从一个Tomcat转发到另一个Tomcat时,Session就会失效,因为Session在各个Tomcat中不能共享。如果使用Redis等缓存数据库系统存储Session,就可以在Tomcat实例之间实现Session共享。
Java框架redisson实现Tomcat会话管理器(Tomcat Session Manager),支持Apache Tomcat 6.x,7.x和8.x。配置方法如下:
1.编辑 TOMCAT_BASE/conf/context.xml 文件节点,添加以下内容:
<Manager className="org.redisson.tomcat.RedissonSessionManager"<Manager className="org.redisson.tomcat.RedissonSessionManager" configPath="${catalina.base}/redisson-config.json" />
注:configPath – 是指的Redisson的JSON或YAML格式的配置文件路径。配置文件详见这里。
2.拷贝相应的***两个***JAR包到指定的TOMCAT_BASE/lib目录下:
适用于 JDK 1.8+
redisson-all-3.5.0.jar
for Tomcat 6.x
redisson-tomcat-6-3.5.0.jar
for Tomcat 7.x
redisson-tomcat-7-3.5.0.jar
for Tomcat 8.x
redisson-tomcat-8-3.5.0.jar
3.按照 [Single instance mode]单实例模式的说明,创建编辑 Json 格式的 redisson-config.json 文件,添加以下内容,并将其上传到TOMCAT_BASE下,重载Tomcat服务即可。
{
"singleServerConfig":{
"idleConnectionTimeout":10000,
"pingTimeout":1000,
"connectTimeout":10000,
"timeout":3000,
"retryAttempts":3,
"retryInterval":1500,
"reconnectionTimeout":3000,
"failedAttempts":3,
"password":null,
"subscriptionsPerConnection":5,
"clientName":null,
"address": "redis://127.0.0.1:6379",
"subscriptionConnectionMinimumIdleSize":1,
"subscriptionConnectionPoolSize":50,
"connectionMinimumIdleSize":10,
"connectionPoolSize":64,
"database":0,
"dnsMonitoring":false,
"dnsMonitoringInterval":5000
},
"threads":0,
"nettyThreads":0,
"codec":null,
"useLinuxNativeEpoll":false
}
4.开始测试session。创建session.jsp并添加以下代码,上传到TOMCAT_BASE/webapps/ROOT/下。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>shared session</title>
</head>
<body>
<br>session id=<%=session.getId()%>
</body>
</html>
5.浏览器访问会出现以下会话信息
session id=1D349A69E8F5A27E1C12DEEFC304F0DC
6.现在查看Redis中是否存储了该值,如果存在会话值,此时你重启Tomcat后,该会话值也不会发生变化。
# redis-cli
127.0.0.1:6379> keys *
1) "redisson_tomcat_session:1D349A69E8F5A27E1C12DEEFC304F0DC"
已经存储成功。
如果是Redis集群或多实例模式,可参阅以下更多资料进行配置:
//github.com/redisson/redisson/wiki/14.-Integration%20with%20frameworks#144-tomcat-redis-session-manager
//github.com/redisson/redisson/wiki/2.-Configuration
//github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95 (中文文档)