Tomcat集群会话共享实战

1. 概述

1.1 集群会话共享的重要性

在Web应用集群部署中,会话共享(Session Sharing)是实现高可用和负载均衡的关键技术。当用户请求被分发到不同的Tomcat服务器时,必须保证用户的Session信息在所有服务器间共享,否则用户登录状态会丢失,导致用户体验问题。

会话共享的价值

  • 高可用性:单台服务器故障不影响用户会话
  • 负载均衡:请求可以分发到任意服务器
  • 水平扩展:可以动态增加服务器节点
  • 用户体验:用户无需重复登录

1.2 会话共享方案对比

方案 优点 缺点 适用场景
Session Sticky 实现简单,性能好 单点故障,扩展性差 小规模集群
Session Replication 无单点故障 性能损耗大,网络开销 小规模集群
Session集中存储 扩展性好,性能稳定 依赖外部存储 大规模集群(推荐)

1.3 本文内容结构

本文将从以下几个方面详细介绍Tomcat集群会话共享:

  1. Redis介绍:Redis特性、与Memcached对比
  2. Session保持方案:三种主要方案的对比
  3. Nginx+Tomcat+Redis架构:完整实现方案
  4. Redis安装配置:Redis部署和配置
  5. Tomcat Session配置:Redis Session Manager配置
  6. 测试验证:功能测试和性能测试
  7. 故障排查:常见问题和解决方案
  8. 生产环境优化:性能优化和最佳实践

2. Redis介绍

2.1 Redis简介

Redis(Remote Dictionary Server)是一个开源的、基于内存的数据结构存储系统,可以用作数据库、缓存和消息中间件。Redis支持多种数据结构,包括字符串(string)、列表(list)、集合(set)、有序集合(sorted set)和哈希(hash)。

Redis核心特性

  • 高性能:基于内存存储,读写速度极快
  • 数据结构丰富:支持多种数据结构
  • 持久化:支持RDB和AOF两种持久化方式
  • 主从复制:支持主从模式数据备份
  • 集群模式:支持Redis Cluster集群

2.2 Redis与Memcached对比

2.2.1 网络IO模型

Memcached

  • 多线程模型:使用多线程、非阻塞IO复用
  • 架构:监听主线程 + worker子线程
  • 网络层:使用libevent封装的事件库
  • 优势:多线程可以发挥多核CPU优势

Redis

  • 单线程模型:使用单线程的IO复用模型
  • 事件框架:自己封装的AeEvent事件处理框架
  • 支持:epoll、kqueue、select
  • 优势:对于纯IO操作,单线程速度优势最大
  • 劣势:计算操作会阻塞IO调度

对比总结

  • Memcached适合多核CPU环境
  • Redis单线程模型适合IO密集型操作
  • Redis的计算功能(排序、聚合)会影响整体吞吐量

2.2.2 内存管理

Memcached

  • 预分配内存池:使用slab和不同大小的chunk管理内存
  • 内存分配:value根据大小选择合适的chunk存储
  • 特点:内存利用率可能不高,但分配速度快

Redis

  • 现场申请内存:使用现场申请内存的方式存储数据
  • 内存优化:支持内存淘汰策略(LRU、LFU等)
  • 特点:内存利用率高,但可能产生内存碎片

对比总结

  • Memcached内存管理更简单,但可能浪费内存
  • Redis内存管理更灵活,但需要关注内存碎片

2.2.3 存储方式

Memcached

  • 数据结构:只支持简单的key-value存储
  • 持久化:不支持数据持久化
  • 复制:不支持主从复制
  • 特点:纯内存缓存,重启后数据丢失

Redis

  • 数据结构:支持string、list、set、zset、hash等
  • 持久化:支持RDB快照和AOF日志两种方式
  • 复制:支持主从复制和哨兵模式
  • 集群:支持Redis Cluster集群模式
  • 特点:功能丰富,可以持久化数据

对比总结

  • Memcached功能简单,适合纯缓存场景
  • Redis功能丰富,适合更多业务场景

2.2.4 数据交换机制

Redis的Swap机制

Redis并不是所有数据都一直存储在内存中。当内存使用量超过阈值时,Redis会触发swap操作:

  1. 计算swappability:根据公式 swappability = age * log(size_in_memory) 计算
  2. 选择swap对象:选择需要swap到磁盘的key对应的value
  3. 持久化到磁盘:将这些value持久化到磁盘
  4. 内存清除:在内存中清除这些数据

