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表示全部