关于 Tomcat 系列其他文章的传送门

会话绑定的三种方式¶
在 Tomcat 集群中,保持 session 的方式有以下三种:
- session sticky:基于会话粘性;
- 可以根据 source_ip 实现,如:
- nginx 中的
ip_hash; - haproxy 中的
source; - lvs 中的
sh。
- nginx 中的
- 或使用 cookie 实现绑定,如:
- nginx 中的
hash; - haproxy 中的
cookie。
- nginx 中的
- 可以根据 source_ip 实现,如:
- session cluster:基于 session 复制集群,每个节点都保存 session 信息;
- session server:使用一个专用的服务器保存 session,如 redis,memcached 等。
本文会逐一讲解以上 3 种会话绑定的实现方式。
一、配置基于 session sticky 的会话绑定¶
想要实现 session sticky 可以通过 source_ip 或 Cookie 的方式实现,使用 Cookie 可以控制的颗粒度比较精细,建议使用。
在 Nginx 中使用 Cookie 会话绑定要购买企业版,而 HAProxy 是可以免费使用的,之前已经介绍过配置方法,可以查看之前的博客。 《 从零玩转 HAProxy 》
本篇主要介绍如何通过 Apache 实现基于 Cookie 的 session sticky。
Apache 配置示例¶
Apache 中配置会话粘性的方法非常简单。
示例:
为了配置文件的规范整洁,建议在 /etc/httpd/conf.d 中新建子配置文件。
1 | Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED |
然后在 Tomcat 中配置好网页即可,不需要别的什么特殊配置,这里就不详细说明了。
主页测试文件可以使用这个 index.jsp。
接下来重启 httpd 使配置生效,再使用浏览器访问时就会启用基于 Cookie 的 session sticky。
可以看出,因为 Cookie 的存在,无论怎么刷新都会被调度至同一台主机。
如果把 Cookie 清理了或者更换其他浏览器,则会重新调度至新的后端并生成 Cookie。

二、配置基于 session cluster 的会话绑定¶
Tomcat 自身支持使用 Tomcat Session Replication 来配置 session cluster,集群内的 Tomcat 节点可自动同步 session 信息。