特点

  • 所有key信息必须保持在内存中
  • 只有value会被swap到磁盘
  • 读取时如果value不在内存,会从swap文件加载

2.3 Redis适用场景

适合使用Redis的场景

  1. Session存储:分布式Session共享
  2. 缓存:热点数据缓存
  3. 计数器:访问量、点赞数等
  4. 消息队列:简单的消息队列
  5. 排行榜:使用有序集合实现
  6. 分布式锁:实现分布式锁

不适合使用Redis的场景

  1. 大数据量存储:数据量超过内存容量
  2. 复杂查询:需要复杂SQL查询
  3. 事务强一致性:需要ACID特性

3. 如何保持Session会话

3.1 Session会话问题

在集群环境下,用户的请求可能被分发到不同的服务器,如果Session只存储在单台服务器上,会导致:

  • 用户登录状态丢失
  • 购物车信息丢失
  • 用户需要重复登录

3.2 Session保持方案

3.2.1 方案一:Session Sticky(会话粘性)

原理

  • 基于访问IP的hash策略
  • 将同一用户的请求都定位到同一台服务器
  • 单台服务器保存用户的Session信息

实现方式

1
2
3
4
5
6
# Nginx配置
upstream backend_tomcat {
ip_hash; # 基于IP的hash
server 192.168.6.241:8080;
server 192.168.6.242:8080;
}

优点

  • 实现简单,无需额外配置
  • 性能好,Session存储在本地内存
  • 无网络开销

缺点

  • 单点故障:服务器宕机,Session丢失
  • 扩展性差:增加或减少服务器需要重新hash
  • 负载不均:某些IP可能集中在某台服务器

适用场景

  • 小规模集群
  • 对Session丢失不敏感的应用
  • 临时方案

3.2.2 方案二:Session Replication(会话复制)

原理

  • 集群环境下,多台应用服务器之间同步Session
  • 每台服务器都保存所有Session的副本
  • 当Session发生变化时,广播给所有服务器

实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- Tomcat server.xml配置 -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>

优点

  • 无单点故障,任何服务器都可以处理请求
  • 实现相对简单,Tomcat自带支持
  • 无需外部依赖

缺点

  • 性能损耗大:Session复制消耗CPU和内存
  • 网络开销:广播Session数据占用网络带宽
  • 扩展性差:服务器越多,复制开销越大
  • 内存占用:每台服务器都保存所有Session

适用场景

  • 小规模集群(2-4台服务器)
  • Session数据量小
  • 对性能要求不高的应用

3.2.3 方案三:Session集中存储(推荐)

原理

  • 使用外部存储(Redis、Memcached)存储Session
  • 应用服务器接受请求时,从外部存储读取Session
  • Session变化时,更新到外部存储

架构图

1
2
3
4
5
6
7
用户请求

Nginx(负载均衡)

├──→ Tomcat-1 → Redis(Session存储)
├──→ Tomcat-2 → Redis(Session存储)
└──→ Tomcat-N → Redis(Session存储)

优点

  • 高可用:Redis支持主从和集群,高可用
  • 扩展性好:可以动态增加Tomcat服务器
  • 性能稳定:Session集中管理,性能可控
  • 无单点故障:Redis集群保证高可用

缺点

  • 依赖外部存储:需要维护Redis服务
  • 网络延迟:每次Session操作需要网络请求
  • 配置复杂:需要配置Redis和Session Manager

适用场景

  • 大规模集群(推荐)
  • 对高可用要求高的应用
  • 需要水平扩展的场景

3.3 方案选择建议

选择原则

  1. 小规模集群(<4台):Session Replication
  2. 中大规模集群(>4台):Session集中存储(Redis)
  3. 临时方案:Session Sticky
  4. 高可用要求:Session集中存储(Redis)

4. Nginx+Tomcat+Redis实现负载均衡、Session共享

4.1 实验环境

4.1.1 主机规划

