Tomcat
TOMCAT
tomcat安装
准备jdk
rpm:
[root@node1 ~]# yum -y install jdk-8u291-linux-x64.rpm
[root@node1 ~]# cat /etc/profile.d/jdk.sh
export JAVA_HOME=/usr/java/default
export PATH=$JAVA_HOME/bin:$PATH
#以下两项非必须项
export JRE_HOME=$JAVA_HOME/jre
export CLASSPATH=$JAVA_HOME/lib/:$JRE_HOME/lib/
[root@node1 ~]#. /etc/profile.d/jdk.sh
二进制:
[root@node1 ~]# tar xvf jdk-8u241-linux-x64.tar.gz -C /usr/local/
[root@node1 ~]# cd /usr/local/
[root@node1 ~]# ln -s jdk1.8.0_241/ jdk
[root@node1 ~]# vim /etc/profile.d/jdk.sh
export JAVA_HOME=/usr/local/jdk
export PATH=$PATH:$JAVA_HOME/bin
#以下两项非必须项
#export JRE_HOME=$JAVA_HOME/jre
#export CLASSPATH=$JAVA_HOME/lib/:$JRE_HOME/lib/
[root@node1 ~]# . /etc/profile.d/jdk.sh
[root@node1 local]# java -version
java version "1.8.0_291"
Java(TM) SE Runtime Environment (build 1.8.0_291-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.291-b10, mixed mode)
1.包安装
centos 安装tomcat
[root@centos7 ~]# yum -y install tomcat tomcat-webapps tomcat-admin-webapps tomcat-docs-webapp #centos 7 版本较低
Ubuntu 安装tomcat
root@ubuntu:~# apt -y install openjdk-8-jdk
root@ubuntu:~# java -version
openjdk version "1.8.0_342"
OpenJDK Runtime Environment (build 1.8.0_342-8u342-b07-0ubuntu1~22.04-b07)
OpenJDK 64-Bit Server VM (build 25.342-b07, mixed mode)
2.二进制安装
[root@node1 ~]# useradd -r -s /sbin/nologin tomcat
[root@node1 ~]# tar -xvf apache-tomcat-8.5.82.tar.gz -C /usr/local/src/
[root@node1 ~]# ln -s /usr/local/src/apache-tomcat-8.5.82 /usr/local/src/tomcat
[root@node1 ~]# echo "PATH=/usr/local/src/tomcat/bin:$PATH" >/etc/profile.d/tomcat.sh
[root@node1 ~]# . /etc/profile.d/tomcat.sh
创建service文件
[root@node1 ~]# vim tomcat/conf/tomcat.conf
JAVA_HOME=/usr/java/default
[root@node1 ~]# vim /lib/systemd/system/tomcat.service
[Unit]
Description=Tomcat
#After=syslog.target network.target remote-fs.target nss-lookup.target
After=syslog.target network.target
[Service]
Type=forking
EnvironmentFile=/usr/local/src/tomcat/conf/tomcat.confvim /lib/systemd/system/tomcat.service
ExecStart=/usr/local/src/tomcat/bin/startup.sh
ExecStop=/usr/local/src/tomcat/bin/shutdown.sh
RestartSec=3
PrivateTmp=true
User=tomcat
Group=tomcat
[Install]
WantedBy=multi-user.target
[root@node1 ~]# chown -R tomcat:tomcat tomcat/
[root@node1 ~]# systemctl daemon-reload
[root@node1 ~]# systemctl start tomcat.service
[root@node1 ~]# systemctl status tomcat.service
tomcat.service - Tomcat
Loaded: loaded (/usr/lib/systemd/system/tomcat.service; disabled; vendor preset: disabled)
Active: active (running) since Thu 2022-09-29 00:27:43 CST; 5min ago
tomcat根目录结构和核心组件
[root@node1 ~]# tree -d -L 1 /usr/local/src/tomcat/
/usr/local/src/tomcat/
├── bin
├── conf
├── lib
├── logs
├── temp
├── webapps
└── work
目录 说明
bin 服务启动、停止等相关程序和文件
conf 配置文件
lib 库目录
logs 日志目录
webapps 应用程序,应用部署目录
work jsp编译后的结果文件,建议提前预热访问
配置文件
[root@node1 ~]# tree /usr/local/src/tomcat/conf/
/usr/local/src/tomcat/conf/
├── Catalina
│ └── localhost
├── catalina.policy
├── catalina.properties
├── context.xml
├── jaspic-providers.xml
├── jaspic-providers.xsd
├── logging.properties
├── server.xml
├── tomcat.conf
├── tomcat-users.xml
├── tomcat-users.xsd
└── web.xml
server.xml
主配置文件
web.xml
每个webapp只有“部署”后才能被访问,它的部署方式通常由web.xml进行定义,其存放位置为WEB-INF/目录中;此文件为所有的webapps提供默认部署相关的配置,每个web应用也可以使用专用配置文件,来覆盖全局文件
context.xml
用于定义所有web应用均需加载的Context配置,此文件为所有的webapps提供默认配置,每个web应用也可以使用自已专用的配置,它通常由专用的配置文件context.xml来定义,其存放位置为WEB-INF/目录中,覆盖全局的文件
tomcat-users.xml
用户认证的账号和密码文件
catalina.policy
当使用security选项启动tomcat时,用于为tomcat设置安全策略
catalina.properties
Tomcat 环境变量的配置,用于设定类加载器路径,以及一些与JVM调优相关参数
logging.properties
Tomcat 日志系统相关的配置,可以修改日志级别和日志路径等
核心组件
顶级组件
Server,代表整个Tomcat容器,一台主机可以启动多tomcat实例,需要确保端口不要产生冲突
服务类组件
Service,实现组织Engine和Connector,建立两者之间关联关系, service 里面只能包含一个Engine
连接器组件
Connector,有HTTP(默认端口8080/tcp)、HTTPS(默认端口8443/tcp)、AJP(默认端口
8009/tcp)协议的连接器,AJP(Apache Jserv protocol)是一种基于TCP的二进制通讯协议。
容器类
Engine、Host(虚拟主机)、Context(上下文件,解决路径映射)都是容器类组件,可以嵌入其它组件,
内部配置如何运行应用程序。
内嵌类
可以内嵌到其他组件内,valve、logger、realm、loader、manager等。以logger举例,在不同容器组
件内分别定义。
集群类组件
listener、cluster
Server 服务器,Tomcat 运行的进程实例,一个Server中可以有多个service,但通常就一个
Service 服务,用来组织Engine和Connector的对应关系,一个service中只有一个Engine
Connector 连接器,负责客户端的HTTP、HTTPS、AJP等协议连接。一个Connector只属于某一个Engine
Engine 即引擎,用来响应并处理用户请求。一个Engine上可以绑定多个Connector
Host 即虚拟主机,可以实现多虚拟主机,例如使用不同的主机头区分
Context 应用的上下文,配置特定url路径映射和目录的映射关系:url => directory
Tomcat启动一个Server进程。可以启动多个Server,即tomcat的多实例, 但一般只启动一个
创建一个Service提供服务。可以创建多个Service,但一般也只创建一个
每个Service中,是Engine和其连接器Connector的关联配置
可以为这个Service提供多个连接器Connector,这些Connector使用了不同的协议,绑定了不同的端口。其作用就是处理来自客户端的不同的连接请求或响应
Service 内部还定义了Engine,引擎才是真正的处理请求的入口,其内部定义多个虚拟主机Host
Engine对请求头做了分析,将请求发送给相应的虚拟主机
如果没有匹配,数据就发往Engine上的defaultHost缺省虚拟主机
Engine上的缺省虚拟主机可以修改
Host 定义虚拟主机,虚拟主机有name名称,通过名称匹配
Context 定义应用程序单独的路径映射和配置
tomcat处理请求过程
http://localhost:8080/test/index.jsp
1.浏览器端的请求发送到服务端8080,tomcat进程监听8080端口,通过侦听connector获得请求
2.connector把获得的请求发送给service的Engine处理,等待Engine响应
3.Engine获得请求localhost:8080/test/index.jsp,遍历所有的虚拟主机Host
4.Engine匹配名为localhost的的Host,如匹配不到交由该Engine中defaultHost处理
5.名为localhost的Host获得请求/test/index.jsp ,在Host所拥有的Context中匹配路径为/test的Context
6.path=/test的Context获得请求index.jsp,在它的mapping table中寻找URL PATTERN为*.jsp的servlet,最英语JspServlet类构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet和doPost方法
7,Context把执行完的HttpServletResponse对象返回给Host
8.Host把HttpServletResponse返回给Engine
9.Engine把HttpServletResponse返回给Connector
10.Connector把HttpServletResponse返回给浏览器
tomcat根目录结构
在tomcat中部署web应用与nginx和apache有相同之处也有不同
在nginx部署web应用
假定根目录为/data/nginx/html,将A应用解压缩所有文件放到/data/nginx/html/下,B应用文件放到data/nginx/html/B/,网站对应关系为:
http://localhost/ 对应于A的应用,即/data/nginx/html/
http://localhost/B/ 对应于B的应用,即/data/nginx/html/B/
在tomca中部署应用
Tomcat中网站根目录是$CATALINA_BASE/webapps/,但是在webapps目录中有个特殊的ROOT目录,它是网站的默认根目录,
将A解压后所有文件放到$CATALINA_BASE/webapps/ROOT中,B解压后所有文件放到$CATALINA_BASE/webapps/B中。$CATALINA_BASE/webapps下面的每个目录都对应一个Web应用,即WebApp,网站对应关系为:
http://localhost/ 对应于A的应用WebApp,即$CATALINA_BASE/webapps/ROOT/目录,
http://localhost/B/ 对应于B的应用WebApp,即$CATALINA_BASE/webapps/B/
JSP WebApp目录结构
$CATALINA_BASE/webapps下面的每个目录对应的WebApp,可能有以下子目录,但下面子目录是非必须的
主页配置:默认按以下顺序查找主页文件 index.html,index.htm、index.jsp
WEB-INF/:当前目录WebApp的私有资源路径,通常存储当前应用使用的web.xml和context.xml配置文件
META-INF/:类似于WEB-INF,也是私有资源的配置信息,和WEB-INF/目录一样浏览器无法访问
classes/:类文件,当前webapp需要的类
lib/:当前应用依赖的jar包
默认主页设置
通过修改 $CATALINA_BASE/conf/web.xml 中的下面
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
专用配置文件
将面主配置文件conf/web.xml中的
配置规则:
webApp的专有配置优先于系统的全局配置
修改系统的全局配置文件,需要重新启动服务生效
修改 webApp的专有配置,无需重启即可生效
应用部署
归档格式:
.war:WebApp打包,类zip格式文件,通常包括一个应用的所有资源,比如jsp,html,配置文件等
.jar:EJB类文件的打包压缩类zip格式文件,,包括很多的class文件, 网景公司发明
.rar:资源适配器类打包文件,目前已不常用
.ear:企业级WebApp打包,目前已不常用
传统应用开发测试后,通常打包为war格式,这种文件部署到Tomcat的webapps目录下,并默认会自动解包展开和部署上线。
部署方式:
部署Deploy:将webapp的源文件放置到目标目录,通过web.xml和context.xml文件中配置的路径就可以访问该webapp,通过类加载器加载其特有的类和依赖的类到JVM上,即:最终用户可以通过浏览器访问该应用
自动部署:Tomcat一旦发现多了一个web应用APP.war包,默认会自动把它解压缩,加载并启动起来
手动部署
冷部署:将webapp放到指定目录,才去启动Tomcat服务
热部署:Tomcat服务不停止,需要依赖manager、ant脚本、tcd(tomcat client deployer)等工具
反部署undeploy:停止webapp运行,并从JVM上清除已经加载的类,从Tomcat应用目录中移除部署的文件
启动start:是webapp能够访问
停止stop:webapp不能访问,不能提供服务,但是JVM并不清除它
手动部署
手动部署主目录webapp
将测试页放入ROOT下
[root@node1 webapps]# cat ROOT/index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>jsp例子</title>
</head>
<body>
后面的内容是服务器端动态生成字符串,最后拼接在一起
<%
out.println("hello jsp");
%>
<br>
<%=request.getRequestURL()%>
</body>
</html>
[root@node1 webapps]# tree ../work/
../work/
└── Catalina
└── localhost
├── docs
├── examples
├── host-manager
├── manager
├── ROOT
│ └── org
│ └── apache
│ └── jsp
│ ├── index_jsp.class
│ └── index_jsp.java
└── web1
[root@node1 webapps]# curl 10.0.0.18:8080 -I
HTTP/1.1 200
Set-Cookie: JSESSIONID=CB3D7988B2D41258E54ADCDA37A3BA91; Path=/; HttpOnly
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 29 Sep 2022 21:49:00 GMT
自动部署
将war包放入webapps下,war包会自动解压缩
创建war包
[root@node1 data]# jar -cvf /data/app2.war *
added manifest
adding: index.jsp(in = 346) (out= 297)(deflated 14%)
[root@node1 data]# ls
app2.war index.jsp
[root@node1 data]# cp app2.war /usr/local/src/tomcat/webapps/
[root@node1 data]# ls /usr/local/src/tomcat/webapps/
app2 app2.war docs examples host-manager manager ROOT web1
[root@node1 tomcat]# tree work/
work/
└── Catalina
└── localhost
├── app2
│ └── org
│ └── apache
│ └── jsp
│ ├── index_jsp.class
│ └── index_jsp.java
├── docs
├── examples
├── host-manager
├── manager
├── ROOT
│ └── org
│ └── apache
│ └── jsp
│ ├── index_jsp.class
│ └── index_jsp.java
└── web1
[root@node1 data]# curl 10.0.0.18:8080/app2/ -I
HTTP/1.1 200
Set-Cookie: JSESSIONID=2660E8150179A024659F008D8146FAD9; Path=/app2; HttpOnly
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 29 Sep 2022 21:55:52 GMT
#注:首次访问之后会在tomcat/work/下生成对应的.class .java文件,建议预访问
相关配置
端口8005/tcp安全配置管理
在conf/server.xml 有以下内容
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<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="web1_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s
%b" />
</Host>
</Engine>
</Service>
</Server>
8005是Tomcat的管理端口,默认监听在127.0.0.1上。无需验证就可发送SHUTDOWN (大小写敏感)这个字符串,tomcat接收到后就会关闭此Server。
此管理功能建议禁用,可将SHUTDOWN改为一串猜不出的字符串实现
或者port修改成 0, 会使用随机端口,如:36913
port设为-1等无效端口,将关闭此功能
此行不能被注释,否则无法启动tomcat服务
显示指定的http服务器版本信息
默认不显示tomcat的http的Server头信息, 可以指定tomcat的http的Server头信息为相应的值
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" Server="SOME STRING"/> #修改Server=""显示指定版本
[root@node1 conf]# curl 10.0.0.18:8080 -I
HTTP/1.1 200
Set-Cookie: JSESSIONID=788149FF9B38E63C3C9D823297A8C07C; Path=/; HttpOnly
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 29 Sep 2022 22:13:52 GMT
Server: tomcat
其他配置
service配置
一般情况下,一个Server实例配置一个Service,name属性相当于该Service的ID。
<Service name="Catalina">
连接器配置
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
redirectPort,如果访问HTTPS协议,自动转向这个连接器。但大多数时候,Tomcat并不会开启HTTPS,因为Tomcat往往部署在内部,HTTPS性能较差
引擎配置
<Engine name="Catalina" defaultHost="localhost">
defaultHost 配置
defaultHost指向内部定义某虚拟主机。缺省虚拟主机可以改动,默认localhost。
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
日志
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="web_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s
%b" />
多虚拟主机
- name 必须是主机名,用主机名来匹配
- appBase 当前主机的网页根目录,是相对于 $CATALINA_HOME ,也可以使用绝对路径
- unpackWARs 是否自动解压war格式
- autoDeploy 热部署,自动加载并运行应用
实现多虚拟主机
1.创建资源
[root@node1 conf]# mkdir /data/web{1,2}/ROOT -pv
[root@node1 conf]# echo "web1.magedu.org" >/data/web1/ROOT/index.html
[root@node1 conf]# echo "web2.magedu.org" >/data/web2/ROOT/index.html
2.修改配置
[root@node1 conf]# vim server.xml
<Host name="web1.magedu.org" appBase="/data/web1" unpackWARs="true" autoDeploy="true">
</Host>
<Host name="web2.magedu.org" appBase="/data/web2" unpackWARs="true" autoDeploy="true">
</Host>
修改端口80
[root@node1 conf]# vim server.xml
<Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" Server="tomcat"/>
[root@node1 conf]# vim /usr/lib/systemd/system/tomcat.service
#User=tomcat
#Group=tomcat
#注意: 因为以tomcat用户运行,不能直接使用1024以下的端口,需要修改tomcat的运行身份,否则会出现下面错误
Caused by: java.net.SocketException: Permission denied
Context组件
作用:
路径映射:将url映射至指定路径,而非使用appBase下的物理目录,实现虚拟目录功能
应用独立配置,例如单独配置应用日志、单独配置应用访问控制
#映射指定路径
<Context path="/test" docBase="/data/test" reloadable="true" />
#映射站点的根目录
<Context path="/" docBase="/data/website" reloadable="true" />
#还可以添加日志等独立的配置
<Context path="/test" docBase="/data/test" reloadable="true" >
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_test_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Context>
说明:
path:指的是访问的URL路径,如果path与appBase下面的子目录同名,context的docBase路径优先更高
docBase:可以是磁盘文件的绝对路径,也可以是相对路径(相对于Host的appBase)
reloadable:true表示如果WEB-INF/classes或META-INF/lib目录下.class文件有改动,就会将WEB应用重新加载。生产环境中,建议使用false来禁用。
valve(阀门)组件
定义访问日志
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
定义访问控制
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
deny="10\.0\.0\.\d+"/>
反向代理配合tomcat
nginx将所有请求转发到tomcat
1.创建资源
node1:10.0.0.18 node2:10.0.0.38 nginx:10.0.0.28
[root@node1 ~]# hostname -I > /usr/local/src/tomcat/webapps/ROOT/index.html
[root@node2 ~]# hostname -I > /usr/local/src/tomcat/webapps/ROOT/index.html
root@ubuntu:~# curl 10.0.0.18 -i
HTTP/1.1 200
Accept-Ranges: bytes
ETag: W/"11-1664531719000"
Last-Modified: Fri, 30 Sep 2022 09:55:19 GMT
Content-Type: text/html
Content-Length: 11
Date: Fri, 30 Sep 2022 10:36:05 GMT
Server: tomcat
10.0.0.18
root@ubuntu:~# curl 10.0.0.38 -i
HTTP/1.1 200
Accept-Ranges: bytes
ETag: W/"11-1664531395993"
Last-Modified: Fri, 30 Sep 2022 09:49:55 GMT
Content-Type: text/html
Content-Length: 11
Date: Fri, 30 Sep 2022 10:36:08 GMT
10.0.0.38
2.修改nginx配置
location / {
proxy_pass http://node1.magedu.org; #将所有请求转发到node1
#proxy_pass http://node2.magedu.org; #将所有请求转发到node2
}
测试访问:
root@ubuntu:~# curl 10.0.0.28 -i
HTTP/1.1 200
Server: nginx/1.18.0
Date: Fri, 30 Sep 2022 10:38:07 GMT
Content-Type: text/html
Content-Length: 11
Connection: keep-alive
Accept-Ranges: bytes
ETag: W/"11-1664531719000"
Last-Modified: Fri, 30 Sep 2022 09:55:19 GMT
10.0.0.18
配合nginx实现动静分离
1.准备资源
[root@node1 ~]# hostname -I >/usr/local/src/tomcat/webapps/ROOT/index.jsp #在node1上准备jsp文件
2.修改nginx配置
location ~* \.jsp$ {
proxy_pass http://node1.magedu.org;
}
location ~* \.html$ { #可将静态资源放到本地nginx
proxy_pass http://node2.magedu.org;
}
测试:
root@ubuntu:~# curl 10.0.0.28/index.jsp -i
HTTP/1.1 200
Server: nginx/1.18.0
Date: Fri, 30 Sep 2022 10:41:58 GMT
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 11
Connection: keep-alive
Set-Cookie: JSESSIONID=A842D023C669FA4CE602C03FFA0D66A0; Path=/; HttpOnly
10.0.0.18
root@ubuntu:~# curl 10.0.0.28/index.html -i
HTTP/1.1 200
Server: nginx/1.18.0
Date: Fri, 30 Sep 2022 10:42:03 GMT
Content-Type: text/html
Content-Length: 11
Connection: keep-alive
Accept-Ranges: bytes
ETag: W/"11-1664531395993"
Last-Modified: Fri, 30 Sep 2022 09:49:55 GMT
10.0.0.38
httpd实现tomcat反向代理
[root@node1 conf.d]# vim tomcat.conf
<VirtualHost *:80>
servername httpd.magedu.org
ProxyRequests off
Proxypass / http://node1.magedu.org/
Proxypassreverse / http://node1.magedu.org/
ProxyPreserveHost on
proxyvia on
</VirtualHost>
测试:
root@ubuntu:~# curl 10.0.0.28 -I
HTTP/1.1 200
Date: Fri, 30 Sep 2022 10:58:52 GMT
Server: tomcat
Accept-Ranges: bytes
ETag: W/"11-1664531719000"
Last-Modified: Fri, 30 Sep 2022 09:55:19 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 11
Via: 1.1 httpd.magedu.org
ProxyRequests:Off 关闭正向代理功能,即启动反向代理
ProxyPass:反向代理指令,指向后端服务器
ProxyPassReverse:当反向代理时,返回给客户端的报文需将之重写个别后端主机的response头, 如:Location,Content-Location,URI
ProxyPreserveHost:On时让反向代理保留原请求的Host首部转发给后端服务器,off 时则删除host首部后再转发至后面端服务器, 这将导致只能转发到后端的默认虚拟主机
ProxyVia:On开启。反向代理的响应报文中提供一个response的via首部,默认值off
httpd实现AJP反向代理
1.tomcat开启AJP协议
<Connector protocol="AJP/1.3" address="0.0.0.0" port="8009" redirectPort="8443" secretRequired=""/>
2.修改httpd配置
<VirtualHost *:80>
servername httpd.magedu.org
ProxyRequests off
Proxypass / ajp://node1.magedu.org/
ProxyPreserveHost on
proxyvia on
</VirtualHost>
测试:
root@ubuntu:~# curl 10.0.0.28 -i
HTTP/1.1 200 200
Date: Fri, 30 Sep 2022 11:13:57 GMT
Server: Apache/2.4.37 (rocky)
Accept-Ranges: bytes
ETag: W/"11-1664531719000"
Last-Modified: Fri, 30 Sep 2022 09:55:19 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 11
10.0.0.18
#注:除httpd外,其它支持AJP代理的服务器非常少,比如Nginx就不支持AJP,所以目前一般都禁用AJP协议端口
删除配置: <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
tomcat负载均衡
http的无状态,有连接和短连接
无状态:服务器无法判断两次请求之间的关系。后来可以通过cookie、session机制来判断。
有连接:基于TCP协议,是面向连接的,需要3次握手、4次断开。
短连接:HTTP 1.1之前每个请求一个连接,资源浪费。HTTP 1.1之后默认开启keep-alive
会话保持的方式:
session sticky 会话黏性
session绑定:source ip ,cookie
优点:简单易配置
缺点:如果目标服务器故障后,如果没有做sessoin持久化,就会丢失session,此方式生产很少使用
session集群复制
Tomcat自己的提供的多播集群,通过多播将任何一台的session同步到其它节点。
缺点
Tomcat的同步节点不宜过多,互相即时通信同步session需要太多带宽
每一台都拥有全部session,内存损耗太多
session server
session 共享服务器,使用memcached、redis做共享的Session服务器,此为推荐方式
nginx实现后端负载
修改配置
[root@node1 conf.d]# vim /apps/nginx/conf/nginx.conf
upstream tomcat-server {
server node1.magedu.org;
server node2.magedu.org;
}
server {
location / {
proxy_pass http://tomcat-server;
}
测试:
root@ubuntu:~# curl 10.0.0.28/index.jsp -I
HTTP/1.1 200
Server: nginx/1.18.0
Date: Fri, 30 Sep 2022 11:56:53 GMT
Content-Type: text/html;charset=UTF-8
Connection: keep-alive
Set-Cookie: JSESSIONID=64BA17BA27DD5FC108E63B07B652265A; Path=/; HttpOnly
root@ubuntu:~# curl 10.0.0.28/index.jsp -I
HTTP/1.1 200
Server: nginx/1.18.0
Date: Fri, 30 Sep 2022 11:56:54 GMT
Content-Type: text/html;charset=ISO-8859-1
Connection: keep-alive
Set-Cookie: JSESSIONID=26FC396E89666F83B1198B0A1603CF08; Path=/; HttpOnly
Memcached
编译安装memcache
安装依赖
[root@node1 ~]# yum -y install gcc libevent-devel
[root@node1 ~]# tar -xvf memcached-1.6.9.tar.gz -C /usr/local/src/
[root@node1 ~]# ln -s /usr/local/src/memcached-1.6.9 /usr/local/src/memcached
[root@node1 ~]# cd /usr/local/src/memcached
[root@node1 memcached]# ./configure --prefix=/apps/memcached
[root@node1 memcached]# make && make install
[root@node1 memcached]# echo 'PATH=/apps/memcached/bin:$PATH' > /etc/profile.d/memcached.sh
[root@node1 memcached]# . /etc/profile.d/memcached.sh
[root@node1 memcached]# useradd -r -s /sbin/nologin memcached
[root@node1 ~]# vim /etc/sysconfig/memcached
PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="64"
#OPTIONS="-l 127.0.0.1,::1"
OPTIONS=""
[root@centos7 ~]#cat /lib/systemd/system/memcached.service
[Unit]
Description=memcached daemon
Before=httpd.service
After=network.target
[Service]
EnvironmentFile=/etc/sysconfig/memcached
ExecStart=/apps/memcached/bin/memcached -p ${PORT} -u ${USER} -m ${CACHESIZE} -c${MAXCONN} $OPTIONS
[Install]
WantedBy=multi-user.target
[root@centos7 ~]#systemctl daemon-reload
[root@node1 memcached]# memcached --version
memcached 1.6.9
memcached启动程序
修改memcached 运行参数,可以使用下面的选项修改/etc/sysconfig/memcached文件
常见选项:
-u username memcached运行的用户身份,必须普通用户
-p 绑定的端口,默认11211
-m num 最大内存,单位MB,默认64MB
-c num 最大连接数,缺省1024
-d 守护进程方式运行
-f 增长因子Growth Factor,默认1.25
-v 详细信息,-vv能看到详细信息
-M 使用内存直到耗尽,不许LRU
-U 设置UDP监听端口,0表示禁用UDP
memcached开发库和工具
与memcached通信的不同语言的连接器。libmemcached提供了C库和命令行工具。
#测试memcached是否可访问
[root@node1 ~]# memping --servers=10.0.0.28
[root@node1 ~]# echo $?
0
[root@node1 ~]# memping --servers=10.0.0.28
#查看memcached状态
[root@node1 ~]# memstat --servers=10.0.0.28
memcached命令
[root@node1 memcached]# cat share/man/man1/memcached.1 #帮助文档
command <key> <flags> <expiration time> <bytes>
<value>
#参数说明如下:
command set/add/replace
key key 用于查找缓存值
flags 可以包括键值对的整型参数,客户机使用它存储关于键值对的额外信息
expiration time 在缓存中保存键值对的时间长度(以秒为单位,0 表示永远)
bytes 在缓存中存储的字节数
value 存储的值(始终位于第二行)
#增加key,过期时间为秒,bytes为存储数据的字节数
add key flags exptime bytes
get key #查
delete key #删
flush_all #清空
session共享服务器
msm:msm(memcached session manager)提供将Tomcat的session保持到memcached或redis的程序,可以实现高可用。
准备jar包
kryo-3.0.3.jar
asm-5.2.jar
objenesis-2.6.jar
reflectasm-1.11.9.jar
minlog-1.3.1.jar
kryo-serializers-0.45.jar
msm-kryo-serializer-2.3.2.jar
memcached-session-manager-tc8-2.3.2.jar
spymemcached-2.12.3.jar
memcached-session-manager-2.3.2.jar
jedis.jar
memcached
修改conf/context.xml,添加以下行
sticky:
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:10.0.0.18:11211,n2:10.0.0.38:11211"
failoverNodes="n1"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
no-sticky:
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:10.0.0.18:11211,n2:10.0.0.38:11211"
sticky="false"
sessionBackupAsync="false"
lockingMode="uriPattern:/path1|/path2"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
redis
修改conf/context.xml,添加以下行
no-sticky:
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="redis://10.0.0.18:6379" # redis://:password@10.0.0.18:6379 redis设置密码时
sticky="false"
sessionBackupAsync="false"
lockingMode="uriPattern:/path1|/path2"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
tomcat优化
常用优化配置
内存空间优化
JAVA_OPTS="-server -Xms4g -Xmx4g -XX:NewSize= -XX:MaxNewSize= "
-server:服务器模式
-Xms:堆内存初始化大小
-Xmx:堆内存空间上限
-XX:NewSize=:新生代空间初始化大小
-XX:MaxNewSize=:新生代空间最大值
案例L
[root@centos8 ~]#vim /usr/local/tomcat/bin/catalina.sh
JAVA_OPTS="-server -Xms4g -Xmx4g -Xss512k -Xmn1g -
XX:CMSInitiatingOccupancyFraction=65 -XX:+AggressiveOpts -XX:+UseBiasedLocking -
XX:+DisableExplicitGC -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -
XX:PermSize=128m -XX:MaxPermSize=512m -XX:CMSFullGCsBeforeCompaction=5 -
XX:+ExplicitGCInvokesConcurrent -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -
XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -
XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods"
#一台tomcat服务器并发连接数不高,生产建议分配物理内存通常4G到8G较多,如果需要更多连接,一般会利用虚拟化技术实现多台tomcat
线程池优化
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
常用属性:
connectionTimeout :连接超时时长,单位ms
maxThreads:最大线程数,默认200
minSpareThreads:最小空闲线程数
maxSpareThreads:最大空闲线程数
acceptCount:当启动线程满了之后,等待队列的最大长度,默认100
URIEncoding:URI 地址编码格式,建议使用 UTF-8
enableLookups:是否启用客户端主机名的DNS反向解析,缺省禁用,建议禁用,就使用客户端IP就行
compression:是否启用传输压缩机制,建议 "on",CPU和流量的平衡
compressionMinSize:启用压缩传输的数据流最小值,单位是字节
compressableMimeType:定义启用压缩功能的MIME类型text/html, text/xml, text/css, text/javascript
案例:
总结tomcat的核心组件以及根目录结构
顶级组件
Server,代表整个Tomcat容器,一台主机可以启动多tomcat实例,需要确保端口不要产生冲突
服务类组件
Service,实现组织Engine和Connector,建立两者之间关联关系, service 里面只能包含一个Engine
连接器组件
Connector,有HTTP(默认端口8080/tcp)、HTTPS(默认端口8443/tcp)、AJP(默认端口
8009/tcp)协议的连接器,AJP(Apache Jserv protocol)是一种基于TCP的二进制通讯协议。
容器类
Engine、Host(虚拟主机)、Context(上下文件,解决路径映射)都是容器类组件,可以嵌入其它组件,
内部配置如何运行应用程序。
内嵌类
可以内嵌到其他组件内,valve、logger、realm、loader、manager等。以logger举例,在不同容器组
件内分别定义。
集群类组件
listener、cluster
Server 服务器,Tomcat 运行的进程实例,一个Server中可以有多个service,但通常就一个
Service 服务,用来组织Engine和Connector的对应关系,一个service中只有一个Engine
Connector 连接器,负责客户端的HTTP、HTTPS、AJP等协议连接。一个Connector只属于某一个Engine
Engine 即引擎,用来响应并处理用户请求。一个Engine上可以绑定多个Connector
Host 即虚拟主机,可以实现多虚拟主机,例如使用不同的主机头区分
Context 应用的上下文,配置特定url路径映射和目录的映射关系:url => directory
根目录结构:
/usr/local/src/tomcat/webapps/
├── app2
├── docs
├── examples
├── host-manager
├── manager
├── ROOT
└── web1
webapps下每个目录为一个应用,特殊目录ROOT为应用默认根目录
WEB-INF/:当前目录WebApp的私有资源路径,通常存储当前应用使用的web.xml和context.xml配置文件
META-INF/:类似于WEB-INF,也是私有资源的配置信息,和WEB-INF/目录一样浏览器无法访问
classes/:类文件,当前webapp需要的类
lib/:当前应用依赖的jar包
tomcat实现多虚拟主机
实现tomcat多虚拟主机
1.修改server.xml配置
[root@node1 tomcat]# vim conf/server.xml
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Service name="Catalina">
<Connector port="80" 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="web1.magedu.org" appBase="/data/web1" unpackWARs="true" autoDeploy="true">
</Host>
<Host name="web2.magedu.org" appBase="/data/web2" unpackWARs="true" autoDeploy="true">
</Host>
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" />
</Engine>
</Service>
</Server>
2.准备资源
[root@node1 tomcat]# tree /data/
/data/
├── web1
│ └── ROOT
│ └── index.html
└── web2
└── ROOT
└── index.html
测试
root@ubuntu:~# curl web1.magedu.org -I
HTTP/1.1 200
Accept-Ranges: bytes
ETag: W/"16-1664490661000"
Last-Modified: Thu, 29 Sep 2022 22:31:01 GMT
Content-Type: text/html
Content-Length: 16
Date: Sat, 01 Oct 2022 04:43:38 GMT
root@ubuntu:~# curl web2.magedu.org -I
HTTP/1.1 200
Accept-Ranges: bytes
ETag: W/"16-1664490819000"
Last-Modified: Thu, 29 Sep 2022 22:33:39 GMT
Content-Type: text/html
Content-Length: 16
Date: Sat, 01 Oct 2022 04:43:42 GMT
nginx实现后端tomcat的负载均衡调度
node1:10.0.0.18 node2:10.0.0.38 nginx:10.0.0.28
修改nginx配置,默认轮询方式
[root@nginx ~]# vim /apps/nginx/conf/nginx.conf
upstream tomcat-server {
#ip_hash; #源地址哈希
#hash $cookie_JSESSIONID; #对session哈希,配合session共享
server node1.magedu.org;
server node2.magedu.org;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://tomcat-server;
}
session共享设置 redis
修改context.xml文件,添加以下内容(no-sticky:)
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="redis://10.0.0.18:6379"
sticky="false"
sessionBackupAsync="false"
lockingMode="uriPattern:/path1|/path2"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
准备jar文件
jedis.jar
root@ubuntu:~# curl 10.0.0.28
10.0.0.18
root@ubuntu:~# curl 10.0.0.28
10.0.0.38
简述memcached的工作原理
Memcached采用了Slab Allocator机制来分配、管理内存。
page:分配给slab的内存空间,slab分配后按固定字节大小等分为chunk,page默认1M,
Chunk:用于缓存记录k/v值的内存空间。
Slab Class:Slab按照Chunk的大小分组,就组成不同的Slab Class,Slab之间的差异可以使用Growth Factor 控制,默认1.25。
懒过期Lazy Expiration
memcached不会监视数据是否过期,而是在取数据时才看是否过期,如果过期,把数据有效期限标识为0,并不清除该数据。以后可以覆盖该位置存储其它数据。
LRU
当内存不足时,memcached会使用LRU(Least Recently Used)机制来查找可用空间,分配给新记录使用。
集群
Memcached集群,称为基于客户端的分布式集群,即由客户端实现集群功能,即Memcached本身不支持集群
Memcached集群内部并不互相通信,一切都需要客户端连接到Memcached服务器后自行组织这些节点,并决定数据存储的节点。
总结tomcat优化方法
内存空间优化
JAVA_OPTS="-server -Xms4g -Xmx4g -XX:NewSize= -XX:MaxNewSize= "
-server:服务器模式
-Xms:堆内存初始化大小
-Xmx:堆内存空间上限
-XX:NewSize=:新生代空间初始化大小
-XX:MaxNewSize=:新生代空间最大值
线程池优化
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
常用属性:
connectionTimeout :连接超时时长,单位ms
maxThreads:最大线程数,默认200
minSpareThreads:最小空闲线程数
maxSpareThreads:最大空闲线程数
acceptCount:当启动线程满了之后,等待队列的最大长度,默认100
URIEncoding:URI 地址编码格式,建议使用 UTF-8
enableLookups:是否启用客户端主机名的DNS反向解析,缺省禁用,建议禁用,就使用客户端IP就行
compression:是否启用传输压缩机制,建议 "on",CPU和流量的平衡
compressionMinSize:启用压缩传输的数据流最小值,单位是字节
compressableMimeType:定义启用压缩功能的MIME类型text/html, text/xml, text/css,
text/javascript
JVM环境调优
垃圾回收算法
标记清除 Mark-Sweep
分垃圾标记阶段和内存释放阶段。标记阶段,找到所有可访问对象打个标记。清理阶段,遍历整个堆,对未标记对象(即不再使用的对象)清理。
标记-清除最大的问题会造成**内存碎片**,但是效率很高,不浪费空间
标记压缩 (压实)Mark-Compact
分垃圾标记阶段和内存整理阶段。标记阶段,找到所有可访问对象打个标记。内存清理阶段时,整理时将对象向内存一端移动,整理后存活对象连续的集中在内存一端。
标记-压缩算法好处是整理后内存空间连续分配,有大段的连续内存可分配,没有内存碎片。
缺点是内存整理过程有消耗,效率相对低下
复制 Copying
先将可用内存分为大小相同两块区域A和B,每次只用其中一块,比如A。当A用完后,则将A中存活的对象复制到B。复制到B的时候连续的使用内存,最后将A一次性清除干净。
缺点是比较**浪费内存**,只能使用原来一半内存,因为内存对半划分了,复制过程毕竟也是有代价。
好处是没有碎片,复制过程中保证对象使用连续空间
没有最好的算法,在不同场景选择最合适的算法
效率: 复制算法>标记清除算法> 标记压缩算法
内存整齐度: 复制算法=标记压缩算法> 标记清除算法
内存利用率: 标记压缩算法=标记清除算法>复制算法
分代堆内存GC策略
Heap堆内存分为
年轻代Young:Young Generation
伊甸园区eden: 只有一个,刚刚创建的对象
幸存(存活)区Servivor Space:有2个幸存区,一个是from区,一个是to区。大小相等、地位相同、可互换。
from 指的是本次复制数据的源区
to 指的是本次复制数据的目标区
老年代Tenured:Old Generation, 长时间存活的对象
垃圾收集方式
按工作模式不同:指的是GC线程和工作线程是否一起运行
独占垃圾回收器:只有GC在工作,STW 一直进行到回收完毕,工作线程才能继续执行
并发垃圾回收器:让GC线程垃圾回收某些阶段可以和工作线程一起进行,如:标记阶段并行,回收阶段仍然串行
按回收线程数:指的是GC线程是否串行或并行执行
串行垃圾回收器:一个GC线程完成回收工作
并行垃圾回收器:多个GC线程同时一起完成回收工作,充分利用CPU资源
优化调整Java 相关参数的目标: 尽量减少FullGC和STW
通过以下选项可以单独指定新生代、老年代的垃圾收集器
-server 指定为Server模式,也是默认值,一般使用此工作模式
-XX:+UseSerialGC
运行在Client模式下,新生代是Serial, 老年代使用SerialOld
-XX:+UseParNewGC
新生代使用ParNew,老年代使用SerialOld
-XX:+UseParallelGC
运行于server模式下,新生代使用Serial Scavenge, 老年代使用SerialOld
-XX:+UseParallelOldGC
新生代使用Paralell Scavenge, 老年代使用Paralell Old
-XX:ParallelGCThreads=N,在关注吞吐量的场景使用此选项增加并行线程数
-XX:+UseConcMarkSweepGC
新生代使用ParNew, 老年代优先使用CMS,备选方式为Serial Old
响应时间要短,停顿短使用这个垃圾收集器
-XX:CMSInitiatingOccupancyFraction=N,N为0-100整数表示达到老年代的大小的百分比多少触发回收
默认68
-XX:+UseCMSCompactAtFullCollection 开启此值,在CMS收集后,进行内存碎片整理
-XX:CMSFullGCsBeforeCompaction=N 设定多少次CMS后,进行一次内存碎片整理
-XX:+CMSParallelRemarkEnabled 降低标记停顿
java程序出现oom如何解决?什么场景下会出现oom?
oom:OutOfMemory 内存溢出
在堆内存不足,程序设计不合理,未设置垃圾回收或垃圾回收设置不合理时会出现OOM
解决办法:
调整堆内存
合理设计程序
设置合理的垃圾回收方式
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律