# Tomcat 配置管理实践# 一、OracleJdk 安装OracleJdk 下载地址:https://www.oracle.com/java/technologies/downloads/#java8
[ root@web01 ~] [ root@web01 ~] [ root@web01 ~] export JAVA_HOME = /usr/local/jdkexport PATH = $PATH : $JAVA_HOME /binexport JRE_HOME = $JAVA_HOME /jre export CLASSPATH = $JAVA_HOME /lib/:$JRE_HOME /lib/[ root@web01 ~] [ root@web01 ~]
# 二、Tomcat 安装[ root@web01 ~] [ root@web01 soft] [ root@web01 soft] [ root@web01 soft] [ root@web01 soft] [ root@web01 soft] tcp6 0 0 :::8080 :::* LISTEN 1765 /java tcp6 0 0 127.0 .0.1:8005 :::* LISTEN 1765 /javaa [ root@web01 soft] [ Unit] Description = Apache TomcatAfter = network.target[ Service] Type = forkingEnvironment = JAVA_HOME= /usr/local/jdkEnvironment = CATALINA_HOME= /soft/tomcatEnvironment = CATALINA_BASE= /soft/tomcatExecStart = /soft/tomcat/bin/startup.shExecStop = /soft/tomcat/bin/shutdown.sh[ Install] WantedBy = multi-user.target[ root@web01 soft] [ root@web01 soft] [ root@web01 soft] [ root@web01 ~] tcp6 0 0 :::8080 :::* LISTEN 1842 /java tcp6 0 0 127.0 .0.1:8005 :::* LISTEN 1842 /java
# 三、Tomcat 配置# 3.1 Tomcat 目录结构bin : 二进制可执行(脚本文件 windwos版bat、linux版sh) conf : 配置文件,主配置是server.xml lib : 都是一些java语言编写的jar包,这些都需要被调用,才能执行; module (jar、war) logs : tomcat的日志存放位置,可修改; temp : tomcat的临时目录; webapps : 相当于nginx中 root /soft/tomcat/webapps/ /soft/tomcat/webapps/ROOT work : tomcat的一个缓存目录;(java代码,编译为class字节码对外提供;)
# 3.2 Tomcat 配置文件说明server:表示一个 tomcat 实例; Listener:监听器; connector:连接器,支持 http、https、ajp 等协议请求; service:将 connector 与 engine 关联的组件; engine:具体 http、https 的请求与响应; (Nginx 中 http {}) host:与用户请求的 Host 字段进行比对;哪个 Host 匹配则哪个处理,如没有符合的则交给默认的 defaultHost 处理 [ root@web01 soft] < ?xml version = "1.0" encoding = "UTF-8" ?> < ! --关闭tomcat的端口--> < Server port = "8005" 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 = "8080" protocol = "HTTP/1.1" connectionTimeout = "20000" 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>
# 3.3Tomcat 请求流程
假设来自用户的请求为:http://tomcat.hmallleasing.com:8080/index.jsp
1. 服务端运行 Tomcat 应用,监听在本机的 8080 端口,等待连接; 2. 浏览器发送 http 请求,并且请求服务端的 8080 端口,也就是 Tomcat 进程; 3. 服务端通过 http connector 连接器获取请求,然后通过 service 将请求交给符合条件的 engine; 4.Engine 获得请求 tomcat.hmallleasing.com:8080/index.jsp,遍历它所有虚拟主机 Host; 5. 如果 Engine 匹配不到对应的 Host,就把请求交给 Engine 中的 defaultHost 处理; 6. 如果 Engine 匹配到对应的 Host, 则提取该 Host 中指定的 appBase(代码存储路径); 7. 最后 Engine 将解析后的结果返回给 connector,connector 返回给浏览器; # 3.5 Tomcat 与 Nginx 对比Nginx 中:
http 层:只能有一个; server:可以有多个,多个表示多个站点; location:一个站点的多个 uri 的路径匹配 Tomcat 中:
engine:只能有一个,也是负责请求与响应的; Host: 可以有多个,多个表示多个站点; context:用来定义用户请求的 uri,将其调度到指定的目录中提取代码; # 3.6 Tomcat 虚拟主机 Hostname: 主机名称 appBase:网站代码存放路径 unpackWARs:自动解压 war 包 autoDeploy:自动部署 < ! --tom1.hmallleasing.com--> < Host name = "tom1.hmallleasing.com" appBase = "/code/tom1" unpackWARs = "true" autoDeploy = "true" > < Valve className = "org.apache.catalina.valves.AccessLogValve" directory = "logs" prefix = "tomcat_access_log" suffix = ".txt" pattern = "%h %l %u %t "%r" %s %b" /> < /Host>
创建网站代码存放路径,写入测试代码,并重启 Tomcat;
[ root@web01 soft] [ root@web01 soft] [ root@web01 soft]
# 3.7 Tomcat 虚拟主机 Contextcontext 使用方式
docBase: 站点存放的路径 path: 访问站点的 URI 路径 reloadable:修改了 jsp 文件,无需重启就可以实现显示的同步 访问 http:/tom2.hamllleasing.com/zh-> 映射 ->/code/zh
< ! --tom1.hmallleasing.com--> < Host name = "tom1.hmallleasing.com" appBase = "/code/tom1" unpackWARs = "true" autoDeploy = "true" > < Context docBase = "/code/zh" path = "/zh" reloadable = "true" /> < Valve className = "org.apache.catalina.valves.AccessLogValve" directory = "logs" prefix = "tomcat_access_log" suffix = ".txt" pattern = "%h %l %u %t "%r" %s %b" /> < /Host>
注意:如果设定了 context,没有创建对应目录则无法启动 Tomcat
[ root@web01 tomcat] .. . Caused by: java.lang.IllegalArgumentException: The main resource set specified [ /code/zh] is not a directory or war file, or is not readable ( it does not exist or permissions to access it are missing) at org.apache.catalina.webresources.StandardRoot.createMainResourceSet( StandardRoot.java:758) at org.apache.catalina.webresources.StandardRoot.startInternal( StandardRoot.java:715) at org.apache.catalina.util.LifecycleBase.start( LifecycleBase.java:164) .. .
创建对应目录,写入测试代码,并重启 Tomcat
[ root@web01 tomcat] [ root@web01 tomcat] [ root@web01 tomcat]
# 3.8 Tomcat 管理页面配置1、配置 Tomcat Bsic 认证,配置文件 /soft/tomcat/conf/tomcat-users.xml 最后添加用户,然后关联至对应资源的角色名称;
[ root@web03 ~] .. . < role rolename = "manager-gui,admin-gui" /> < user username = "tomcat" password = "123456" roles = "manager-gui,admin-gui" /> < /tomcat-users>
2、由于 Tomcat 仅语序本地 127.0.0.1 进行 basic 认证,如果需要其他网段通过 basic 认证,还需配置允许访问规则。找到 webapps/ 项目 / META-INF/context.xml 配置;
[ root@web01 tomcat] 修改为: allow = "127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1|192\.168\.40\.\d+" /> [ root@web01 tomcat] 修改后: allow = "127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1|192\.168\.40\.\d+" /> [ root@web01 ~]
# 四、Tomcat 部署 zrlog# 4.1 zrlog 单节点部署# 4.1.1 部署 MySQL[ root@db01 ~] [ root@db01 ~] [ root@db01 ~] [ root@db01 ~] [ root@db01 ~] [ root@db01 ~] [ root@db01 ~] [ root@db01 ~] [ root@db01 ~] [ root@db01 ~] */5 * * * * /usr/sbin/ntpdate time2.aliyun.com &> /dev/nul [ root@db01 ~] [ root@db01 ~] [ root@db01 ~] [ root@aizj_db01 ~] mysql> ALTER USER 'root' @'localhost' IDENTIFIED BY 'Superman*2023' ; mysql> grant all on *.* to 'zrlog' @'192.168.40.%' identified by 'Superman*2023' ; mysql -u root -p"youpass" mysql> GRANT ALL PRIVILEGES ON *.* TO 'root' @'%' IDENTIFIED BY 'Superman*2023' WITH GRANT OPTION; FLUSH PRIVILEGES; mysql> create database zrlog; Query OK, 1 row affected ( 0.00 sec)
# 4.1.2 配置 Tomcat 虚拟主机[ root@web01 ~] .. .< ! --zrlog.hmallleasing.com--> < Host name = "zrlog.hmallleasing.com" appBase = "/code/zrlog" unpackWARs = "true" autoDeploy = "true" > < Valve className = "org.apache.catalina.valves.AccessLogValve" directory = "logs" prefix = "zrlog_access_log" suffix = ".txt" pattern = "%h %l %u %t "%r" %s %b" /> < /Host> .. .
# 4.1.3 准备项目代码[ root@web01 ~] [ root@web01 ~] [ root@web01 ~] ROOT.war [ root@web01 ~]
# 4.2 zrlog 集群部署服务器名称 服务器 IP 服务器角色 lb01 192.168.40.150 Nginx web01 192.168.40.7 Tomcat web02 192.168.40.8 Tomcat NFS 192.168.40.32 NFS db01 192.168.40.51 MySQL Redis 192.168.40.41 Redis
# 4.2.1 安装 jdk[ root@web01 ~] [ root@web02 ~] [ root@web02 ~] [ root@web02 ~] export JAVA_HOME = /usr/local/jdkexport PATH = $PATH : $JAVA_HOME /binexport JRE_HOME = $JAVA_HOME /jre export CLASSPATH = $JAVA_HOME /lib/:$JRE_HOME /lib/[ root@web02 ~] [ root@web02 ~]
# 4.2.2 安装 Tomcat[ root@web01 ~] [ root@web02 ~] [ root@web02 soft] [ root@web01 ~]
# 4.2.3 部署 zrlog[ root@web01 ~] [ root@web02 ~] [ root@web02 ~] [ root@web02 ~] tcp6 0 0 :::8080 :::* LISTEN 1806 /java tcp6 0 0 127.0 .0.1:8005 :::* LISTEN 1806 /java
# 4.2.4 接入 Nginx 负载均衡[ root@lb01 ~] [ root@lb01 ~] upstream zrlog { server 192.168 .40.7:8080; server 192.168 .40.8:8080; } server { listen 80 ; server_name zrlog.hmallleasing.com; location / { proxy_pass http://zrlog; include proxy_params; } } [ root@lb01 ~] proxy_set_header Host $http_host ; proxy_set_header X-Real-IP $remote_addr ; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; proxy_connect_timeout 30 ; proxy_send_timeout 60 ; proxy_read_timeout 60 ; proxy_buffering on; proxy_buffer_size 32k; proxy_buffers 4 128k; [ root@lb01 conf.d]
# 4.2.5 接入共享存储 Nfs[ root@nfs ~] [ root@nfs ~] /data/zrlog 192.168 .40.0/24( rw,all_squash,anonuid= 666 ,anongid= 666 ) [ root@nfs ~] [ root@nfs ~] [ root@nfs ~] [ root@nfs ~] [ root@nfs ~] [ root@web01 ~] [ root@web01 ~] [ root@web01 ~] [ root@web01 ~] [ root@web01 ~] [ root@web02 ~] [ root@web02 ~] [ root@web02 ~] [ root@web02 ~] [ root@web02 ~]
# 4.2.6 集群配置 Https[ root@lb01 ssl_key] upstream zrlog { server 192.168 .40.7:8080; server 192.168 .40.8:8080; } server { listen 443 ssl; server_name zrlog.hmallleasing.com; client_max_body_size 1G; ssl_prefer_server_ciphers on; ssl_certificate /etc/nginx/ssl_key/hmallleasing.com.pem; ssl_certificate_key /etc/nginx/ssl_key/hmallleasing.com.key; ssl_session_timeout 100m; ssl_session_cache shared:cache:10m; location / { proxy_pass http://zrlog; include proxy_params; } } server { listen 80 ; server_name zrlog.hmallleasing.com; return 302 https://$server_name $request_uri ; } [ root@lb01 ~] [ root@lb01 ssl_key] [ root@lb01 ~]
# 4.2.7 Tomcat 会话保持 session1、为 web01、web02 节点添加虚拟主机
[ root@web01 ~] < ! --session.hmallleasing.com--> < Host name = "session.hmallleasing.com" appBase = "/code/session" unpackWARs = "true" autoDeploy = "true" > < Valve className = "org.apache.catalina.valves.AccessLogValve" directory = "logs" prefix = "zrlog_access_log" suffix = ".txt" pattern = "%h %l %u %t "%r" %s %b" /> < /Host>
2、为 web01、web02 配置 session 相关的网页
[ root@web01 ~] [ root@web01 ~] < body> < %//HttpSession session = request.getSession( true) ; System.out.println( session.getCreationTime( )) ; out.println( "<br> web01 SESSION ID:" + session.getId( ) + "<br>" ) ; out.println( "Session created time is :" + session.getCreationTime( ) + "<br>" ) ; %> < /body> [ root@web01 ~] [ root@web01 ~] [ root@web02 ~] [ root@web02 ~] < body> < %//HttpSession session = request.getSession( true) ; System.out.println( session.getCreationTime( )) ; out.println( "<br> web02 SESSION ID:" + session.getId( ) + "<br>" ) ; out.println( "Session created time is :" + session.getCreationTime( ) + "<br>" ) ; %> < /body> [ root@web02 ~] [ root@web02 ~]
3、接入负载均衡,测试检查,看看是否通过轮询调度,获取的 session 是不一致
[ root@lb01 ~] upstream session { server 192.168 .40.7:8080; server 192.168 .40.8:8080; } server { listen 443 ssl; server_name session.hmallleasing.com; client_max_body_size 1G; ssl_prefer_server_ciphers on; ssl_certificate /etc/nginx/ssl_key/hmallleasing.com.pem; ssl_certificate_key /etc/nginx/ssl_key/hmallleasing.com.key; ssl_session_timeout 100m; ssl_session_cache shared:cache:10m; location / { proxy_pass http://session; include proxy_params; } } server { listen 80 ; server_name session.hmallleasing.com; return 302 https://$server_name $request_uri ; } [ root@lb01 conf.d] [ root@lb01 conf.d]
当用户通过负载均衡向服务器 A 发起 http 请求,A 服务器会下发一个 sessionID,通过 head 中 set-cookie 回传用户,用户下次请求时会携带 cookie 字段加上服务器用户名和密码进行请求 A 服务器,该值就是 A 服务器下发的 sessionID,如果验证通过则登录成功。由于负载均衡采用轮询调度机制,请求 A 节点下发一个 sessionID,用户带着 A 节点下发一个 sessionID 请求负载均衡,会调度到 B 节点,B 节点不认 A 节点 sessionID,最终形成死循环。
4、使用负载均衡 ip_hash 来解决 session;
[ root@lb01 ~] upstream session { ip_hash; server 192.168 .40.7:8080; server 192.168 .40.8:8080; } server { listen 443 ssl; server_name session.hmallleasing.com; client_max_body_size 1G; ssl_prefer_server_ciphers on; ssl_certificate ssl_key/hmallleasing.com.pem; ssl_certificate_key ssl_key/hmallleasing.com.key; ssl_session_timeout 100m; ssl_session_cache shared:cache:10m; location / { proxy_pass http://session; include proxy_params; } } server { listen 80 ; server_name session.hmallleasing.com; return 302 https://$server_name $request_uri ; } [ root@lb01 ~] [ root@lb01 ~]
5、通过 redis 来解决 session 会话保持
[ root@redis ~] [ root@redis ~] bind 127.0 .0.1 192.168 .40.104requirepass 123456 [ root@redis ~] [ root@db01 ~] 192.168 .40.104:6379 > keys * https://github.com/ran-jit/tomcat-cluster-redis-session-manager [ root@web01 ~] [ root@web01 ~] [ root@web01 ~] [ root@web01 ~] [ root@web01 ~] redis.hosts = 192.16 .40.41:6379redis.password = 123456 [ root@web02 ~] .. . < Valve className = "tomcat.request.session.redis.SessionHandlerValve" /> < Manager className = "tomcat.request.session.redis.SessionManager" /> < /Context> [ root@web01 ~] .. .< session-config> < session-timeout> 6 0 < /session-timeout> < /session-config> [ root@web01 ~] [ root@redis ~] 192.168 .40.41:6379 > flushdb192.168 .40.41:6379 > keys *1 ) "A8704CF8988CF60C30080420BA66AD0F"