主机 操作系统 IP地址 角色
Nginx CentOS 7.4 192.168.6.240 负载均衡器
Tomcat-1 CentOS 7.4 192.168.6.241 应用服务器1
Tomcat-2 CentOS 7.4 192.168.6.242 应用服务器2
Redis CentOS 7.4 192.168.6.244 Session存储
MySQL CentOS 7.4 192.168.6.244 数据库

4.1.2 实验拓扑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
                  Internet
|

[Nginx]
192.168.6.240
|
┌──────────────┴──────────────┐
↓ ↓
[Tomcat-1] [Tomcat-2]
192.168.6.241 192.168.6.242
| |
└──────────────┬──────────────┘

[Redis]
192.168.6.244
|

[MySQL]
192.168.6.244

架构说明

  • Nginx作为反向代理,实现负载均衡
  • 将静态资源直接返回,动态请求转发给Tomcat
  • 两台Tomcat服务器共享Redis中的Session
  • MySQL作为后端数据库

4.2 Nginx安装配置

4.2.1 安装Nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 安装依赖
yum install pcre pcre-devel -y
yum install openssl openssl-devel -y

# 创建目录
mkdir -p /home/oldboy/tools
cd /home/oldboy/tools/

# 下载Nginx
wget -q http://nginx.org/download/nginx-1.6.2.tar.gz

# 创建Nginx用户
useradd nginx -s /sbin/nologin -M

# 解压和编译
mkdir /application/
tar xf nginx-1.6.2.tar.gz
cd nginx-1.6.2

# 编译安装
./configure \
--user=nginx \
--group=nginx \
--prefix=/application/nginx1.6.2 \
--with-http_stub_status_module \
--with-http_ssl_module \
--with-http_realip_module

make && make install

# 创建软链接
ln -s /application/nginx1.6.2/ /application/nginx

4.2.2 配置Nginx反向代理

编辑nginx.conf

1
vim /application/nginx/conf/nginx.conf

完整配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
worker_processes  4;

events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;

sendfile on;
keepalive_timeout 65;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

# Tomcat负载均衡配置
upstream backend_tomcat {
# 不使用ip_hash,实现真正的负载均衡
# ip_hash; # 如果使用ip_hash,则不能实现真正的负载均衡

server 192.168.6.241:8080 weight=1 max_fails=2 fail_timeout=10s;
server 192.168.6.242:8080 weight=1 max_fails=2 fail_timeout=10s;
# server 192.168.6.243:8080 weight=1 max_fails=2 fail_timeout=10s;

# 健康检查(需要nginx_upstream_check_module模块)
# check interval=3000 rise=2 fall=3 timeout=1000;
}

server {
listen 80;
server_name www.98yz.cn;
charset utf-8;

access_log logs/access.log main;
error_log logs/error.log;

# 静态资源直接返回
location / {
root html;
index index.jsp index.html index.htm;
}

# 动态请求转发到Tomcat
location ~* \.(jsp|do)$ {
proxy_pass http://backend_tomcat;
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;
proxy_set_header X-Forwarded-Proto $scheme;

# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;

# 错误处理
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
}

# 健康检查接口(可选)
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow 192.168.6.0/24;
deny all;
}
}
}

配置说明

  • upstream backend_tomcat:定义Tomcat服务器组
  • weight:权重,可以调整流量分配
  • max_fails:最大失败次数
  • fail_timeout:失败超时时间
  • proxy_next_upstream:错误时切换到下一台服务器

4.2.3 启动Nginx

1
2
3
4
5
6
7
8
# 测试配置
/application/nginx/sbin/nginx -t

# 启动Nginx
/application/nginx/sbin/nginx

# 检查端口
netstat -tunlp | grep nginx

4.3 安装部署Tomcat应用服务器

4.3.1 安装JDK

在Tomcat-1和Tomcat-2节点上安装JDK

1
2
3
4
5
# 方式1:使用YUM安装(简单)
yum install java-1.8.0-openjdk java-1.8.0-openjdk-devel -y

# 验证安装
java -version

输出示例

1
2
3
openjdk version "1.8.0_201"
OpenJDK Runtime Environment (build 1.8.0_201-b09)
OpenJDK 64-Bit Server VM (build 25.201-b09, mixed mode)

方式2:手动安装JDK(推荐生产环境)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 下载JDK
cd /soft/src
wget https://download.oracle.com/java/8/latest/jdk-8uXXX-linux-x64.tar.gz

