对于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                80192.168.10.201     Tomcat-node1     java8.131、tomcat8.0.53    8080192.168.10.202     Tomcat-node2     java8.131、tomcat8.0.53    8080192.168.10.203     Mem-node1        memcached-1.4.34           11211192.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-releaseCentOS release 6.9 (Final)   为了方便测试,关闭iptables防火墙和selinux。如果是生产环境,开启iptables后,需要开放对应的应用端口。[root@Nginx-node ~]# setenforce 0[root@Nginx-node ~]# getenforcedisabled[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 -versionjava 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_131JAVA_BIN=/usr/java/jdk1.8.0_131/binPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/bin:/sbin/CLASSPATH=.:/lib/dt.jar:/lib/tools.jarexport JAVA_HOME JAVA_BIN PATH CLASSPATH  [root@Tomcat-node1 ~]# source /etc/profile[root@Tomcat-node1 ~]# java -versionjava 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.shUsing CATALINA_BASE:   /usr/local/tomcat8Using CATALINA_HOME:   /usr/local/tomcat8Using CATALINA_TMPDIR: /usr/local/tomcat8/tempUsing JRE_HOME:        /usr/java/jdk1.8.0_131Using CLASSPATH:       /usr/local/tomcat8/bin/bootstrap.jar:/usr/local/tomcat8/bin/tomcat-juli.jarTomcat started.You have new mail in /var/spool/mail/root[root@Tomcat-node1 src]# ps -ef|grep tomcatroot      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 startroot      8528  6829  0 03:11 pts/0    00:00:00 grep tomcat[root@Tomcat-node1 src]# lsof -i:8080COMMAND  PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAMEjava    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]# lltotal 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 -tnginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is oknginx: 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:80COMMAND   PID   USER   FD   TYPE   DEVICE SIZE/OFF NODE NAMEnginx   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 memcachedroot      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.11211pidroot      1400 16303  0 14:35 pts/0    00:00:00 grep memcached[root@mem-node1 ~]# lsof -i:11211COMMAND    PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAMEmemcached 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 *:memcachememcached 1340 root   29u  IPv4 18958549      0t0  UDP *:memcachememcached 1340 root   30u  IPv4 18958549      0t0  UDP *:memcachememcached 1340 root   31u  IPv4 18958549      0t0  UDP *:memcachememcached 1340 root   32u  IPv6 18958550      0t0  UDP *:memcachememcached 1340 root   33u  IPv6 18958550      0t0  UDP *:memcachememcached 1340 root   34u  IPv6 18958550      0t0  UDP *:memcachememcached 1340 root   35u  IPv6 18958550      0t0  UDP *:memcache  测试一下memcached连接,如下说明成功(输入quit退出)[root@mem-node1 ~]# telnet 192.168.10.203 11211Trying 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]# lltotal 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.baka)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包!!

©著作权归作者所有:来自51CTO博客作者80民工的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 学习一下小顶堆
  2. 使用AMD灵活配置实例使PeopleSoft降本增效
  3. WinForm基于插件开发实现多项配置存储
  4. linux下配置免安装版zabbix-agent配置详解
  5. Mysql Cluster7.4.6安装与配置
  6. 4-10(二叉搜索树)
  7. 【DB宝18】在Docker中安装使用MySQL高可用之MGR
  8. 2021-04-10:给定两个可能有环也可能无环的单链表,头节点head1和hea
  9. Elasticsearch 最佳运维实践 - 总结(一)

随机推荐

  1. angularjs是怎么为ng-click事件传递参数
  2. WPF核心的技术--数据绑定
  3. 比如说我们要从用户表customer和用户订单
  4. MVC 5限制所有HTTP请求必须是POST方式
  5. lambda表达式进行对象结合操作的实例详解
  6. 最新开源DBLayer的详细介绍
  7. IIS中出现了时间格式转换错误该如何解决?
  8. C#中匿名委托以及Lambda表达式的实例详解
  9. WPF实现简单的进度条怎么做?
  10. Oracle Clob字段过长保存出错改如何解决?