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介绍:(详细介绍可以参考:Tomcat利用MSM实现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共享之目的, 支持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等进行了指定。

1

2

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)基础环境

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

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两台机器上操作)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

安装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机器上操作)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

[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简单并且强大, 其简单的设计加快了部署, 易于开发, 缓存解决了面临的大量数据时很多的问题.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

[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目录下,下载地址

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

[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服务,继续访问页面:


版权声明:本文为weixin_42073629原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。