# 解压
tar xf jdk-8uXXX-linux-x64.tar.gz -C /application/

# 创建软链接
ln -s /application/jdk1.8.0_XXX /application/jdk

# 配置环境变量
cat >> /etc/profile << 'EOF'
export JAVA_HOME=/application/jdk
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
EOF

source /etc/profile

4.3.2 安装Tomcat

在Tomcat-1节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 创建目录
mkdir -p /soft/src
cd /soft/src

# 下载Tomcat
wget https://mirrors.aliyun.com/apache/tomcat/tomcat-9/v9.0.16/bin/apache-tomcat-9.0.16.tar.gz

# 解压
tar xf apache-tomcat-9.0.16.tar.gz -C /soft

# 创建实例目录
cd /soft
cp -r apache-tomcat-9.0.16/ tomcat-8080

# 进入Tomcat目录
cd tomcat-8080/

在Tomcat-2节点执行相同操作

4.3.3 配置Tomcat

编辑server.xml(Tomcat-1)

1
vim /soft/tomcat-8080/conf/server.xml

修改Engine配置

1
2
<!-- 设置jvmRoute,用于Session粘性(可选) -->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat-1">

修改Host配置

1
2
3
4
5
6
<!-- 修改默认虚拟主机,指向自定义应用目录 -->
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<!-- 配置Context,指向自定义应用目录 -->
<Context docBase="/web/webapp1" path="" reloadable="true"/>
</Host>

Tomcat-2配置类似,但jvmRoute不同

1
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat-2">

4.3.4 创建测试应用

在Tomcat-1节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 创建应用目录
mkdir -p /web/webapp1
cd /web/webapp1

# 创建测试页面
cat > index.jsp << 'EOF'
<%@page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
<head>
<title>tomcat-1</title>
</head>
<body>
<h1><font color="red">Session serviced by tomcat-1</font></h1>
<table align="center" border="1">
<tr>
<td>Session ID</td>
<td><%=session.getId() %></td>
<% session.setAttribute("abc","abc");%>
</tr>
<tr>
<td>Created on</td>
<td><%= session.getCreationTime() %></td>
</tr>
<tr>
<td>Server Info</td>
<td>tomcat-1 (192.168.6.241)</td>
</tr>
</table>
</body>
</html>
EOF

在Tomcat-2节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 创建应用目录
mkdir -p /web/webapp1
cd /web/webapp1

# 创建测试页面(标题不同,用于区分)
cat > index.jsp << 'EOF'
<%@page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
<head>
<title>tomcat-2</title>
</head>
<body>
<h1><font color="blue">Session serviced by tomcat-2</font></h1>
<table align="center" border="1">
<tr>
<td>Session ID</td>
<td><%=session.getId() %></td>
<% session.setAttribute("abc","abc");%>
</tr>
<tr>
<td>Created on</td>
<td><%= session.getCreationTime() %></td>
</tr>
<tr>
<td>Server Info</td>
<td>tomcat-2 (192.168.6.242)</td>
</tr>
</table>
</body>
</html>
EOF

4.3.5 启动Tomcat

1
2
3
4
5
6
7
8
9
# 启动Tomcat
/soft/tomcat-8080/bin/startup.sh

# 验证启动
netstat -tunlp | grep java
ps -ef | grep [j]ava

# 查看日志
tail -f /soft/tomcat-8080/logs/catalina.out

4.3.6 验证负载均衡

访问测试

  • 访问:http://192.168.6.240/
  • 多次刷新,观察Session ID和服务器信息

预期结果

  • 请求会分发到不同的Tomcat服务器
  • Session ID每次可能不同(因为还没有配置Session共享)

5. 安装Redis

5.1 Redis安装方式

5.1.1 方式一:YUM安装(简单)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 安装EPEL源
yum install epel-release -y

# 安装Redis
yum install redis -y

# 启动Redis
systemctl start redis
systemctl enable redis

# 验证
redis-cli ping
# 应返回:PONG

5.1.2 方式二:源码编译安装(推荐生产环境)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 安装依赖
yum install gcc gcc-c++ -y

# 下载Redis
cd /soft/src
wget http://download.redis.io/releases/redis-6.2.6.tar.gz

# 解压
tar xf redis-6.2.6.tar.gz
cd redis-6.2.6

