Tomcat

发布时间 2023-04-07 16:54:06作者: ——浮生——

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中的 标签 内容,复制到/usr/local/tomcat/webapps/ROOT/WEB-INF/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 &quot;%r&quot; %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 &quot;%r&quot; %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 &quot;%r&quot; %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 &quot;%r&quot; %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 &quot;%r&quot; %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
解决办法:
	调整堆内存
	合理设计程序
	设置合理的垃圾回收方式