技能目标:
· 了解Session共享解决方案
· 掌握Tomcat集群Session共享方法
8.1案例分析
8.1.1案例概述
HTTP协议属于无状态协议,当完成数据交换时,客户端与服务器端的连接就会关闭;但再次进行数据交换时,就需要建立新的连接。这意味着服务器无法从连接上跟踪会话,客户端与服务器端之间的联系是离散的、非连续性的。当用户在同一网站的多个页面转换时,服务器无法确定是否是同一用户。例如:用户登录淘宝后进入商品页购买商品,但是服务器不能将用户和商品对应起来。因此,需要通过Session会话跟踪技术来实现追踪定位,以保证用户在多次切换时,服务器可以保存该用户的相关信息,使用Session技术可以实现会话跟踪。
8.1.2案例前置知识点
1. Session概述
Session(中文为"会话")在计算机中被称之为"会话控制",是指一个终端用户与交互系统进行通信的时间间隔,通常是指从登录进入系统开始到注销退出系统结束之间的所用时间。Session所包括的客户端、服务器端以及不中断的操作时间,决定Session的不同。例如:用户A和服务器C建立连接时所处的Session与用户B和服务器C建立连接时所处的Session是两个完全不同的Session。
2. Session共享方案
对于Web应用集群的技术而言,最大的难点是在集群中多个节点之间保持数据的一致性,其中会话(Session)信息是这些数据中最重要的一部分。要实现这一点, 可以采用以下两种方式。
· 把所有Session数据放到一台服务器上或者数据库中,集群中的所有节点通过访问这台Session服务器来获取数据;
· 在集群中的所有节点间进行Session数据的同步拷贝,任何一个节点均保存了所有的Session数据。
在集群中可以通过以下几种方案实现Session共享:
(1) 请求精确定位
例如基于访问IP地址的Hash策略,即当前用户的请求都集中定位到一台服务器中,这样单台服务器就会保存用户的Session登录信息。但是如果宕机,则等同于单点故障,保存用户的Session登录信息就会丢失,会话不复制。
(2) Session复制共享
例如Tomcat自带Session共享,主要是指集群环境下多台应用服务器之间同步Session,使Session保持一致,对外则是保持透明。 如果其中一台服务器发生故障,根据负载均衡的原理,调度器会遍历寻找可用节点来分发请求。由于Session已同步,所以能够保证用户的Session信息不会丢失,也就是所谓的会话复制。
但是这个方案存在以下几个缺点:
· 必须在同一种中间件之间完成,例如Tomcat与Tomcat之间;
· Session复制带来的性能损失会快速增加,特别是当Session中保存了较大的对象,而且对象变化较快时,性能下降更加显著,会大量消耗系统性能。这种特性使得Web应用的水平扩展受到了限制。
· Session内容通过广播同步给成员,会造成网络流量瓶颈。
(3) 基于高速缓存数据库的Session共享
基于Memcached/Redis缓存的Session共享,即使用高速缓存数据库存取Session信息,应用服务器接受新请求将Session信息保存在高速缓存数据库中。应用服务器发生故障时,调度器会遍历寻找可用节点来分发请求,当应用服务器发现Session不在本机内存时,则去高速缓存数据库中查找。如果找到则复制到本机,这样实现Session共享和高可用。
3.Tomcat集群Session同步方案
Tomcat集群Session同步方案有以下几种方式:
(1)使用Tomcat自带的Cluster方式
多个Tomcat之间自动实时复制Session信息,配置起来比较简单。但这个方案的效率比较低。
(2)利用Nginx的基于访问IP地址的Hash路由策略
保证访问的IP地址始终被路由到同一个Tomcat上。每个请求按访问IP地址的Hash结果分配,这样每个访客固定访问一个后端服务器,可以解决Session的问题。
(3)利用Nginx插件实现Tomcat集群和Session同步
nginx-upstream-jvm-route是一个Nginx的扩展模块,用来实现基于Cookie的Session Sticky(会话粘滞)的功能。但是这个模块的补丁在Nginx1.4版本之后就没有再更新了,所以Nginx1.4之后版本跟该模块就不兼容。
(4)利用Memcached实现(MSM工具)
Memcached存储Session,并把多个Tomcat的Session集中管理,前端利用Nginx实现负载均衡和动静态资源分离,在兼顾系统水平扩展的同时又能保证较高的性能。其原理是通过MSM工具把Tomcat的Session序列化后保存到Memcached里面,从而实现Session共享。
MSM是一个高可用的Tomcat Session共享解决方案,除了可以从本机内存快速读取Session信息(仅针对黏性Session)外,还可使用Memcached存取Session,以实现高可用。传统Tomcat集群,会话复制随着结点数增多,扩展性成为瓶颈。MSM使用memcached完成统一管理Tomcat会话,避免Tomcat节点之间过多会话复制。
MSM利用Value(Tomcat 阀)对Request进行跟踪。Request请求到来时,从Memcached加载Session,Request请求结束时将Tomcat Session更新至Memcached,以达到Session共享DE 目的,支持Sticky和Non-Sticky模式:
· Sticky :会话粘连模式(黏性Session)。客户端在一台Tomcat实例上完成登录后,以后的请求均会根据IP直接绑定到该Tomcat实例。
· Non-Sticky:会话非粘连模式(非粘性Session)。客户端的请求是随机分发,多台Tomcat实例都会收到请求。
(5)利用Redis实现
使用Redis不仅仅是因为它可以将缓存的Session持久化,还因为它支持的单个对象比较大,而且数据类型丰富。也就是说Redis不仅仅可以用于缓存 Session,还可以做其他用途。Redis这种方式目前还不支持Tomcat8环境(现在网上插件不支持Tomcat8,想要支持Tomcat8的话,则需修改插件jar包的源代码)。
在Tomcat集群中,当一个节点出现故障,虽然有高可用集群来负责故障转移,但用户的Session信息如何保持呢?下面通过案例的方式具体介绍上述所说的第4种Tomcat集群Session同步方案:使用MSM(Memcached-Session-Manager)进行Session复制同步,即利用MSM+Memcached实现Session共享。
8.1.3案例环境
1.案例环境
本案例的实验环境如表8-1所示。
![27f8c7b2aa81465d049fdc1f9e8d4a58.png](https://i-blog.csdnimg.cn/blog_migrate/e04dd5a46a84ea36c85bed6988e1d5e8.jpeg)
表8-1 实验环境
案例实验环境的网络拓扑如图8.1所示。
![746ac6e99cb276496e6b47680d13c214.png](https://i-blog.csdnimg.cn/blog_migrate/6973e41a76e33f3b48f3c4d2ac5ec9d9.jpeg)
图8.1 实验拓扑
2.案例需求
本案例的需求如下:
利用Nginx实现Tomcat集群的负载均衡和动静态资源分离,并结合Memcached实现Session共享。
3.案例实现思路
本案例的实现思路如下:
(1) 为主机进行基本配置;
(2) 安装配置Tomcat服务;
(3) 安装配置Nginx服务;
(4) 安装配置Memcached服务;
(5) 配置Session共享。
8.2案例实施
8.2.1主机配置
1.设置主机名
设置IP地址为192.168.9.91的主机的主机名为nginx-node,具体操作如下所示。
[root@localhost ~]# hostnamectl set-hostname nginx-node
[root@localhost ~]# bash
[root@nginx-node ~]#
设置IP地址为192.168.9.71的主机的主机名为tomcat-node1,具体操作如下所示。
[root@localhost ~]# hostnamectl set-hostname tomcat-node1
[root@localhost ~]# bash
[root@tomcat-node1 ~]#
设置IP地址为192.168.9.74的主机的主机名为tomcat-node2,具体操作如下所示。
[root@localhost ~]# hostnamectl set-hostname tomcat-node2
[root@localhost ~]# bash
[root@tomcat-node2 ~]#
设置IP地址为192.168.9.77的主机的主机名为mem-node1,具体操作如下所示。
[root@localhost ~]# hostnamectl set-hostname mem-node1
[root@localhost ~]# bash
[root@mem-node1 ~]#
设置IP地址为192.168.9.79的主机的主机名为mem-node2,具体操作如下所示。
[root@localhost ~]# hostnamectl set-hostname mem-node2
[root@localhost ~]# bash
[root@mem-node2 ~]#
2.关闭防火墙与SELinux
关闭所有主机上的Firewalld防火墙与SELinux,并重启主机使配置生效。下面以nginx-node主机为例进行操作演示。
[root@nginx-node ~]# systemctl stop firewalld
[root@nginx-node ~]# systemctl disable firewalld
Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.
Removed symlink /etc/systemd/system/basic.target.wants/firewalld.service.
[root@nginx-node ~]# sed -i '/SELINUX/s/enforcing/disabled/' /etc/selinux/config
[root@nginx-node ~]# reboot
[root@nginx-node ~]# systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)
Active: inactive (dead)
Docs: man:firewalld(1)
[root@nginx-node ~]# getenforce
Disabled
8.2.2安装Tomcat
1.安装Java环境
在tomcat-node1与tomcat-node2两台主机上都与需要安装Java环境,下面以tomcat-node1主机为例进行操作演示。
[root@tomcat-node1 ~]# yum install -y java
[root@tomcat-node1 ~]# java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)
2.安装Tomcat
在tomcat-node1与tomcat-node2两台主机上都与需要安装Tomcat,下面以tomcat-node1主机为例进行操作演示。
[root@tomcat-node1 ~]# cd /usr/local/src/
[root@tomcat-node1 src]# ll apache-tomcat-8.5.16.tar.gz
-rw-r--r-- 1 root root 9417469 7月 27 2017 apache-tomcat-8.5.16.tar.gz
[root@tomcat-node1 src]# tar -zvxf apache-tomcat-8.5.16.tar.gz
[root@tomcat-node1 src]# mv apache-tomcat-8.5.16 /usr/local/tomcat8
[root@tomcat-node1 src]# /usr/local/tomcat8/bin/startup.sh
Using CATALINA_BASE: /usr/local/tomcat8
Using CATALINA_HOME: /usr/local/tomcat8
Using CATALINA_TMPDIR: /usr/local/tomcat8/temp
Using JRE_HOME: /usr
Using CLASSPATH: /usr/local/tomcat8/bin/bootstrap.jar:/usr/local/tomcat8/bin/tomcat-juli.jar
Tomcat started.
[root@tomcat-node1 src]# ps -ef | grep tomcat
root 2303 1 19 22:01 pts/0 00:00:05 /usr/bin/java -Djava.util.logging.config.file=/usr/local/tomcat8/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -classpath /usr/local/tomcat8/bin/bootstrap.jar:/usr/local/tomcat8/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat8 -Dcatalina.home=/usr/local/tomcat8 -Djava.io.tmpdir=/usr/local/tomcat8/temp org.apache.catalina.startup.Bootstrap start
root 2320 2144 0 22:01 pts/0 00:00:00 grep --color=auto tomcat
[root@tomcat-node1 src]# lsof -i:8080
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 2303 root 48u IPv6 20206 0t0 TCP *:webcache (LISTEN)
3.编写测试页面
tomcat-node1主机上编写的测试页面,具体内容如下所示。
[root@tomcat-node1 ~]# cat /usr/local/tomcat8/webapps/ROOT/index.jsp
192.168.9.71
port:8080
this is Tomcat-node1 !
SessionID:
SessionIP:
SessionPort:
tomcat-node2主机上编写的测试页面,具体内容如下所示。
[root@Tomcat-node2 ~]# cat /usr/local/tomcat8/webapps/ROOT/index.jsp
192.168.9.74
port:8080
this is Tomcat-node2 !
SessionID:
SessionIP:
SessionPort:
8.2.3安装Nginx服务
在Nginx-node主机上安装配置Nginx服务,具体操作如下所示。
[root@nginx-node ~]# yum -y install zlib-devel pcre-devel openssl-devel gcc gcc-c++ //安装依赖包与编译环境
[root@nginx-node ~]# ll nginx-1.12.0.tar.gz
-rw-r--r-- 1 root root 980831 7月 11 2017 nginx-1.12.0.tar.gz
[root@nginx-node ~]# tar xf nginx-1.12.0.tar.gz
[root@nginx-node ~]# cd nginx-1.12.0
[root@nginx-node nginx-1.12.0]# ./configure && make && make install
[root@nginx-node nginx-1.12.0]# ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/
[root@nginx ~]# cd
[root@nginx ~]# vim /usr/local/nginx/conf/nginx.conf
//修改配置文件以实现对Tomcat负载均衡
http {
upstream backend {
server 192.168.9.71:8080 weight=1 max_fails=1 fail_timeout=10s;
server 192.168.9.74:8080 weight=1 max_fails=1 fail_timeout=10s;
}
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
proxy_pass http://backend;
}
}
}
events {
worker_connections 1024;
}
[root@nginx ~]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@nginx ~]# nginx
启动Nginx服务后访问http://192.168.9.91,发现访问请求结果会负载到192.168.9.71和192.168.9.74的Tomcat上,如图8.2与8.3所示。注:如果浏览器一直不切换页面,请清除浏览器缓存。
如上图8.2、图8.3所示,在配置Memcached-Session-Manager会话共享之前,访问http://192.168.9.91的请求会轮询负载到tomcat-node1和tomcat-node2两个节点上,并且Session ID会随着页面的刷新而改变,即此时还没有实现Session会话共享。
8.2.4安装Memcached
Memcached是一款免费、开源、分布式的内存对象缓存系统, 主要用于减少数据库的负载, 加快Web应用程序的访问。Memcached虽然简单但是功能很强大, 其简单的设计加快了部署过程,同时也易于开发, 缓存则解决了面临的大量数据时很多的问题。依次执行以下命令安装并启动Memcached,以下操作mem-node1与mem-node2主机上都需要执行。
[root@mem-node1 ~]# yum -y install libevent libevent-devel gcc gcc-c++ lsof
[root@mem-node1 ~]# cd /usr/local/src/
[root@mem-node1 src]# ll memcached-1.4.34.tar.gz
-rw-r--r-- 1 root root 391131 6月 27 07:41 memcached-1.4.34.tar.gz
[root@mem-node1 src]# tar -zxf memcached-1.4.34.tar.gz
[root@mem-node1 src]# cd memcached-1.4.34
[root@mem-node1 memcached-1.4.34]# ./configure --prefix=/usr/local/memcached
[root@mem-node1 memcached-1.4.34]# make && make install
[root@mem-node1 memcached-1.4.34]# cd
[root@mem-node1 ~]# /usr/local/memcached/bin/memcached -d -m 512 -u root -p 11211 -c 1024 -P /var/lib/memcached.11211pid
//启动Memcached,端口11211可以根据自己需要修改不同端口
[root@mem-node1 ~]# ps -ef | grep memcached //查看Memcached进程
root 13210 1 0 10:50 ? 00:00:00 /usr/local/memcached/bin/memcached -d -m 512 -u root -p 11211 -c 1024 -P /var/lib/memcached.11211pid
root 13218 2126 0 10:50 pts/0 00:00:00 grep --color=auto memcached
[root@mem-node1 ~]# lsof -i:11211
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
memcached 13210 root 26u IPv4 34585 0t0 TCP *:memcache (LISTEN)
memcached 13210 root 27u IPv6 34586 0t0 TCP *:memcache (LISTEN)
memcached 13210 root 28u IPv4 34589 0t0 UDP *:memcache
memcached 13210 root 29u IPv4 34589 0t0 UDP *:memcache
memcached 13210 root 30u IPv4 34589 0t0 UDP *:memcache
memcached 13210 root 31u IPv4 34589 0t0 UDP *:memcache
memcached 13210 root 32u IPv6 34590 0t0 UDP *:memcache
memcached 13210 root 33u IPv6 34590 0t0 UDP *:memcache
memcached 13210 root 34u IPv6 34590 0t0 UDP *:memcache
memcached 13210 root 35u IPv6 34590 0t0 UDP *:memcache
[root@mem-node1 ~]# telnet 192.168.9.77 11211 //测试Memcached连接
Trying 192.168.9.77...
Connected to 192.168.9.77.
Escape character is '^]'.
quit
Connection closed by foreign host.
[root@mem-node1 ~]#
8.2.5配置Session共享
MSM目前可以在Tomcat6、7、8版本中使用, 并且支持Memcached会话故障转移。使用MSM配置Session共享需要提前下载MSM的类库文件到/usr/local/src目录下。Session共享配置需要在tomcat-node1与tomcat-node2两台主机上都进行操作,下面以tomcat-node1进行操作演示。
[root@tomcat-node1 ~]# mkdir /usr/local/src/MSM_Software
[root@tomcat-node1 ~]# cd /usr/local/src/MSM_Software
[root@tomcat-node1 MSM_Software]# ll
总用量 1212
-rw-r--r-- 1 root root 53259 8月 27 09:53 asm-5.2.jar
-rw-r--r-- 1 root root 323740 8月 27 09:51 kryo-4.0.0.jar
-rw-r--r-- 1 root root 85217 8月 27 09:51 kryo-serializers-0.38.jar
-rw-r--r-- 1 root root 152401 8月 27 09:49 memcached-session-manager-1.9.7.jar
-rw-r--r-- 1 root root 10788 8月 27 09:49 memcached-session-manager-tc8-1.9.7.jar
-rw-r--r-- 1 root root 5711 8月 27 09:52 minlog-1.3.0.jar
-rw-r--r-- 1 root root 37160 8月 27 09:51 msm-kryo-serializer-1.9.7.jar
-rw-r--r-- 1 root root 51287 8月 27 09:53 objenesis-2.4.jar
-rw-r--r-- 1 root root 20883 8月 27 09:52 reflectasm-1.11.3.jar
-rw-r--r-- 1 root root 472838 8月 27 09:50 spymemcached-2.12.2.jar
[root@tomcat-node1 MSM_Software]# cp -rf /usr/local/src/MSM_Software/* /usr/local/tomcat8/lib/
接下来进行序列化Tomcat配置,序列化Tomcat配置的方法有很多种:java默认序列化tomcat配置、javolution序列化tomcat配置、xstream序列化tomcat配置、flexjson序列化tomcat配置和kryo序列化tomcat配置。Tomcat官网介绍说使用kryo序列化Tomcat的效率最高,所以这里只介绍使用kryo序列化。
在Non-Sticky模式和Sticky模式下context.xml文件配置也有所不同(一般用的是Non-Sticky模式),本实验采用Non-Sticky模式,只需要修改conf/context.xml文件即可。
[root@tomcat-node1 MSM_Software]# cd /usr/local/tomcat8/conf/
[root@tomcat-node1 conf]# cp context.xml context.xml.bak
[root@tomcat-node1 conf]# vim context.xml
//在和之间添加下面内容.就在底部之前添加就行
memcachedNodes="n1:192.168.9.77:11211 n2:192.168.9.79:11211"
lockingMode="auto"
sticky="false"
sessionBackupAsync="false"
sessionBackupTimeout= "1000"
copyCollectionsForSerialization="true"
requestUriIgnorePattern=".*.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
[root@tomcat-node1 conf]# /usr/local/tomcat8/bin/shutdown.sh
[root@tomcat-node1 conf]# lsof -i:8080
[root@tomcat-node1 conf]# /usr/local/tomcat8/bin/startup.sh
Using CATALINA_BASE: /usr/local/tomcat8
Using CATALINA_HOME: /usr/local/tomcat8
Using CATALINA_TMPDIR: /usr/local/tomcat8/temp
Using JRE_HOME: /usr
Using CLASSPATH: /usr/local/tomcat8/bin/bootstrap.jar:/usr/local/tomcat8/bin/tomcat-juli.jar
Tomcat started.
[root@tomcat-node1 conf]# lsof -i:8080
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 11915 root 58u IPv6 36201 0t0 TCP *:webcache (LISTEN)
tomcat-node2主机也需要依次执行上述的操作。
访问http://192.168.9.91并按ctrl+F5刷新页面,发现Session信息会发生改变,但是Session ID不会改变,说明Session实现了共享。如图8.4所示,当前Session ID保存到了Memcached n1节点上。
至此Tomcat的Session共享已配置完成。