Tomcat实验2之负载均衡和session

Tomcat实验二

(1)Tomcat常见部署方式:


(2)Tomcat要实现负载均衡,必然涉及session问题:
session问题的分析:https://blog.csdn.net/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表示全部



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值