# 编译安装
make && make install PREFIX=/application/redis

# 创建软链接
ln -s /application/redis /application/redis-6.2.6

# 创建配置目录
mkdir -p /application/redis/conf
mkdir -p /application/redis/data
mkdir -p /application/redis/logs

5.1.3 方式三:使用宝塔面板安装

1
2
3
4
# 如果使用宝塔面板,可以通过面板安装Redis
# 1. 登录宝塔面板
# 2. 软件商店 → 搜索Redis → 安装
# 3. 配置Redis密码和端口

5.2 Redis配置

5.2.1 基本配置

编辑redis.conf

1
vim /application/redis/conf/redis.conf

关键配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 绑定IP(0.0.0.0表示监听所有IP)
bind 0.0.0.0

# 端口
port 6379

# 守护进程模式
daemonize yes

# PID文件
pidfile /var/run/redis_6379.pid

# 日志文件
logfile /application/redis/logs/redis.log

# 数据目录
dir /application/redis/data

# 持久化配置
save 900 1
save 300 10
save 60 10000

# 密码配置(重要)
requirepass pwd@123

# 最大内存
maxmemory 2gb
maxmemory-policy allkeys-lru

5.2.2 启动Redis

1
2
3
4
5
6
7
8
9
10
# 使用配置文件启动
/application/redis/bin/redis-server /application/redis/conf/redis.conf

# 或使用systemd(如果使用YUM安装)
systemctl start redis
systemctl enable redis

# 验证
redis-cli -a pwd@123 ping
# 应返回:PONG

5.2.3 测试Redis

1
2
3
4
5
6
7
8
9
# 连接Redis
redis-cli -a pwd@123

# 测试命令
127.0.0.1:6379> set test "hello"
OK
127.0.0.1:6379> get test
"hello"
127.0.0.1:6379> exit

6. 配置Tomcat Session Redis同步

6.1 下载Tomcat Redis Session Manager

6.1.1 下载地址

GitHub地址

6.1.2 下载和安装

在Tomcat-1节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 进入Tomcat lib目录
cd /soft/tomcat-8080/lib/

# 下载Tomcat Redis Session Manager
wget https://github.com/ran-jit/tomcat-cluster-redis-session-manager/releases/download/2.0.4/tomcat-cluster-redis-session-manager.zip

# 解压
unzip tomcat-cluster-redis-session-manager.zip

