对于web应用集群的技术实现而言,最大的难点就是:如何能在集群中的多个节点之间保持数据的一致性,会话(Session)信息是这些数据中最重要的一块。要实现这一点, 大体上有两种方式:
一种是把所有Session数据放到一台服务器上或者数据库中,集群中的所有节点通过访问这台Session服务器来获取数据;
另一种就是在集群中的所有节点间进行Session数据的同步拷贝,任何一个节点均保存了所有的Session数据。
在集群系统下实现session统一的有如下几种方案:
1) 请求精确定位:session sticky,例如基于访问ip的hash策略,即当前用户的请求都集中定位到一台服务器中,这样单台服务器保存了用户的session登录信息,如果宕机,则等同于单点部署,会丢失,会话不复制。
2) session复制共享:session replication,如tomcat自带session共享,主要是指集群环境下,多台应用服务器之间同步session,使session保持一致,对外透明。 如果其中一台服务器发生故障,根据负载均衡的原理,调度器会遍历寻找可用节点,分发请求,由于session已同步,故能保证用户的session信息不会丢失,会话复制,。
此方案的不足之处:
必须在同一种中间件之间完成(如:tomcat-tomcat之间).
session复制带来的性能损失会快速增加.特别是当session中保存了较大的对象,而且对象变化较快时, 性能下降更加显著,会消耗系统性能。这种特性使得web应用的水平扩展受到了限制。
Session内容通过广播同步给成员,会造成网络流量瓶颈,即便是内网瓶颈。
在大并发下表现并不好
3) 基于cache DB缓存的session共享
基于memcache/redis缓存的session共享.即使用cacheDB存取session信息,应用服务器接受新请求将session信息保存在cache DB中,当应用服务器发生故障时,调度器会遍历寻找可用节点,分发请求,当应用服务器发现session不在本机内存时,则去cacheDB中查找,如果找到则复制到本机,这样实现session共享和高可用。
Tomcat集群session同步方案有以下几种方式:
1)使用tomcat自带的cluster方式,多个tomcat间自动实时复制session信息,配置起来很简单。但这个方案的效率比较低,在大并发下表现并不好。
2)利用nginx的基于访问ip的hash路由策略,保证访问的ip始终被路由到同一个tomcat上,这个配置更简单。每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session(并不是共享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共享.
5)利用redis实现。使用redis不仅仅可以将缓存的session持久化,还因为它支持的单个对象比较大,而且数据类型丰富,不只是缓存 session,还可以做其他用途,可以一举几得。Redis这种方式目前还不支持Tomcat8环境(现在网上插件不支持tomcat8,非要支持tomcat8,则需修改插件jar包的源代码)!
6)利用filter方法实现。这种方法比较推荐,因为它的服务器使用范围比较多,不仅限于tomcat ,而且实现的原理比较简单容易控制。
7)利用terracotta服务器共享session。这种方式配置比较复杂。
在Tomcat集群中,当一个节点出现故障,虽然有高可用集群来负责故障转移,但用户的session信息如何保持呢?
下面介绍第4种方案,session复制同步使用MSM(Memcache-Session-Manager),即利用MSM+Memcached做Session共享。
MSM介绍:(详细介绍可以参考:http://www.cnblogs.com/kevingrace/p/6401025.html)
MSM是一个高可用的Tomcat Session共享解决方案,除了可以从本机内存快速读取Session信息(仅针对黏性Session)外,还可使用Memcached存取Session,以实现高可用。
传统tomcat集群,会话复制随着结点数增多,扩展性成为瓶颈。MSM使用memcached完成统一管理tomcat会话,避免tomcat结点间过多会话复制。
MSM利用Value(Tomcat 阀)对Request进行跟踪。Request请求到来时,从memcached加载session,Request请求结束时,将tomcat session更新至memcached,以达到session共享之目的, 支持sticky和non-sticky模式:
sticky : 会话粘连模式(黏性session)。客户端在一台tomcat实例上完成登录后,以后的请求均会根据IP直接绑定到该tomcat实例。
no-sticky:会话非粘连模式(非粘性session)。客户端的请求是随机分发,多台tomcat实例均会收到请求。
在进行环境部署之前,要对cookie和session的工作机制非常了解,如果不了解其中的原理且只是机械性地去按照参考文档部署,那么这是毫无意义的。
a)cookie是怎么工作的?
加入我们创建了一个名字为login的Cookie来包含访问者的信息,创建Cookie时,服务器端的Header如下面所示,这里假设访问者的注册名是“wangshibo”,同时还对所创建的Cookie的属性如path、domain、expires等进行了指定。
Set-Cookie:login=wangshibo;path=/;domain=msn.com; expires=Monday,01-Mar-99 00:00:01 GMT
上面这个Header会自动在浏览器端计算机的Cookie文件中添加一条记录。浏览器将变量名为“login”的Cookie赋值为“wangshibo”。
注意,在实际传递过程中这个Cookie的值是经过了URLEncode方法的URL编码操作的。 这个含有Cookie值的HTTP Header被保存到浏览器的Cookie文件后,Header就通知浏览器将Cookie通过请求以忽略路径的方式返回到服务器,完成浏览器的认证操作。
此外,我们使用了Cookie的一些属性来限定该Cookie的使用。例如Domain属性能够在浏览器端对Cookie发送进行限定,具体到上面的例子,该Cookie只能传到指定的服务器上,而决不会跑到其他的Web站点上去。Expires属性则指定了该Cookie保存的时间期限,例如上面的Cookie在浏览器上只保存到1999年3月1日1秒。 当然,如果浏览器上Cookie太多,超过了系统所允许的范围,浏览器将自动对它进行删除。至于属性Path,用来指定Cookie将被发送到服务器的哪一个目录路径下。
说明:浏览器创建了一个Cookie后,对于每一个针对该网站的请求,都会在Header中带着这个Cookie;不过,对于其他网站的请求Cookie是绝对不会跟着发送的。而且浏览器会这样一直发送,直到Cookie过期为止。
b)session是如何工作的?
由于http是无状态的协议,你访问了页面A,然后再访问B页面,http无法确定这2个访问来自一个人,因此要用cookie或session来跟踪用户,根据授权和用户身份来 显示不同的页面。比如用户A登陆了,那么能看到自己的个人信息,而B没登陆,无法看到个人信息。还有A可能在购物,把商品放入购物车,此时B也有这个过程, 你无法确定A,B的身份和购物信息,所以需要一个session ID来维持这个过程。
cookie是服务器发给客户端并保持在客户端的一个文件,里面包含了用户的访问信息(账户密码等),可以手动删除或设置有效期,在下次访问的时候,会返给服务器。
注意:cookie可以被禁用,所以要想其他办法,这就是session。cookie数据存放在客户的浏览器上,session数据放在服务器上。cookie同时也是session id的载体,cookie保存session id。另外:cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。session是服务器端缓存,cookie是客户端缓存。所以建议:将登陆信息等重要信息存放为session;其他信息如果需要保留,可以放在cookie中,
比如:你去商场购物,商场会给你办一张会员卡,下次你来出示该卡,会有打折优惠,该卡可以自己保存(cookie),或是商场代为保管,由于会员太多,个人需要保存卡号信息(session ID)。
为什么要持久化session(共享session)?
因为:在客户端每个用户的Session对象存在Servlet容器中,如果Tomcat服务器重启或者宕机的话,那么该session就会丢失,而客户端的操作会由于session丢失而造成数据丢失;如果当前用户访问量巨大,每个用户的Session里存放大量数据的话,那么就很占用服务器大量的内存,进而致使服务器性能受到影响。
可以使用数据库持久化session,分为物理数据库和内存数据库。物理数据库备份session,由于其性能原因,不推荐;内存数据库可以使用redis和memcached,这里介绍memcached的方法。
MSM工作原理:
a)Sticky Session(黏性) 模式下的工作原理:
Tomcat本地Session为主Session,Memcached 中的Session为备Session。Request请求到来时, 从memcached加载备 Session到 tomcat (仅当tomcat jvmroute发生变化时, 否则直接取Tomcat Session);Request请求结束时,将Tomcat Session更新至memcached,以达到主备同步之目的。 安装在Tomcat上的MSM使用本机内存保存Session,当一个请求执行完毕之后,如果对应的Session在本地不存在(即某用户的第一次请求),则将该Session复制一份至Memcached;当该Session的下一个请求到达时,会使用Tomcat的本地Session,请求处理结束之后,Session的变化会同步更新到 Memcached,保证数据一致。当集群中的一个Tomcat挂掉,下一次请求会被路由到其他Tomcat上。负责处理此此请求的Tomcat并不清楚Session信息,于是从Memcached查找该Session,更新该Session并将其保存至本机。此次请求结束,Session被修改,送回Memcached备份。
b)Non-sticky Session (非黏性)模式下的工作原理(记住:多台tomcat集群或多个tomcat实例时需要选择Non-Sticky模式,即sticky="false"):
Tomcat本地Session为中转Session,Memcached1为主Session,Memcached2为备Session。Request请求到来时,从Memcached2加载备Session到tomcat,(当容器中还是没有Session 则从Memcached1加载主Session到tomcat,这种情况是只有一个memcached节点,或者有Memcached1 出错时),Request请求结束时,将Tomcat Session更新至主Memcached1和备memcached2,并且清除Tomcat Session 。以达到主备同步之目的。 多台tomcat集群时 需要选择Non-Sticky模式,即sticky="false"
===================================================
Tomcat8+Memcached+Nginx实现session会话共享的操作记录:
1)基础环境
ip 主机名 应用 端口 192.168.10.200 Nginx-node nginx1.12.2 80 192.168.10.201 Tomcat-node1 java8.131、tomcat8.0.53 8080 192.168.10.202 Tomcat-node2 java8.131、tomcat8.0.53 8080 192.168.10.203 Mem-node1 memcached-1.4.34 11211 192.168.10.205 Mem-node2 memcached-1.4.34 11211 这里的两台memcache,基于tomcat做会话同步;(只对动态内容缓存,用于追踪用户会话) 使用Nginx+Tomcat进行负载均衡时,一般使用轮询方式进行负载。但是如果使用轮询方式的话,可能会访问不同的Tomcat, 此时如果不进行Session共享,则相当于是一个新的Session。就比如现有系统都是需要认证登录的系统,如果没有Session共享,则会导致用户退出登录。 Nginx配置中可以使用ip_hash的方式简单实现session共享.但是这种方式只能将session固定到单台tomcat机器上,如果这台tomcat机器挂掉,则session 信息就会丢失,不能实现session的故障转移. 下面操作在五台机器上同样执行: [root@Nginx-node ~]# cat /etc/redhat-release CentOS release 6.9 (Final) 为了方便测试,关闭iptables防火墙和selinux。如果是生产环境,开启iptables后,需要开放对应的应用端口。 [root@Nginx-node ~]# setenforce 0 [root@Nginx-node ~]# getenforce disabled [root@Nginx-node ~]# cat /etc/sysconfig/selinux |grep "SELINUX=disabled" SELINUX=disabled [root@Nginx-node ~]# /etc/init.d/iptables stop 本案例环境部署中所需的软件下载地址:https://pan.baidu.com/s/1E92JgSov5IqHsY9wAzgRMA 提取密码:hwpw 下载到服务器上的/usr/local/src目录下.另外:节点服务器的系统时间一定要保持一致!! memcached-session-manager环境部署所需的各种jar包的下载地址: https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration 温馨提示: Tomcat+MSM环境的部署对版本要求极其谨慎,本案例中下载的软件版本是经过验证后的正确版本,不可随意更改这些软件的版本,否则会导致环境部署失败!
实验拓扑图:
2)安装tomcat(在192.168.10.201和192.168.10.202两台机器上操作)
安装java8环境。先卸载掉系统自带的java7,然后安装java8 [root@Tomcat-node1 ~]# java -version java version "1.7.0_131" OpenJDK Runtime Environment (rhel-2.6.9.0.el6_8-x86_64 u131-b00) OpenJDK 64-Bit Server VM (build 24.131-b00, mixed mode) [root@Tomcat-node1 ~]# yum -y remove java-1.7.0-openjdk* [root@Tomcat-node1 ~]# yum -y remove tzdata-java.noarch [root@Tomcat-node1 ~]# java -version -bash: /usr/bin/java: No such file or directory [root@Tomcat-node1 ~]# ll /usr/local/src/jdk-8u131-linux-x64_.rpm -rw-rw-r--. 1 root root 169983496 Nov 19 2017 /usr/local/src/jdk-8u131-linux-x64_.rpm [root@Tomcat-node1 ~]# rpm -ivh /usr/local/src/jdk-8u131-linux-x64_.rpm --force [root@Tomcat-node1 ~]# vim /etc/profile ...... JAVA_HOME=/usr/java/jdk1.8.0_131 JAVA_BIN=/usr/java/jdk1.8.0_131/bin PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/bin:/sbin/ CLASSPATH=.:/lib/dt.jar:/lib/tools.jar export JAVA_HOME JAVA_BIN PATH CLASSPATH [root@Tomcat-node1 ~]# source /etc/profile [root@Tomcat-node1 ~]# java -version java version "1.8.0_131" Java(TM) SE Runtime Environment (build 1.8.0_131-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode) You have new mail in /var/spool/mail/root 安装配置tomcat8 [root@Tomcat-node1 ~]# cd /usr/local/src/ [root@Tomcat-node1 src]# ll apache-tomcat-8.0.53.tar.gz -rw-rw-r--. 1 root root 9472492 Nov 9 2017 apache-tomcat-8.0.53.tar.gz [root@Tomcat-node1 src]# tar -zvxf apache-tomcat-8.0.53.tar.gz [root@Tomcat-node1 src]# mv apache-tomcat-8.0.53 /usr/local/tomcat8 启动tomcat [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/java/jdk1.8.0_131 Using CLASSPATH: /usr/local/tomcat8/bin/bootstrap.jar:/usr/local/tomcat8/bin/tomcat-juli.jar Tomcat started. You have new mail in /var/spool/mail/root [root@Tomcat-node1 src]# ps -ef|grep tomcat root 8477 1 87 03:11 pts/0 00:00:03 /usr/java/jdk1.8.0_131/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 8528 6829 0 03:11 pts/0 00:00:00 grep tomcat [root@Tomcat-node1 src]# lsof -i:8080 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 8477 root 49u IPv6 12974768 0t0 TCP *:webcache (LISTEN) 再另一台tomcat节点192.168.10.202如上同样部署。 编写一个测试页面(直接修改index.jsp文件): [root@Tomcat-node1 ~]# cat /usr/local/tomcat8/webapps/ROOT/index.jsp <html> <body bgcolor="green"> <center> <%= request.getSession().getId() %> <h1>192.168.10.201</h1> <h1>port:8080</h1> <h1>this is Tomcat-node1 ! </h1> </center> </body> </html> <%@ page contentType="text/html;charset=UTF-8" isELIgnored="false"%> SessionID:<%=session.getId()%><BR> SessionIP:<%=request.getServerName()%> <BR> SessionPort:<%=request.getServerPort()%> <% out.println("This is Tomcat server 201 !"); %> 另一台tomcat的测试页面为: [root@Tomcat-node2 ~]# cat /usr/local/tomcat8/webapps/ROOT/index.jsp <html> <body bgcolor="green"> <center> <%= request.getSession().getId() %> <h1>192.168.10.202</h1> <h1>port:8080</h1> <h1>this is Tomcat-node2! </h1> </center> </body> </html> <%@ page contentType="text/html;charset=UTF-8" isELIgnored="false"%> SessionID:<%=session.getId()%><BR> SessionIP:<%=request.getServerName()%> <BR> SessionPort:<%=request.getServerPort()%> <% out.println("This is Tomcat server 202 !"); %>
3)安装Nginx(在192.168.10.200机器上操作)
[root@Nginx-node ~]# cd /usr/local/src/ [root@Nginx-node src]# ll total 8920 -rw-rw-r--. 1 root root 981687 Oct 27 2017 nginx-1.12.2.tar.gz -rw-rw-r--. 1 root root 5453234 Aug 23 2018 openssl-1.1.0i.tar.gz -rw-rw-r--. 1 root root 2081413 Aug 23 2018 pcre-8.42.tar.gz -rw-rw-r--. 1 root root 607698 Jan 16 2017 zlib-1.2.11.tar.gz 安装依赖包 [root@Nginx-node src]# yum -y install gcc gcc-c++ 安装pcre库 [root@Nginx-node src]# tar -zvxf pcre-8.42.tar.gz [root@Nginx-node src]# cd pcre-8.42 [root@Nginx-node pcre-8.42]# ./configure && make && make install 安装zlib库 [root@Nginx-node pcre-8.42]# cd /usr/local/src/ [root@Nginx-node src]# tar -zvxf zlib-1.2.11.tar.gz [root@Nginx-node src]# cd zlib-1.2.11 [root@Nginx-node zlib-1.2.11]# ./configure && make && make install 安装openssl [root@Nginx-node zlib-1.2.11]# cd /usr/local/src/ [root@Nginx-node src]# tar -zvxf openssl-1.1.0i.tar.gz [root@Nginx-node src]# cd openssl-1.1.0i [root@Nginx-node openssl-1.1.0i]# ./config && make && make install 安装nginx,特别注意要指定prce zlib openssl原码包位置 [root@Nginx-node openssl-1.1.0i]# cd /usr/local/src/ [root@Nginx-node src]# tar -zvxf nginx-1.12.2.tar.gz [root@Nginx-node src]# cd nginx-1.12.2 [root@Nginx-node nginx-1.12.2]# ./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-pcre=/usr/local/src/pcre-8.42 --with-zlib=/usr/local/src/zlib-1.2.11 --with-openssl=/usr/local/src/openssl-1.1.0i [root@Nginx-node nginx-1.12.2]# make && make install 安装成功后配置nginx [root@Nginx-node nginx-1.12.2]# cd /usr/local/nginx/conf/ [root@Nginx-node conf]# cp nginx.conf nginx.conf.bak [root@Nginx-node conf]# cat nginx.conf #user nobody; worker_processes 8; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; worker_rlimit_nofile 65535; events { use epoll; worker_connections 65535; } http { include mime.types; default_type application/octet-stream; charset utf-8; ###### ## set access log format ###### log_format main '$http_x_forwarded_for $remote_addr $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_cookie" $host $request_time'; ####### ## http setting ####### sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; fastcgi_connect_timeout 30000; fastcgi_send_timeout 30000; fastcgi_read_timeout 30000; fastcgi_buffer_size 256k; fastcgi_buffers 8 256k; fastcgi_busy_buffers_size 256k; fastcgi_temp_file_write_size 256k; fastcgi_intercept_errors on; ##cache## client_header_timeout 60s; client_body_timeout 60s; client_max_body_size 10m; client_body_buffer_size 1m; proxy_connect_timeout 5; proxy_read_timeout 60; proxy_send_timeout 5; proxy_buffer_size 64k; proxy_buffers 4 128k; proxy_busy_buffers_size 128k; proxy_temp_file_write_size 1m; proxy_temp_path /home/temp_dir; proxy_cache_path /home/cache levels=1:2 keys_zone=cache_one:200m inactive=1d max_size=30g; ##end## gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.1; gzip_comp_level 9; gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php; gzip_vary on; ## includes vhosts include vhosts/*.conf; } [root@Nginx-node conf]# mkdir vhosts [root@Nginx-node conf]# cd vhosts/ [root@Nginx-node vhosts]# vim lb_tomcat.conf upstream tomcat-lb { server 192.168.10.201:8080; server 192.168.10.202:8080; } server { listen 80; server_name www.kevin.com; location / { proxy_pass http://tomcat-lb; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location ~ .*.(gif|jpg|png|htm|html|css|ico|flv|swf)(.*) { proxy_pass http://tomcat-lb; proxy_redirect off; proxy_set_header Host $host; proxy_cache cache_one; proxy_cache_valid 200 302 1h; proxy_cache_valid 301 1d; proxy_cache_valid any 10m; expires 30d; proxy_cache_key $host$uri$is_args$args; } } [root@Nginx-node vhosts]# /usr/local/nginx/sbin/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-node conf]# /usr/local/nginx/sbin/nginx [root@Nginx-node conf]# lsof -i:80 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME nginx 25292 root 6u IPv4 19679665 0t0 TCP *:http (LISTEN) nginx 25293 nobody 6u IPv4 19679665 0t0 TCP *:http (LISTEN) nginx 25294 nobody 6u IPv4 19679665 0t0 TCP *:http (LISTEN) nginx 25295 nobody 6u IPv4 19679665 0t0 TCP *:http (LISTEN) nginx 25296 nobody 6u IPv4 19679665 0t0 TCP *:http (LISTEN) nginx 25297 nobody 6u IPv4 19679665 0t0 TCP *:http (LISTEN) nginx 25298 nobody 6u IPv4 19679665 0t0 TCP *:http (LISTEN) nginx 25299 nobody 6u IPv4 19679665 0t0 TCP *:http (LISTEN) nginx 25300 nobody 6u IPv4 19679665 0t0 TCP *:http (LISTEN) 将域名www.kevin.com解析到192.168.10.200上,访问http://www.kevin.com,发现访问请求结果会负载到192.168.10.201和192.168.10.202的tomcat上了。
如上,在配置memcached-session-manager会话共享之前,访问http://www.kevin.com的请求会轮询负载到tomcat-node1和tomcat-node2两个节点上,并且session id会随着页面的刷新而改变,即此时还没有实现session会话共享!!
4)安装Memcached(在192.168.10.203和192.168.10.205机器上操作)
Memcached是一款免费、开源、分布式的内存对象缓存系统, 用于减少数据库的负载, 加快web应用程序的访问. Memcached简单并且强大, 其简单的设计加快了部署, 易于开发, 缓存解决了面临的大量数据时很多的问题.
[root@mem-node1 ~]# yum -y install libevent libevent-devel [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 Jun 27 07:41 memcached-1.4.34.tar.gz [root@mem-node1 src]# tar -zvxf 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 启动memcached,端口11211可以根据自己需要修改不同端口 [root@mem-node1 ~]# /usr/local/memcached/bin/memcached -d -m 512 -u root -p 11211 -c 1024 -P /var/lib/memcached.11211pid 查看memcached进程是否起来 [root@mem-node1 ~]# ps -ef|grep memcached root 1340 1 0 14:34 ? 00:00:00 /usr/local/memcached/bin/memcached -d -m 512 -u root -p 11211 -c 1024 -P /var/lib/memcached.11211pid root 1400 16303 0 14:35 pts/0 00:00:00 grep memcached [root@mem-node1 ~]# lsof -i:11211 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME memcached 1340 root 26u IPv4 18958545 0t0 TCP *:memcache (LISTEN) memcached 1340 root 27u IPv6 18958546 0t0 TCP *:memcache (LISTEN) memcached 1340 root 28u IPv4 18958549 0t0 UDP *:memcache memcached 1340 root 29u IPv4 18958549 0t0 UDP *:memcache memcached 1340 root 30u IPv4 18958549 0t0 UDP *:memcache memcached 1340 root 31u IPv4 18958549 0t0 UDP *:memcache memcached 1340 root 32u IPv6 18958550 0t0 UDP *:memcache memcached 1340 root 33u IPv6 18958550 0t0 UDP *:memcache memcached 1340 root 34u IPv6 18958550 0t0 UDP *:memcache memcached 1340 root 35u IPv6 18958550 0t0 UDP *:memcache 测试一下memcached连接,如下说明成功(输入quit退出) [root@mem-node1 ~]# telnet 192.168.10.203 11211 Trying 192.168.10.203... Connected to 192.168.10.203. Escape character is '^]'.
5)配置Tomcat,通过MSM实现共享session(192.168.10.201和192.168.10.202机器上操作)
MSM(memcached session manager), MSM是一款实现Tomcat会话保持的管理组件, 支持粘性和无粘性的配置, 目前可以在Tomcat6,7,8中使用, 并且支持Memcached会话故障转移.提前下载MSM的类库文件到/usr/local/src目录下,下载地址
[root@Tomcat-node1 ~]# cd /usr/local/src/MSM_Software [root@Tomcat-node1 MSM_Software]# ll total 1212 -rw-rw-r--. 1 root root 53259 Aug 27 09:53 asm-5.2.jar -rw-rw-r--. 1 root root 323740 Aug 27 09:51 kryo-4.0.0.jar -rw-rw-r--. 1 root root 85217 Aug 27 09:51 kryo-serializers-0.38.jar -rw-rw-r--. 1 root root 152401 Aug 27 09:49 memcached-session-manager-1.9.7.jar -rw-rw-r--. 1 root root 10788 Aug 27 09:49 memcached-session-manager-tc8-1.9.7.jar -rw-rw-r--. 1 root root 5711 Aug 27 09:52 minlog-1.3.0.jar -rw-rw-r--. 1 root root 37160 Aug 27 09:51 msm-kryo-serializer-1.9.7.jar -rw-rw-r--. 1 root root 51287 Aug 27 09:53 objenesis-2.4.jar -rw-rw-r--. 1 root root 20883 Aug 27 09:52 reflectasm-1.11.3.jar -rw-rw-r--. 1 root root 472838 Aug 27 09:50 spymemcached-2.12.2.jar 特别注意: memcached-session-manager-tc8-1.9.7.jar中的tc8为tomcat的版本号。 一定要注意:不同版本号的tomcat,对应的msm包也不同。此处为tomcat8的jar包。 需要把上面这些MSM依赖的jar包下载后全部上传到两台机器的tomcat安装路径的lib/ 目录下 [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配置。 官网介绍说 使用kryo序列化tomcat的效率最高,所以这里只介绍kryo序列化。 在No-Stick模式和Stick模式下context.xml文件配置也有所不同(一般用的是No-Stick模式) 只需要修改conf/context.xml文件: [root@Tomcat-node1 ~]# cd /usr/local/tomcat8/conf/ [root@Tomcat-node1 conf]# cp context.xml context.xml.bak a)No-Stick模式 记住:多个tomcat实例时 需要选择Non-Sticky模式,即sticky="false" [root@Tomcat-node1 conf]# vim context.xml #在<Context>和</Context>之间添加下面内容.就在底部</Context>之前添加就行 ....... <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="n1:192.168.10.203:11211,n2:192.168.10.205: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" /> 第一台tomcat节点的congtext.xml配置好之后,再将该文件拷贝到另一台tomcat节点的相同路径下 b) Stick模式。 故障转移配置节点(failoverNodes),不能使用在Non-Sticky模式,多个使用空格或逗号分开,配置某个节点为备份节点。 当其他节点都不可用时才会存储到备份节点,适用于sticky模式(即一台tomcat,多台memcached)。 [root@Tomcat-node1 conf]# vim context.xml ...... <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="n1:192.168.10.203:11211,n2:192.168.10.205:11211" #多个memcached之间用空格或逗号隔开都可以的 sticky="true" failoverNodes="n2" requestUriIgnorePattern=".*.(png|gif|jpg|css|js|swf|flv)$" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory" copyCollectionsForSerialization="true" /> 第一台tomcat节点的congtext.xml配置好之后,再将该文件拷贝到另一台tomcat节点的相同路径下,并将failoverNodes后面的参数改为n1 配置好之后,一定要记得重启两台机器的tomcat服务! [root@Tomcat-node1 ~]# /usr/local/tomcat8/bin/shutdown.sh #或者直接使用kill杀死 [root@Tomcat-node1 ~]# lsof -i:8080 [root@Tomcat-node1 ~]# /usr/local/tomcat8/bin/startup.sh ====================================================================================== Manager 各参数说明: memcachedNodes 必选项,memcached的节点信息,多个memcached节点,中间需要使用空格 failoverNodes="n2" 表示当前session保持到n1的memcached节点上 failoverNodes 可选项,不能使用在non-sticky sessions模式。故障转移配置节点,多个使用空格或逗号分开,配置某个节点为备份节点, 当其他节点都不可用时才会存储到备份节点,官方建议配置为和tomcat同服务器的节点。 理由如下: 假如有两台服务器m1,m2,其中m1部署tomcat和memcached节点n1,m2部署memcached节点n2。 如果配置tomcat的failoverNodes值为n2或者不配置,则当服务器m1挂掉后n1和tomcat中保存的session会丢失,而n2中未保存或者只保存了部分session, 这就造成 部分用户状态丢失。 如果配置tomcat的failoverNodes值为n1,则当m1挂掉后因为n2中保存了所有的session,所以重启tomcat的时候用户状态不会丢失。 为什么n2中保存了所有的session? 因为failoverNodes配置的值是n1,只有当n2节点不可用时才会把session存储到n1,所以这个时候n1中是没有保存任何session的。 lockingMode 可选值,默认none,只对non-sticky有效。 requestUriIgnorePattern 可选值,制定忽略那些请求的session操作,一般制定静态资源如css,js一类的。 sessionBackupAsync 可选值,默认true,是否异步的方式存储到memcached。 sessionBackupTimeout 可选项,默认100毫秒,异步存储session的超时时间。
6) MSM会话共享测试
a) 访问http://www.kevin.com,按ctrl+F5强刷页面,发现session信息会变,但是sessionid不会改变!说明session实现了共享! 如下,表示当前sessionid保存到了n1这个memcached节点上了.
b) 关闭Mem-node1节点的memcached服务,继续访问页面,发现sessionid保存到了n2这个memcached节点上了,但是sessionid任然没有改变,说明session已共享. 也就是说,关闭memcached集群中的任意一个节点.访问页面,sessionid都不会改变.即可以实现memcached故障转移!如下:
c) 关闭tomcat-node1和tomcat-node2中的任意一个节点的tomcat服务,继续访问页面,发现前端从nginx负载过来的请求达到未关闭的tomcat节点上,sessionid都不会改变,任然在共享中!即可以实现tomcat故障转移
关闭tomcat-node1节点的tomcat服务,继续访问页面:
关闭tomcat-node2节点的tomcat服务,继续访问页面:
特别提示:
如果memcached session manager的会话共享配置后,重启tomcat服务没有报错,但是访问页面的时候报错,页面访问失败,如下在logs/catalina.out日志里发现的错误:SEVERE [http-nio-8080-exec-1] org.apache.coyote.http11.AbstractHttp11Processor.process Error processing request java.lang.NoSuchFieldError: attributes
这种错误情况基本就是jar包版本不兼容导致的,需要到这里下载跟tomcat版本相对应的jar包!!