• Tomcat通过Memcached实现session共享的完整部署记录


    对于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包!!

  • 相关阅读:
    机器视觉资料整理
    《用TCP/IP进行网络互连》读书笔记
    Win Form不能响应键盘事件
    C语言 字符串前加L的意义 如:L“A”
    UniCode 下 CString 转 char* 的方法(转)
    BATCH
    HALCON不支持的设备中,获取图像
    关于FragmentManager动态管理Fragment时Fragment生命周期的探究
    关于如何惟一地标识一台Android设备的综合性讨论
    如何使ActionBar不那么单调
  • 原文地址:https://www.cnblogs.com/kevingrace/p/6398672.html
Copyright © 2020-2023  润新知