# 复制JAR包到lib目录
cp tomcat-cluster-redis-session-manager/lib/* ./

# 复制配置文件到conf目录
cp tomcat-cluster-redis-session-manager/conf/redis-data-cache.properties ../conf/

在Tomcat-2节点执行相同操作

6.2 配置Redis连接

6.2.1 编辑Redis配置文件

编辑redis-data-cache.properties

1
vim /soft/tomcat-8080/conf/redis-data-cache.properties

配置内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Redis data-cache configuration

# Redis服务器地址和端口(支持多个,用逗号分隔)
# 格式:host:port, host:port, ...
redis.hosts=192.168.6.244:6379

# Redis密码(单机模式)
redis.password=pwd@123

# 是否启用Redis集群模式(false表示单机模式)
redis.cluster.enabled=false

# Redis数据库编号(默认0)
redis.database=0

# Redis连接超时时间(毫秒,默认2000)
redis.timeout=2000

# 连接池配置(可选)
# redis.maxTotal=200
# redis.maxIdle=20
# redis.minIdle=5
# redis.maxWaitMillis=10000

集群模式配置(如果使用Redis Cluster)

1
2
3
4
5
6
7
8
# 启用集群模式
redis.cluster.enabled=true

# Redis集群节点(至少3个)
redis.hosts=192.168.6.244:6379,192.168.6.245:6379,192.168.6.246:6379

# 集群模式不需要密码(或使用统一密码)
# redis.password=

6.3 配置Tomcat Context

6.3.1 编辑context.xml

编辑context.xml

1
vim /soft/tomcat-8080/conf/context.xml

添加Redis Session Manager配置

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- Redis Session Manager Valve -->
<Valve className="tomcat.request.session.redis.SessionHandlerValve" />

<!-- Redis Session Manager -->
<Manager className="tomcat.request.session.redis.SessionManager" />
</Context>

完整context.xml示例

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- 资源定义 -->
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000"
username="root" password="password"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://192.168.6.244:3306/testdb"/>

<!-- Redis Session Manager -->
<Valve className="tomcat.request.session.redis.SessionHandlerValve" />
<Manager className="tomcat.request.session.redis.SessionManager" />
</Context>

6.4 配置Session超时时间

6.4.1 编辑web.xml

编辑web.xml

1
vim /soft/tomcat-8080/conf/web.xml

添加Session配置

1
2
3
4
5
6
7
8
9
10
<!-- Session配置 -->
<session-config>
<!-- Session超时时间(分钟) -->
<session-timeout>60</session-timeout>
<!-- Cookie配置 -->
<cookie-config>
<http-only>true</http-only>
<secure>false</secure>
</cookie-config>
</session-config>

6.5 启动Tomcat并验证

6.5.1 启动Tomcat

1
2
3
4
5
# 启动Tomcat
/soft/tomcat-8080/bin/startup.sh

# 查看日志
tail -f /soft/tomcat-8080/logs/catalina.out

检查日志中是否有Redis连接信息

1
2
INFO: Redis Session Manager initialized
INFO: Connected to Redis at 192.168.6.244:6379

6.5.2 验证Session共享

测试步骤

  1. 访问应用

    • 访问:http://192.168.6.240/
    • 记录Session ID
  2. 多次刷新

    • 多次刷新页面
    • 观察Session ID是否一致
    • 观察服务器信息是否变化
  3. 验证结果

    • Session ID应该一致:说明Session共享成功
    • 服务器信息可能变化:说明负载均衡正常

6.5.3 验证Redis中的Session

1
2
3
4
5
6
7
8
9
10
11
# 连接Redis
redis-cli -a pwd@123

# 查看所有Session key
127.0.0.1:6379> KEYS *session*

# 查看Session内容
127.0.0.1:6379> GET "session:xxxxx"

# 查看Session TTL
127.0.0.1:6379> TTL "session:xxxxx"

7. 测试验证

7.1 功能测试

7.1.1 Session一致性测试

测试脚本

1
2
3
4
5
6
# 使用curl测试
for i in {1..10}; do
echo "Request $i:"
curl -s -c /tmp/cookies.txt -b /tmp/cookies.txt http://192.168.6.240/ | grep "Session ID"
sleep 1
done

预期结果

  • 所有请求的Session ID应该相同
  • 说明Session共享成功

7.1.2 负载均衡测试

测试脚本

1
2
3
4
# 测试负载均衡
for i in {1..20}; do
curl -s http://192.168.6.240/ | grep "Server Info"
done | sort | uniq -c

预期结果

  • 请求应该分发到不同的Tomcat服务器
  • 说明负载均衡正常

7.1.3 故障转移测试

测试步骤

  1. 停止一台Tomcat

    1
    /soft/tomcat-8080/bin/shutdown.sh
  2. 继续访问

    • 访问应该正常
    • Session应该保持
  3. 恢复Tomcat

    1
    /soft/tomcat-8080/bin/startup.sh

7.2 性能测试

7.2.1 使用Apache Bench测试

1
2
3
4
5
6
7
8
9
10
# 安装ab工具
yum install httpd-tools -y

# 压力测试
ab -n 1000 -c 100 -C "JSESSIONID=xxx" http://192.168.6.240/

# 参数说明:
# -n: 总请求数
# -c: 并发数
# -C: Cookie(保持Session)

7.2.2 监控指标

监控项

  • 响应时间:平均响应时间、最大响应时间
  • 吞吐量:每秒请求数(QPS)
  • 错误率:4xx、5xx错误比例
  • Redis性能:Redis连接数、命令执行时间

8. 故障排查

8.1 常见问题

8.1.1 Redis连接失败

问题现象

  • Tomcat启动失败
  • 日志显示Redis连接错误

排查步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 检查Redis是否运行
redis-cli -a pwd@123 ping

# 2. 检查网络连通性
telnet 192.168.6.244 6379

# 3. 检查防火墙
firewall-cmd --list-ports
iptables -L -n

# 4. 检查Redis配置
grep -E "bind|requirepass" /application/redis/conf/redis.conf

# 5. 检查Tomcat配置
cat /soft/tomcat-8080/conf/redis-data-cache.properties

解决方案

  • 确保Redis服务运行
  • 检查防火墙规则
  • 验证密码配置
  • 检查网络连通性

8.1.2 Session不共享

问题现象

  • 刷新页面Session ID变化
  • 登录状态丢失

排查步骤

1
2
3
4
5
6
7
8
9
10
11
12
# 1. 检查Redis中是否有Session
redis-cli -a pwd@123
KEYS *session*

# 2. 检查Tomcat日志
tail -f /soft/tomcat-8080/logs/catalina.out | grep -i session

# 3. 检查context.xml配置
cat /soft/tomcat-8080/conf/context.xml | grep -i session

# 4. 检查JAR包
ls -lh /soft/tomcat-8080/lib/ | grep redis

解决方案

  • 确保JAR包正确安装
  • 检查context.xml配置
  • 验证Redis连接
  • 检查Session Manager配置

8.1.3 性能问题

问题现象

  • 响应时间变慢
  • Redis连接超时

排查步骤

1
2
3
4
5
6
7
8
9
10
11
# 1. 检查Redis性能
redis-cli -a pwd@123 --latency

# 2. 检查Redis连接数
redis-cli -a pwd@123 INFO clients

# 3. 检查Redis内存使用
redis-cli -a pwd@123 INFO memory

# 4. 检查Tomcat线程
jstack <pid> | grep -i thread

解决方案

  • 优化Redis配置
  • 增加连接池大小
  • 优化Session数据大小
  • 考虑使用Redis集群

9. 生产环境优化

9.1 Redis优化

9.1.1 内存优化

1
2
3
4
5
6
7
8
9
10
# redis.conf
# 设置最大内存
maxmemory 4gb

# 内存淘汰策略
maxmemory-policy allkeys-lru

# 启用内存压缩
hash-max-ziplist-entries 512
hash-max-ziplist-value 64

9.1.2 持久化优化

1
2
3
4
5
6
7
8
# RDB配置
save 900 1
save 300 10
save 60 10000

# AOF配置
appendonly yes
appendfsync everysec

9.1.3 连接池优化

1
2
3
4
5
6
# redis-data-cache.properties
# 连接池配置
redis.maxTotal=200
redis.maxIdle=50
redis.minIdle=10
redis.maxWaitMillis=5000

9.2 Tomcat优化

9.2.1 Session优化

1
2
3
4
5
<!-- web.xml -->
<session-config>
<!-- 减少Session超时时间 -->
<session-timeout>30</session-timeout>
</session-config>

9.2.2 JVM优化

1
2
3
4
5
6
7
# catalina.sh
JAVA_OPTS="$JAVA_OPTS \
-server \
-Xms2g \
-Xmx2g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200"

9.3 高可用方案

9.3.1 Redis主从复制

1
2
3
# 从服务器配置
slaveof 192.168.6.244 6379
masterauth pwd@123

9.3.2 Redis哨兵模式

1
2
3
# sentinel.conf
sentinel monitor mymaster 192.168.6.244 6379 2
sentinel auth-pass mymaster pwd@123

9.3.3 Redis集群模式

1
2
3
# redis-data-cache.properties
redis.cluster.enabled=true
redis.hosts=192.168.6.244:6379,192.168.6.245:6379,192.168.6.246:6379

10. 总结

10.1 核心要点

  1. Session共享方案:Session Sticky、Session Replication、Session集中存储
  2. Redis优势:高性能、数据结构丰富、支持持久化
  3. 架构设计:Nginx负载均衡 + Tomcat集群 + Redis Session存储
  4. 配置要点:Redis连接配置、Tomcat Context配置
  5. 高可用:Redis主从、哨兵、集群模式

10.2 架构师建议

  1. 方案选择

    • 小规模集群:Session Replication
    • 大规模集群:Redis Session存储(推荐)
  2. 高可用设计

    • Redis主从复制
    • Redis哨兵模式
    • Redis集群模式
  3. 性能优化

    • Redis内存优化
    • 连接池优化
    • Session数据优化

10.3 最佳实践

  1. 安全配置:Redis密码、防火墙规则
  2. 监控告警:Redis性能监控、Session监控
  3. 备份恢复:Redis数据备份、Session恢复
  4. 容量规划:根据用户量规划Redis容量

相关文章