Tomcat实验二 |
(1)Tomcat常见部署方式:
(2)Tomcat要实现负载均衡,必然涉及session问题:
session问题的分析:https://blog.youkuaiyun.com/wdirdo/article/details/103190013
(1)tomcat解决session问题 |
(1)session sticky会话黏性
a.Session绑定
- nginx:source ip
- HAProxy:cookie
b.优点:简单易配置
c.缺点:如果目标服务器故障后,如果没有做sessoin持久化,就会丢失session
(2)session复制集群
a.Tomcat自己的提供的多播集群,通过多播将任何一台的session同步到其它节点
b.session复制集群的缺点
- Tomcat的同步节点不宜过多,互相即时通信同步session需要太多带宽
- 每一台都拥有全部session,内存占用太多
(3)session server
a.session共享服务器,使用memcached、redis做共享的Session服务器。
(2)nginx实现负载均衡 |
(1)nginx实现负载均衡说明
此实现先不管session问题,先实现Tomcat可以通过nginx调度
(2)实验拓扑图:
(3)nginx配置: 192.168.38.27
upstream tomcat {
#ip_hash; # 先禁用看看轮询,之后开启开黏性
server node1.zcpzcp.com:8080;
server node2.zcpzcp.com:8080;
}
location / {
root /data/nginx;
index index.html;
location ~* \.(jsp|do)$ {
proxy_pass http://tomcat;
#proxy_pass http://node1.zcpzcp.com:8080;
}
}
[root@centos7-27 ~]# cat /etc/hosts
192.168.38.17 node1.zcpzcp.com
192.168.38.37 node2.zcpzcp.com
(4)tomcat配置:192.168.38.17(node1.zcpzcp.com)
a.tomcat虚拟主机配置:
#<Engine name="Catalina" defaultHost="localhost"> #默认的缺省虚拟主机为localhost
<Engine name="Catalina" defaultHost="node1.zcpzcp.com"> #将缺省主页修改为node1.zcpzcp.com虚拟主机
....
<Host name="node1.zcpzcp.com" appBase="/data/webapp/"
unpackWARs="true" autoDeploy="true">
</Host>
b.idnex.jsp文件:
[root@centos7-17 tomcat]# cat /data/webapp/ROOT/index.jsp
<%@ page import="java.util.*" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>lbjsptest</title>
</head>
<body>
<div>On <%=request.getServerName() %></div> #取servername
<div><%=request.getLocalAddr() + ":" + request.getLocalPort() %></div> #取本地IP和port
<div>SessionID = <span style="color:blue"><%=session.getId() %></span></div>#显示SessionID
<%=new Date()%> #当前时间
</body>
</html>
(5)tomcat配置:192.168.38.37(node2.zcpzcp.com)
a.tomcat虚拟主机配置:
#<Engine name="Catalina" defaultHost="localhost"> #默认的缺省虚拟主机为localhost
<Engine name="Catalina" defaultHost="node2.zcpzcp.com"> #将缺省主页修改为node1.zcpzcp.com虚拟主机
....
<Host name="node2.zcpzcp.com" appBase="/data/webapp/"
unpackWARs="true" autoDeploy="true">
</Host>
b.idnex.jsp文件:
[root@centos7-37 tomcat]# cat /data/webapp/ROOT/index.jsp
<%@ page import="java.util.*" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>lbjsptest</title>
</head>
<body>
<div>On <%=request.getServerName() %></div>
<div><%=request.getLocalAddr() + ":" + request.getLocalPort() %></div>
<div>SessionID = <span style="color:blue"><%=session.getId() %></span></div>
<%=new Date()%>
</body>
</html>
(6)Windows的解析文件配置:C:\Windows\System32\drivers\etc\hosts
192.168.38.17 node1.zcpzcp.com
192.168.38.37 node2.zcpzcp.com
192.168.38.27 www.zcpzcp.com
(7)测试:http://www.zcpzcp.com/index.jsp
a.浏览器第一次访问:
b.ctrl+r 刷新访问:
c.ctrl+r 刷新访问:
总结:由于负载均衡,浏览器每次被调度至不同的服务器,但是sessionID浏览器只能保存一份(相同域名),因此sessionID一直改变,无法实现浏览器与web server的会话保持。
(3)session绑定的方式解决session问题 |
(1)基于session绑定:nginx上实现基于源地址hash
upstream tomcat {
ip_hash; # 先禁用看看轮询,之后开启开黏性
server node1.zcpzcp.com:8080;
server node2.zcpzcp.com:8080;
}
==>预计看到的现象:当浏览器访问:http://www.zcpzcp.com/index.jsp,将一直被调度至web server中其他一台Tomcat上。
(2)测试:基于源地址hash
a.ctrl+r 刷新访问:
a.ctrl+r 刷新访问:
==>现象:仅有时间改变,sessionID和web server的tomcat均未发生改变
(3)总结:基于源地址hash
①使用基于源地址解决session问题,是一种不好的处理方式,因为web server做负载均衡的目的就是实现将访问的并发分摊至后端各个服务器,但是基于源地址hash,调度始终指向后端某台服务器。
②现在局域网上网基本都是通过SNAT实现的,因此整个局域网上网映射的公网IP相同,基于源地址hash则认为整个局域网的人都属于同一用户。
③其实session绑定中基于cookie做hash仅仅是将用户根据浏览器的不同调度至后端服务器,即将同一局域网和同一主机的不同浏览器分辨出为不同用户,依旧解决不了后端服务器故障的问题。
④session绑定没能从根本上解决问题,即浏览器中的sessionID必须去有sessionID这台后端服务器才能有用,如果带着此sessionID被调度至别的后端服务器,后端服务器找不到此sessionID时,将重发sessionID,等带着此sessionID去访问后端服务器别的主机时,别的主机也找不到此sessionID,本质上解决问题:需要所有的后端服务器均能识别浏览器本次访问时所携带的sessionID。
注1:web server的tomcat代表后端动态服务器,即后端动态服务器使用tomcat举例。
注2:session绑定的方式实现虽然简单,但是实际上却不会使用session绑定的方式处理会话状态保持的问题。
(4)后端服务器session同步解决session问题 |
(1)tomcat session集群
tomcat session集群即后端服务器如果有多个tomcat时,多个tomcat之间将对session进行数据同步,则每一个tomcat节点都有整个tomcat整个集群的session信息。因此浏览器无论被调度至哪一个tomcat节点,tomcat均能识别sessionID,进而完成会话状态保持。
(2)实现tomcat集群的配置
官方文档: https://tomcat.apache.org/tomcat-8.5-doc/cluster-howto.html
- 建议直接去官网粘贴源码,只需将NioReceiver中的address地址修改为当前主机用于tomcat集群通信的IP
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="8">
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="230.100.100.8" #tomcat配置相同的多播地址和端口就是同组的tomcat,可以相互通信,心跳检测之类的
port="45564"
frequency="500"
dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto" #如果此处的address=auto将选择则127.0.0.1,应该填写当前主机用于tomcat集群通信的IP
port="4000" #同组tomcat集群之间发送session信息的端口
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport
className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor
className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor
className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
- 这段代码可以放置的位置:
①engine(对所以虚拟主机生效)即所有虚拟主机都可以启用Session复制
②host(对当前虚拟主机生效)即该虚拟主机可以启用Session复制
(3)修改配置文件:192.168.38.17
① <Engine name="Catalina" defaultHost="node1.zcpzcp.com" jvmRoute="Tomcat17">
在默认的添加了jvmRoute="Tomcat17" ==>为了后续实验观察现象
②在host虚拟主机可以启用Session复制
<Host name="node1.zcpzcp.com" appBase="/data/webapp/"
unpackWARs="true" autoDeploy="true">
...
==>添加tomcat集群的配置
...
</Host>
(4)修改配置文件:192.168.38.37
① <Engine name="Catalina" defaultHost="node2.zcpzcp.com" jvmRoute="Tomcat37">
在默认的添加了jvmRoute="Tomcat37" ==>为了后续实验观察现象
②在host虚拟主机可以启用Session复制
<Host name="node2.zcpzcp.com" appBase="/data/webapp/"
unpackWARs="true" autoDeploy="true">
...
==>添加tomcat集群的配置
...
</Host>
(5)Make sure your web.xml has the <distributable/> element
注意:仅在虚拟主机开启session复制是不够的!如下是tomcat官方提供的实现:
注意:集群实验中各集群节点一定要保证时间一致,时间不一致,集群的实现肯定有问题。
[root@centos7-37 tomcat]#mkdir /data/webapp/ROOT/WEB-INF
[root@centos7-37 tomcat]#cp conf/web.xml /data/webapp/ROOT/WEB-INF/
[root@centos7-37 tomcat]#vim /data/webapp/ROOT/WEB-INF/web.xml
</welcome-file-list>
<distributable/> #在/data/webapp/ROOT/WEB-INF/web.xml最后添加<distributable/>
</web-app>
192.168.38.17(tomcat另一节点)做以上相同操作,也需要/data/webapp/ROOT/WEB-INF/web.xml文件并添加<distributable/>
(6)session同步测试:
①打开浏览器访问此域名:http://www.zcpzcp.com/index.jsp
②ctrl+r刷新:
==>sessionID未改变,无论调度至192.168.38.17还是192.168.38.37
(7)总结:tomcat session集群的方式解决session问题
①每个tomcat节点都需保存整个节点的session信息,tomcat集群中主机数目小,session信息少还可以使用此方法实现,若session信息较多,那么对于tomcat内存的要求。
②随着tomcat节点数目的增加,内部通信的代价也将逐渐增加。
③session同步解决session问题两个问题:后端动态服务器的内存和内部通信,适用于小场景,session较少,动态服务器集群数量较少。
(5)session共享 |
(1)msm
msm(memcached session manager)提供将Tomcat的session保持到memcached或redis的程序,可以实现高可用。
目前项目托管在Github, https://github.com/magro/memcached-session-manager
支持Tomcat的6.x、7.x、8.x、9.x。
(2)tomcat与缓存服务:
官网说明: https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration
- Tomcat的Session管理类,Tomcat版本不同
memcached-session-manager-2.3.2.jar
memcached-session-manager-tc8-2.3.2.jar - Session数据的序列化、反序列化类
官方推荐kyro
在webapp中WEB-INF/lib/下 - 驱动类
memcached(spymemcached.jar)
Redis(jedis.jar)
(3)tomcat需要安装的包:
tomcat将session发送给缓存服务:
支持序列化的jar包:
将spymemcached.jar、memcached-session-manage、kyro相关的jar文件都放到Tomcat的lib目录中去,这个目录是 $CATALINA_HOME/lib/ ,对应本次安装就是/usr/local/tomcat/lib。
sticky模式 |
(1)sticky原理:
①官网画的拓扑图
②按照官网画的图重新绘制的拓扑图
③原理阐述:
当请求结束时Tomcat的session会送给memcached备份。即Tomcat session为主session,memcached session为备session,使用memcached相当于备份了一份Session。查询Session时Tomcat会优先使用自己内存的Session,Tomcat通过jvmRoute发现不是自己的Session,便从memcached中找到该Session,更新本机Session,请求完成后更新memcached。
④示例:
- tomcat1中有个session1 那么session1保存的位置:tomcat1 和 memcache2
如果memcache2 节点故障,那么session1 的信息才会被迫存放至 memcache1。
⑤总结:
- sticky模式最关键的是:使用tomcat自己的内存作为存储session的主,然后交叉以对方的memcache缓存作为’备份的主’(备份的主简单为数据优先备份存放的位置)。
sticky的实现
注:此处tomcat操作相同,省略一个节点的tomcat操作。
①安装memcache
[root@centos7-17 tomcat]# yum install -y memcached
[root@centos7-17 tomcat]# systemctl start memcached.service
②tomcat配置
- node1配置中为failoverNodes=“n17”, node2配置为failoverNodes=“n37”
- failoverNodes故障转移节点,n17是备用节点,n37是主存储节点。另一台Tomcat将n17改为n37,其主节点是n17,备用节点是n37
- memcached的节点们;n17、n37只是别名,可以重新命名。
conf/context.xml 中
<Context>
...
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n17:192.168.38.17:11211,n37:192.168.38.37:11211"
failoverNodes="n17" #故障转移节点
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
</Context>
③配置tomcat配置文件截图:
④配置成功的显示日志:
sticky的测试:
①浏览器访问:http://www.zcpzcp.com/index.jsp
==>调度至192.168.38.17主机,那么session信息应该保存在192.168.38.37主机
②查看memcache的数据:
memcache的数据:
[('192.168.38.17:11211 (1)', {'1DF770481060C28E523C7E0312C3F09C-n17.Tomcat37': '[99 b; 1574424758 s]'})
('192.168.38.37:11211 (1)', {'1DF770481060C28E523C7E0312C3F09C-n37.Tomcat17': '[99 b; 1574424311 s]'})]
==>sessionID无论在17或者37主机均为:1DF770481060C28E523C7E0312C3F09C
non-sticky模式 |
(1)non-sticky模式原理
①拓扑图:
②原理:
-
从msm 1.4.0之后开始支持non-sticky模式。
-
Tomcat session为中转Session,如果n1为主session,n2为备session。产生的新的Session会发送给主、备memcached,并清除本地Session。
-
其中memcache主节点下线,则备用节点转正,主节点上线,将成为目前的备用节点。
(2)non-sticky的tomcat配置:
<Context>
...
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n17:192.168.142.152:11211,n37:192.168.142.153:11211"
sticky="false"
sessionBackupAsync="false"
lockingMode="uriPattern:/path1|/path2"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
</Context>
主要变化是这三行:
sticky="false"
sessionBackupAsync="false"
lockingMode="uriPattern:/path1|/path2"
- 修改配置后重启memcache 和 tomcat服务,重启memcache主要是将上次实现的数据清空
(3)non-sticky测试:
①浏览器重新访问:http://www.zcpzcp.com/index.jsp
②查看memcache中保存的session信息
③当17主机的memcache服务停止,浏览器继续访问和memcache的信息保存:
==>sessionID仍然没有改变,但是memcache存储的session只能放置37主机。
最后附上:查看memcache内存信息的python程序:
- 注:pip install python-memcached
- 运行时需要安装python-memcached,作为memcache客户端连接memcache
import memcache # pip install python-memcached
mc = memcache.Client(['192.168.142.152:11211','192.168.142.153:11211'], debug=True)
stats = mc.get_stats()[0]
print(stats)
for k,v in stats[1].items():
print(k, v)
print('-' * 30)
# 查看全部key
print(mc.get_stats('items')) # stats items 返回 items:5:number 1
print('-' * 30)
print(mc.get_stats('cachedump 5 0')) # stats cachedump 5 0 # 5和上面的items返回的值有关;0表示全部