利刃出鞘_Tomcat 核心原理解析(八)-- Tomcat 集群
一、Tomcat专题 - Tomcat集群 - 介绍及准备工作
1、Tomcat集群 简介
由于单台Tomcat的承载能力是有限的,当我们的业务系统用户量比较大,请求压力比较大时,单台Tomcat是扛不住的,这个时候,就需要搭建Tomcat的集群,而目前比较流程的做法就是通过Nginx来实现Tomcat集群的负载均衡。
2、Tomcat 集群环境准备–准备 Tomcat
1)在服务器上, 安装两台 tomcat, 然后分别改 Tomcat 服务器的端口号 :
8005 ‐‐‐‐‐‐‐‐‐> 8015 ‐‐‐‐‐‐‐‐‐> 8025
8080 ‐‐‐‐‐‐‐‐‐> 8888 ‐‐‐‐‐‐‐‐‐> 9999
8009 ‐‐‐‐‐‐‐‐‐> 8019 ‐‐‐‐‐‐‐‐‐> 8029
2)新建一个目录 D:\java-test\tomcat-colony\
3)解压 apache-tomcat-8.5.42-windows-x64.zip 到 目录 D:\java-test\tomcat-colony\ 下,并命名为 D:\java-test\tomcat-colony\tomcat-8888\
4)再次解压 apache-tomcat-8.5.42-windows-x64.zip 到 目录 D:\java-test\tomcat-colony\ 下,并命名为 D:\java-test\tomcat-colony\tomcat-9999\
5)修改 D:\java-test\tomcat-colony\tomcat-8888\conf\server.xml 配置文件 端口。
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8015" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8888" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8019" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
<!-- D:\java-test\tomcat-colony\tomcat-8888\conf\server.xml -->
6)修改 D:\java-test\tomcat-colony\tomcat-9999\conf\server.xml 配置文件 端口。
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8025" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="9999" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8029" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
<!-- D:\java-test\tomcat-colony\tomcat-9999\conf\server.xml -->
3、Tomcat 集群环境准备–安装配置 Nginx
1)在当前服务器上 , 安装 Nginx :解压 nginx-1.8.1.zip 到 D:\java-test\tomcat-colony\ 目录下。
2)修改 D:\java-test\tomcat-colony\nginx-1.8.1\conf\nginx.conf 配置文件,配置 nginx。
# D:\java-test\tomcat-colony\nginx-1.8.1\conf\nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
upstream serverpool{
server localhost:8888;
server localhost:9999;
}
server {
listen 99;
server_name localhost;
location / {
proxy_pass http://serverpool/;
}
}
}
# D:\java-test\tomcat-colony\nginx-1.8.1\conf\nginx.conf
二、Tomcat专题 - Tomcat集群 - 负载均衡策略
1、修改 D:\java-test\tomcat-colony\tomcat-8888\webapps\ROOT\index.jsp 区别主页访问
......
<body>
<div id="wrapper">
<div id="navigation" class="curved container">
<span id="nav-home"><a href="${tomcatUrl}">Home-8888</a></span>
......
2、修改 D:\java-test\tomcat-colony\tomcat-9999\webapps\ROOT\index.jsp 区别主页访问
......
<body>
<div id="wrapper">
<div id="navigation" class="curved container">
<span id="nav-home"><a href="${tomcatUrl}">Home-9999</a></span>
......
3、启动 两个 tomcat 和 nginx 浏览器访问测试。
1)启动 端口 8888 的 tomcat
D:\java-test\tomcat-colony\tomcat-8888\bin\startup.bat
2)启动 端口 9999 的 tomcat
D:\java-test\tomcat-colony\tomcat-9999\bin\startup.bat
3)启动 nginx
D:\java-test\tomcat-colony\nginx-1.8.1\nginx.exe
4)浏览器访问测试
http://localhost:8888/ 访问 Home-8888
http://localhost:9999/ 访问 Home-9999
http://localhost:99/ 发现 轮询访问 Home-8888 和 Home-9999
4、负载均衡策略
1) 轮询
最基本的配置方法,它是 upstream 模块默认的负载均衡默认策略。每个请求会按时间顺序逐一分配到不同的后端服务器。
- 修改 D:\java-test\tomcat-colony\nginx-1.8.1\conf\nginx.conf 配置文件,配置 nginx 负载均衡策略–轮询。
upstream serverpool{
server localhost:8888;
server localhost:9999;
}
- 参数说明:
参数 | 描述 | |
---|---|---|
fail_timeout | 与max_fails结合使用 | |
max_fails | 设置在fail_timeout参数设置的时间内最大失败次数,如果在这个时间内,所有针对该服务器的请求都失败了,那么认为该服务器会被认为是停机了 | |
fail_time | 服务器会被认为停机的时间长度,默认为10s | |
backup | 标记该服务器为备用服务器。当主服务器停止时,请求会被发送到它这里 | |
down | 标记服务器永久停机了 | |
2) weight 权重
- 权重方式,在轮询策略的基础上指定轮询的几率。
upstream serverpool{
server localhost:8888 weight=3;
server localhost:9999 weight=1;
}
- weight 参数用于指定轮询几率,weight的默认值为1;weight的数值与访问比率成正比,
- 比如8888服务器上的服务被访问的几率为9999服务器的三倍。
- 此策略比较适合服务器的硬件配置差别比较大的情况。
3) ip_hash
指定负载均衡器按照基于客户端IP的分配方式,这个方法确保了相同的客户端的请求一直发送到相同的服务器,以保证 session 会话。这样每个访客都固定访问一个后端服务器,可以解决 session 不能跨服务器的问题。
upstream serverpool{
ip_hash;
server 192.168.192.133:8080;
server 192.168.192.137:8080;
}
三、Tomcat专题 - Tomcat集群 - session共享问题及方案
1、Session 共享问题
在 Tomcat 集群中,如果应用需要用户进行登录,那么这个时候,用于 tomcat 做了负载均衡,则用户登录并访问应用系统时,就会出现问题 。
2、Session 共享方案
1) ip_hash 策略
一个用户发起的请求,只会请求到 tomcat1 上进行操作,另一个用户发起的请求只在 tomcat2 上进行操作 。那么这个时候,同一个用户发起的请求,都会通过 nginx 的 ip_hash 策略,将请求转发到其中的一台 Tomcat 上。
2) Session 复制
四、Tomcat专题 - Tomcat集群 - Session复制
1、在 servlet_demo01 工程模块中,创建 session.jsp 页面,
<%--
D:\java-test\idea2019\project_tomcat\servlet_demo01\web\session.jsp
Created by IntelliJ IDEA.
User: Administrator
Date: 2024/8/24
Time: 21:15
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
TOMCAT ‐ 9999 :
<br/>
sessionID : <%= session.getId()%>
<br/>
<%
Object loginUser = session.getAttribute("loginUser");
if(loginUser != null && loginUser.toString().length()>0){
out.println("session 有值, loginUser = " + loginUser);
}else{
session.setAttribute("loginUser","dgz168");
out.println("session 没有值");
}
%>
</body>
</html>
2、servlet_demo01 工程模块 创建,请点击:
# 利刃出鞘_Tomcat 核心原理解析(六)
3、打包 servlet_demo01 工程模块成 servlet_demo01.war 包。
idea --> File
--> Project Structure
--> Artifacts
--> 点击 【+】
--> 选择【Web Application Archive】
--> Name : ( servlet_demo01 )
点击 servlet_demo01.war
右键 'servlet_demo01'compile output
Put into /WEB-INF/classes
右键 Java EE...
Put into /WEB-INF/lib
右键 Web facet resources
Put into Output Root
--> Apply --> OK
idea --> Build
--> Build Artifact
--> servlet_demo01
--> Build
--> 到 ...\out\artifacts\servlet_demo01\ 文件夹,找到 servlet_demo01.war
4、复制 servlet_demo01.war 到 tomcat 目录中。并启动两个 tomcat 服务。
D:\java-test\tomcat-colony\tomcat-8888\webapps\servlet_demo01.war
D:\java-test\tomcat-colony\tomcat-9999\webapps\servlet_demo01.war
1)启动 端口 8888 的 tomcat
D:\java-test\tomcat-colony\tomcat-8888\bin\startup.bat
2)启动 端口 9999 的 tomcat
D:\java-test\tomcat-colony\tomcat-9999\bin\startup.bat
3)启动两个 tomcat 服务后,会生成两个文件夹:
D:\java-test\tomcat-colony\tomcat-8888\webapps\servlet_demo01\
D:\java-test\tomcat-colony\tomcat-9999\webapps\servlet_demo01\
5、修改 D:\java-test\tomcat-colony\tomcat-8888\webapps\servlet_demo01\session.jsp 页面,
重启两台 tomcat 和 nginx 进行测试。
<%--
D:\java-test\tomcat-colony\tomcat-8888\webapps\servlet_demo01\session.jsp
Created by IntelliJ IDEA.
User: Administrator
Date: 2024/8/24
Time: 21:15
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
TOMCAT ‐ 8888 :
<br/>
sessionID : <%= session.getId()%>
<br/>
<%
Object loginUser = session.getAttribute("loginUser");
if(loginUser != null && loginUser.toString().length()>0){
out.println("session 有值, loginUser = " + loginUser);
}else{
session.setAttribute("loginUser","dgz168");
out.println("session 没有值");
}
%>
</body>
</html>
1)启动 端口 8888 的 tomcat
D:\java-test\tomcat-colony\tomcat-8888\bin\startup.bat
2)启动 端口 9999 的 tomcat
D:\java-test\tomcat-colony\tomcat-9999\bin\startup.bat
3)启动 nginx
D:\java-test\tomcat-colony\nginx-1.8.1\nginx.exe
4)浏览器访问测试
http://localhost:8888/ 访问 Home-8888
http://localhost:9999/ 访问 Home-9999
http://localhost:99/
5)访问到的两台 Tomcat 出现的 sessionID 是不一样的,说明两台 Tomcat 的 Session 各是各的,并没有进行同步,这在集群环境下是存在问题的。
6、Session 同步的配置如下:
1) 在 Tomcat 的 conf/server.xml 配置如下:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
- D:\java-test\tomcat-colony\tomcat-8888\conf\server.xml
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8015" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8888" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8019" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<!-- Session 同步的配置 -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
<!-- D:\java-test\tomcat-colony\tomcat-8888\conf\server.xml -->
- D:\java-test\tomcat-colony\tomcat-9999\conf\server.xml
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8025" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="9999" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8029" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<!-- Session 同步的配置 -->
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
<!-- D:\java-test\tomcat-colony\tomcat-9999\conf\server.xml -->
2) 在 Tomcat 部署的应用程序 servlet_demo01 的 web.xml 中加入如下配置 :
<distributable/>
- D:\java-test\tomcat-colony\tomcat-8888\webapps\servlet_demo01\WEB-INF\web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>bbsServlet</servlet-name>
<servlet-class>djh.it.web.BbsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>bbsServlet</servlet-name>
<url-pattern>/bbs/findAll</url-pattern>
</servlet-mapping>
<context-param>
<param-name>project_param_01</param-name>
<param-value>dzs168</param-value>
</context-param>
<!-- 会话配置session-config -->
<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<name>JESSIONID</name>
<domain>localhost</domain>
<path>/</path>
<comment>Session Cookie</comment>
<http-only>true</http-only>
<secure>false</secure>
<max-age>3600</max-age>
</cookie-config>
</session-config>
<!-- 欢迎页面 -->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
<!-- Session 同步的配置 -->
<distributable/>
<!-- 错误页面 -->
<!-- <error-page>-->
<!-- <error-code>404</error-code>-->
<!-- <location>/404.html</location>-->
<!-- </error-page>-->
<!-- <error-page>-->
<!-- <error-code>500</error-code>-->
<!-- <location>/500.html</location>-->
<!-- </error-page>-->
<!-- <error-page>-->
<!-- <exception-type>java.lang.Exception</exception-type>-->
<!-- <location>/error.jsp</location>-->
<!-- </error-page>-->
</web-app>
<!-- D:\java-test\tomcat-colony\tomcat-8888\webapps\servlet_demo01\WEB-INF\web.xml -->
- D:\java-test\tomcat-colony\tomcat-9999\webapps\servlet_demo01\WEB-INF\web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>bbsServlet</servlet-name>
<servlet-class>djh.it.web.BbsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>bbsServlet</servlet-name>
<url-pattern>/bbs/findAll</url-pattern>
</servlet-mapping>
<context-param>
<param-name>project_param_01</param-name>
<param-value>dzs168</param-value>
</context-param>
<!-- 会话配置session-config -->
<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<name>JESSIONID</name>
<domain>localhost</domain>
<path>/</path>
<comment>Session Cookie</comment>
<http-only>true</http-only>
<secure>false</secure>
<max-age>3600</max-age>
</cookie-config>
</session-config>
<!-- 欢迎页面 -->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
<!-- Session 同步的配置 -->
<distributable/>
<!-- 错误页面 -->
<!-- <error-page>-->
<!-- <error-code>404</error-code>-->
<!-- <location>/404.html</location>-->
<!-- </error-page>-->
<!-- <error-page>-->
<!-- <error-code>500</error-code>-->
<!-- <location>/500.html</location>-->
<!-- </error-page>-->
<!-- <error-page>-->
<!-- <exception-type>java.lang.Exception</exception-type>-->
<!-- <location>/error.jsp</location>-->
<!-- </error-page>-->
</web-app>
<!-- D:\java-test\tomcat-colony\tomcat-9999\webapps\servlet_demo01\WEB-INF\web.xml -->
3) 配置完毕之后, 再次重启两个 Tomcat 服务,进行测试。
- 启动 端口 8888 的 tomcat
D:\java-test\tomcat-colony\tomcat-8888\bin\startup.bat
- 启动 端口 9999 的 tomcat
D:\java-test\tomcat-colony\tomcat-9999\bin\startup.bat
- 启动 nginx
D:\java-test\tomcat-colony\nginx-1.8.1\nginx.exe
- 浏览器访问测试
http://localhost:8888/ 访问 Home-8888
http://localhost:9999/ 访问 Home-9999
http://localhost:99/
7、Session 同步的配置 注意事项:
Session 同步的配置 只适用于较小的集群环境(节点数不超过4个),如果集群的节点数比较多的话,通过这种广播的形式来完成 Session 的复制,会消耗大量的网络带宽,影响服务的性能。
五、Tomcat专题 - Tomcat集群 - SSO解决Session共享问题
1、SSO-单点登录
单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统,也是用来解决集群环境Session共享的方案之一 。
2、SSO-单点登录 示意图
上一节关联链接请点击
# 利刃出鞘_Tomcat 核心原理解析(七)