官方对集群的介绍¶
Using the above configuration will enable all-to-all session replication using the DeltaManager to replicate session deltas. By all-to-all, we mean that every session gets replicated to all the other nodes in the cluster. This works great for smaller clusters, but we don’t recommend it for larger clusters — more than 4 nodes or so. Also, when using the DeltaManager, Tomcat will replicate sessions to all nodes, even nodes that don’t have the application deployed.
To get around these problem, you’ll want to use the BackupManager. The BackupManager only replicates the session data to one backup node, and only to nodes that have the application deployed. Once you have a simple cluster running with the DeltaManager, you will probably want to migrate to the BackupManager as you increase the number of nodes in your cluster.
一些重要的默认配置¶
- 复制集群使用多播地址
228.0.0.4; - 多播地址工作在
45564端口,而端口和地址决定了主机是否在同一个集群; - 多播地址使用
java.net.InetAddress.getLocalHost().getHostAddress()配置,并确认不要使用127.0.0.1; - TCP
4000-4100的第一个可用套接字用来监听复制消息; - 监听器使用
ClusterSessionListener来配置; - 拦截器的配置有两个
- 校验 TCP 信息的完整性的
TcpFailureDetector - 消息分发拦截器
MessageDispatchInterceptor
- 校验 TCP 信息的完整性的
配置文件模板¶
启用集群的方式,就是将下列配置放置于 <engine> 或 <host> 中,生效范围不同;
以下是官方提供的 集群配置模板,仅适用于 Tomcat 8 !!!
1 | <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" |
配置中的注意事项¶
要在 Tomcat 8 容器中运行 session 复制集群,应完成以下步骤:
- 在
server.xml中添加上述配置; - 确认 IP 和 Port 不会发生冲突,集群会在
4000-4100中自动找一个端口来监听; - 网站配置文件
web.xml的<distributable/>配置必须存在; - 确定
<Engine name="Catalina" jvmRoute="node01" >中的jvmRoute参数和workers.properties的worker相匹配;
虽然使用了 session 复制集群,但是还是建议同时启用 Cookie 绑定。
配置 Tomcat 复制集群步骤详解¶
先配置后端的 Tomcat¶
1、安装好 Tomcat 8
2、创建测试网站的目录 myapp。
1 | mkdir /webapps/myapp/WEB-INF -pv |
3、创建首页测试文件
index.jsp¶
主机:Tomcat A,10.0.0.12。
1 | <%@ page language="java" %> |
主机:Tomcat B,10.0.0.13。
1 | <%@ page language="java" %> |
4、编辑 server.xml
1 | <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcatA"> // 配置 jvmRoute="tomcatA" |
5、从 /usr/local/tomcat/conf/ 下复制一份默认配置 web.xml 到 myapp 的 WEB-INF 目录下。
1 | cp /usr/local/tomcat/conf/web.xml /webapps/myapp/WEB-INF |
6、编辑 web.xml,在第一个 <servlet> 前添加以下行。
1 | <distributable/> # 这行是新加的 |
7、重启,tomcat 就配置好了。
再配置前端代理 Nginx¶
接下来安装前端的代理,这里以 Nginx 为例,也可以使用 HAProxy。
编辑 Nginx 的配置文件,在对应的上下文中添加如下配置。
1 | http { |
重载 Nginx 使配置生效,然后从浏览器访问 Nginx 的地址 http://192.168.50.11/myapp,可以看出虽然被调度至不同的后端,但是 session 信息没有变。

三、配置基于 session server 的会话绑定¶
这种架构会使用一个独立的 session server 来保存 session 信息,比如使用 Redis 或 Memcached,本文使用 Memcached 来演示。
Memcached 介绍¶

Memcached 是免费和开源,高性能,分布式的内存型键值存储系统,没有表的概念,所以在性能上比关系型数据库要快上不知道多少倍,但带来的缺点是因为结构太简单,基本没有什么多余的功能,比如主从复制、登录认证等,都需要通过智能客户端软件自己来实现,所以生产环境中还是建议使用 Redis。
缓存服务器分为 代理式 和 旁路式,像 Varnish 就属于代理式缓存,而 Memcached 和 Redis 都属于旁路式缓存。
Memcached 已经被收录在 Base 仓库,可直接使用 yum 安装:
1 | yum install memcached -y |
启动服务
1 | systemctl start memcached |
Memcached 默认监听在 11211/tcp, 11211/udp。
Memcached 配置¶
Memcached 可供配置的参数很少,都是直接通过命令行直接进行配置的,所以没有提供配置文件,可用参数:
-l:配置监听的地址;-p:配置监听的 TCP 端口,默认11211;-U:配置监听的 UDP 端口,默认11211,0代表不使用;-u:定义使用的用户;-s:配置监听的套接字;-d:以守护方式运行;-m:定义最大使用多大内存空间,超出后会使用 LRU 淘汰旧内存,默认64MB;-M:定义最大使用多大内存空间,超出后会拒绝存放新数据;-n:定义最小的存储空间单元从多少起步,默认48;-f:定义增长因子,默认为1.25;-c:最大并发连接数,默认1024个;-t:使用几个线程,默认是4个,最好小于等于 CPU 的核心数。
可以通过修改 Unit File 文件来改变默认配置,Unit File 文件 /usr/lib/systemd/system/memcached.service。
1 | [Unit] |
环境变量配置文件 /etc/sysconfig/memcached。
1 | PORT="11211" |
连接至 Memcached¶
Memcached 的传输使用了 Memcached 协议,支持二进制格式和文本格式,默认使用文本格式,可以通过 telnet 连接至 Memcached,使用 stats 可以查看软件信息和一些统计数据。
1 | # telnet 192.168.50.11 11211 |
Memcached 的控制数据的命令¶
储存:
-
<command name> <key> <flags> <exptime> <bytes> [noreply]\r\n -
<command name>set:无论如何都存储,数据不存在时存储,数据存在时更新;add:当数据不存在时存储。
| 参数 | 含义 |
|---|---|
| key | key 关键字 |
| flag | 存储额外信息 |
| exptime | 数据保存时间,0 表示永远,单位秒 |
| bytes | 存储数据的字节数 |
| noreply | 要存储的数据 |
1 | add mykey 0 0 5 |
读取:
get <key>*\r\n
1 | get mykey |
删除:
delete <key> [noreply]\r\n
清空值:
flush:清空指定值;flush_all:清空全部值。
查看状态:
stats\r\n:全部显示;stats <args>\r\n指定显示某一行。
自加和自减:
incr <key> <value> [noreply]\r\nordecr <key> <value> [noreply]\r\n
Memcached 管理工具:
memcached-tool 127.0.0.1
Memcached session manager¶
msm 介绍¶
我们可以把 session 的信息存储在单独的 Memcached 上,但是这样 Memcached 节点就成为了单点,存在 故障隐患。所以我们最少应该使用两台服务器来作为 Memcached,以保证高可用性,但是这又带来一个新问题,Memcached 自身支持的功能非常单一,主从复制、主备双活等等一概不支持,所以虽然使用了两台来储存 session 信息,但是这两台是完全独立的,一台如果宕机,其中的 session 信息就会全部丢失,为了解决这样的问题,就需要使用智能的客户端软件来实现,比如 msm。
msm 全称是 memcache-session-manager,是一个 tomcat 的会话管理器,它将会话保存在 memcached 或 Redis 中,用于高可用性、可伸缩和容错的 web 应用程序。它支持粘性和非粘性配置。对于粘性会话(使用一主一备),支持会话故障转移,而非粘性会话是缺省值(使用双主双活,对于不同的请求,由不同的 tomcat 提供),如果遇到 memcached 崩溃,通过迁移会话进行故障转移,避免了单点故障。
msm 类库的安装¶
msm 的运行需要安装以下 3 种 jar 类库:
- 会话管理器的类库 (session manager):用来管理 session 信息;
- 驱动存储的类库:用来连接至存储,执行增删改查等操作;
- 流式化的工具类库:使 K/V 序列化传输并保存的工具。
下面开始一步一步讲解 msm 类库的安装,注意:本教程仅适用于 Tomcat 8 !!!
按照官方安装文档的教程下载类库,并将下载好的 jar 包保存至 tomcat 的类库文件夹,本文使用的是 /usr/local/tomcat/lib。
我把下载好的包整理在一起了,点这里可以打包下载。msm.zip
- 下载 会话管理器 的类库:
- 打开 下载地址,进入当前的最新的版本号,下载 jar 文件 memcached-session-manager-2.3.2.jar
- 打开 下载地址,进入当前的最新的版本号,下载另一个 jar 文件 memcached-session-manager-tc8-2.3.2.jar
- 下载 驱动存储 的类库:
- 打开 下载地址,目前经过测试的版本是
v2.12.3,下载驱动 memcached 的类库 spymemcached-2.12.3.jar; - 如果使用的是 Redis,则需要使用 jedis-3.0.0.jar;
- 打开 下载地址,目前经过测试的版本是
- 下载 流式化的工具 类库,有 4 种 session 流式化的工具,都可以使用,这里使用第一种 Kryo,下载依赖的 jar 包:
- kryo-serializer:msm-kryo-serializer, kryo-serializers-0.34+, kryo-3.x, minlog, reflectasm, asm-5.x, objenesis-2.x。
- javolution-serializer:没有使用,略
- xstream-serializer:没有使用,略
- flexjson-serializer:没有使用,略
1 | asm-5.2.jar |
配置 Tomcat 使用 msm¶
安装好 msm 的 jar 类库后,要想让 Tomcat 使用 msm,需要在 server.xml 配置文件中添加一些相关配置段。
而 msm 有两种工作方式,分别可以使后端的 Memcachde 实现 1 主 1 备或双主双活,配置方法略有不同,下面开始逐一介绍。
第 1 种 – 使用粘性会话,即 1 主 1 备¶
正常情况下只有主 memcached 节点在工作,当主节点宕机时,会启用备节点工作。当主节点恢复时会把工作抢回来,备节点开始闲置。
实验拓扑图:

实验步骤:
-
创建两个 Memcached 服务器 1 和 2,对应的主机名为
node1.monster.com和node2.monster.com,并启动服务systemctl start memcached。 -
创建两个 Tomcat 服务器,Tomcat A 和 Tomcat B,将 msm 的相关配置添加到每台的
server.xml配置文件中,主页文件则使用这个 index.jsp,放置在/webapps/myapp中。 -
前端使用 Nginx 使用轮询反向代理到两个 Tomcat 服务器。
msm 的一些配置参数含义:
m1是 Memcached 节点的别名,host1.yourdomain.com为对应的主机名;failoverNodes="m1":定义 m1 为故障转移节点,也就是备用节点;requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$":忽略静态资源;transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory":- 定义使用哪一个流式化工具,本文使用 Kryo,如果使用其他的则将 Kryo 关键字替换为对应的名字即可。
1 | <Engine name="Catalina" defaultHost="localhost" jvmRoute="TomcatA"> // TomcatB 节点中应该是 TomcatB,其他参数保持一致 |
确保启动了 Tomcat 和 Memcached 的服务,然后从浏览器访问测试主页,应该可以看到始终只有 m2 在工作,只有当 m2 宕机了,m1 才会工作,并且当 m2 恢复后,m1 又会闲置。
而最重要的是,在这期间,session 信息始终没有发生变化。
第 2 种 – 不使用粘性会话,即双主双活¶
当使用双主双活时,msm 会将两个 memcached 都作为主节点使用,当其中一台宕机时,会话被转移到另一活动节点。
实验环境和上面的一样,只不过现在两台 Memcached 都是主节点了。
关于 msm 的相关配置,编辑在 server.xml 中,与 1 主 1 备有一些不同:
一些配置参数的含义:
sticky="false":定义不使用粘性会话;sessionBackupAsync="false":节点收到 session 数据会立即同步到其他节点。lockingMode="uriPattern:/path1|/path2":指定哪些 path 启用会话锁定。
1 | <Engine name="Catalina" defaultHost="localhost" jvmRoute="TomcatA"> // TomcatB 节点中应该是 TomcatB,其他参数保持一致 |
确保启动了 Tomcat 和 Memcached 的服务,然后从两个不同的浏览器,访问测试主页 http://192.168.50.11/myapp
可以看到两台 Memcached 同时在工作,m1 和 m2 同时在工作。

这时如果有一台宕机了,就会将会话转移至另一个节点。
而最重要的是,在这期间,session 信息始终没有发生变化。
使用 Redis 时不使用粘性会话的配置模板¶
当后端是 Redis 时使用的配置模板如下。
1 | <Context> |
