JAVA web容器之Tomcat

一、Tomcat概述

1.1 什么是Tomcat?

image-20211115204827687

官方网站:http://tomcat.apache.org/

Tomcat 是 Apache 软件基金会(Apache Software Foundation)的 Jakarta 项目中的一个核心项目,由 Apache、Sun 和其他一些公司及个人共同开发而成。由于有了 Sun 的参与和支持,最新的 Servlet 和JSP 规范总是能在 Tomcat 中得到体现,Tomcat 5 支持最新的 Servlet 2.4 和 JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受 Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的 Web 应用服务器。
Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试 JSP 程序的首选。 Tomcat 和 IIS 等 Web 服务器一样,具有处理 HTML 页面的功能。不过,Tomcat 处理静态 HTML 的能力不如 Apache 服务器。
1.1.1 什么是jsp?
    JSP:全名为Java Server Pages,中文名叫java服务器页面, 是由Sun Microsystems公司倡导、许多公司参与一起建立的一种动态网页技术标准。JSP技术有点类似ASP技术,它是在传统的网页HTML(标准通用标记语言的子集)文件(*.htm,*.html)中插入Java程序段(Scriptlet)和JSP标记(tag),从而形成JSP文件,后缀名为(*.jsp)。 用JSP开发的Web应用是跨平台的,既能在Linux下运行,也能在其他操作系统上运行。
1.1.2 支持jsp的应用服务?
支持jsp,免费的开源版本:jboss,jetty

支持 JSP 网站,收费版 web 服务器:oracle 的 weblogic ; IBM 的 websphere

WebLogic 是美国 Oracle 公司出品的一个 application server 确切的说是一个基于 JAVAEE 架构的中间件,WebLogic 是用于开发、集成、部署和管理大型分布式 Web 应用、网络应用和数据库应用的 Java应用服务器。将 Java 的动态功能和 Java Enterprise 标准的安全性引入大型网络应用的开发、集成、部署和管理之中。

WebSphere 是 IBM 的软件平台。它是 Web 应用程序和跨平台、跨产品解决方案所需要的整个中间件基础设施。WebSphere 提供了可靠、灵活和健壮的软件运行服务。

1.2 Tomcat的运行原理

image-20221106195302512

Tomcat 中最顶层的容器是Server,一个Server包含多个Service,一个Service只能有一个Container ,但可以有多个Connector。

Service主要用来提供对外服务,包含两个部分:Connector 连接器和Container 容器。

1、 Connector 连接器用于处理连接相关的事情,并提供Socket与请求、响应之间的转换;
2、 Container 容器用于封装和管理Servlet,以及具体处理Request请求;

coyote是tomcat的connector框架的名字,简单说就是coyote来处理底层的socket,并格http请求、响应等字节流层 面的东西,包装成Request和Response两个类(这两个类是tomcat定义的,而非servlet中的ServletRequest和ServletResponse),供容器使用

Coyote 中支持多种应用层协议和I/O模型。

IO 模型描述
BIO阻塞IO模型
NIO非阻塞同步I/O,采用Java NIO类库实现。
NIO2非阻塞异步I/O,采用JDK 7最新的NIO2类库实现。
APR采用Apache可移植运行库实现,是C/C++编写的本地库。需要单独安装APR库。
应用层协议描述
HTTP/1.1常见的Web访问协议
AJP用于和Web服务器集成(如Apache),以实现对静态资源的优化以及集群部署,当前支持AJP/1.3。
HTTP/2HTTP 2.0 下一代HTTP协议

连接器组件如下:

image-20230525164018371解析:

1、EndPoint 监听点
 Coyote 通信端点,即通信监听的接口,是具体Socket接收和发送处理器,用来实现TCP/IP协议。
2、Processor 处理接口
 Processor接收来自EndPoint的Socket,读取字节流解析为Tomcat  Request和Response对象,实现HTTP协议。
3、ProtocolHandler 协议处理器
 通过Endpoint 和 Processor,实现针对具体协议的处理能力。
4、Adapter 适配器
 将ProtocolHandler 协议处理器解析出的Tomcat Request对象,适配成ServletRequest,再调ServletRequest的Service方法。

Tomcat的工作原理:

1、用户点击网页内容,请求被发送到本机端口8080,被在那里监听的coyote HTTP/1.1 Connector获得。

2、Connector把该请求交给它所在的Service的Engine来处理,并等待Engine的回应。

3、 Engine获得请求localhost/test/index.jsp,匹配所有的虚拟主机Host

4、 Engine匹配到名为localhost的Host(即使匹配不到也把清求交给该Host处理,因为该Host被定义为该Engine的默认主机),名为locathost的Host获得请求/test/indeX.jsp, 匹配它所拥有的所有的context。 Host匹配到路径为/test的context(如果匹配不到就把该请求交给路径名为*的context去处理)。

5、 path=“/test”的context获得请求/index.jsp, 在它的mapping table中寻找出对应的Servlet。 Context匹配到URL PATTERN为*jsp的servlet,对应于JspServlet类。

6、构造HtpServletRequest对象和HttpServletResponse对象,作为参数调用Jspservlet的doGet(或dopost()执行业务逻辑、数据存储等程序。

7、Context把执行完之后的HttpServletResponse对象返回给Host。

8、Host把HttpservletResponse对象返回给Engine。

9、 Engine把HttpServletResponse对象返回Connector。

10、 Connector把把HttpservletResponse对象返回给客户Browser。

1.3 tomcat的组成结构

image-20211115210210763

Server:指的就是整个 Tomcat 服 务器,包含多组服务,负责管理和 启动各个 Service,同时监听 8005 端口发过来的 shutdown 命令,用 于关闭整个容器 ;

Service:Tomcat 封装的、对外提 供完整的、基于组件的 web 服务, 包含 Connectors、Container 两个 核心组件,以及多个功能组件,各 个 Service 之间是独立的,但是共享 同一 JVM 的资源 ;

Connector:Tomcat 与外部世界的连接器,监听固定端口接收外部请求,传递给 Container,并 将 Container 处理的结果返回给外部;

Container:Catalina,Servlet 容器,内部有多层容器组成,用于管理 Servlet 生命周期,调用 servlet 相关方法。
 
Realm:Tomcat 中为 web 应用程序提供访问认证和角色管理的机制;

JMX:Java SE 中定义技术规范,是一个为应用程序、设备、系统等植入管理功能的框架,通过 JMX 可以远程监控 Tomcat 的运行状态;

Jasper:Tomcat 的 Jsp 解析引擎,用于将 Jsp 转换成 Java 文件,并编译成 class 文件。

Session:负责管理和创建 session,以及 Session 的持久化(可自定义),支持 session 的集群。

Pipeline:在容器中充当管道的作用,管道中可以设置各种 valve(阀门),请求和响应在经由管 道中各个阀门处理,提供了一种灵活可配置的处理请求和响应的机制。

Container组成

image-20211115211005211

 1、Engine
    Catalina的Servlet引擎,用来管理多个虚拟的主机,每个Service一个Engine,但是可以包含多个虚拟主机Host。
 2、Host:
    虚拟主机,负责 web 应用的部 署和 Context 的创建;
 3、Context
   Web 应用上下文,包含多个 Wrapper,负责 web 配置的解析、管 理所有的 Web 资源;
 4、Wrapper
   最底层的容器,是对 Servlet 的封装,负责 Servlet 实例的创 建、执行和销毁。

1.4 Tomcat工作模式

Tomcat采用的是B/S模式,默认服务端口是8080

C/S ,Client/Server,就是客户端与服务器,顾名思义,用户需要安装相应的客户端才能运行,典型的如QQ,微信。
优点:减轻服务端的处理压力,运算速度快,对客户端响应快
缺点:版本升级成本高,每次升级必须先卸载老版本在升级


B/S,Browser/Server,浏览器/服务器,本质上来说也是cs架构,只不过client变成了browser而已。
优点:不存在客户端升级问题,只需要升级服务端程序即可。
缺点:具体业务的处理压力,全部加载了服务端,导致服务端程序运算速度降低,响应速度降低,用户体验差。

1.5 Tomcat部署方式

1)源码部署(安装apache Ant)

2)二进制部署

3)yum方式部署(目前是7版本,默认安装没有tomcat的引导页)

1.6 Tomcat的优势

1、开源免费:无需承担更高的采购成本

2、部署简单:直接解压,经过很简单的配置就可以使用

3、扩展性好,可以在很短的时间内部署多台服务器,部署速度快,随时应对高并发的场景

4、资源占用少:Tomcat是一款轻量级别的java web服务,运行占用资源少。是目前最为流行的web服务器之一

1.7 Tomcat运行模式

tomcat的运行模式有3种:

BIO模式: 阻塞式I/O操作,表示Tomcat使用的是传统Java I/O操作(即:java.io包及其子包);Tomcat 7以下版本默认情况下是以BIO模式运行的,由于每个请求的都要创建一个线程来处理,因此 线程的开销较大,不能处理高兵的场景,在三种模式中性能也最低效

NIO/2模式: 是Java SE 1.4以后续版本提供的一种新的I/O操作方式(即:java.nio包及其子包);是一个基于 缓存区、并提供非阻塞I/O操作的Java API,它拥有比传统的I/O操作(BIO)更好的并发运行性能;

APR模式: 简单理解就是,从操作系统级别解决异步IO问题,大幅度的提高服务器的处理合相应性能,也是Tomcat运行高并发应用的首选模式;

对于每种协议,Tomcat都提供了对应的I/O方式的实现,而且Tomcat官方还提供了在每种协议下每种I/O实现方案的差异, HTTP协议下的处理方式如下表,详情可查看Tomcat官网说明(https://tomcat.apache.org/tomcat-8.5-doc/config/http.html)

image-20230526110838751

推荐使用nio,在tomcat8中有最新的nio2,速度更快,建议使用nio2

设置nio2:

<Connectorexecutor="tomcatThreadPool"port="8080"protocol="org.apache.coyote.http11.Http11Nio2Protocol"connectionTimeout="20000"redirectPort="8443" />

二、部署Tomcat

2.1 安装JDK

tomcat本身是使用的java语言编写的,因此需要提前安装jdk的环境

[root@localhost ~]# tar zxvf jdk-8u311-linux-x64.tar.gz -C /usr/local/src/
jdk1.8.0_311/man/ja_JP.UTF-8/man1/schemagen.1
jdk1.8.0_311/man/ja_JP.UTF-8/man1/serialver.1
jdk1.8.0_311/man/ja_JP.UTF-8/man1/wsgen.1
jdk1.8.0_311/man/ja_JP.UTF-8/man1/wsimport.1
jdk1.8.0_311/man/ja_JP.UTF-8/man1/xjc.1
jdk1.8.0_311/man/ja_JP.UTF-8/man1/jvisualvm.1
jdk1.8.0_311/man/ja_JP.UTF-8/man1/javafxpackager.1
jdk1.8.0_311/man/ja_JP.UTF-8/man1/javapackager.1
jdk1.8.0_311/man/ja
jdk1.8.0_311/release
jdk1.8.0_311/src.zip
jdk1.8.0_311/THIRDPARTYLICENSEREADME-JAVAFX.txt
jdk1.8.0_311/javafx-src.zip
jdk1.8.0_311/jmc.txt

2.2 添加jdk的环境变量

vim  /etc/profile    #在文件结尾添加如下几行:

JAVA_HOME=/usr/local/src/jdk1.8.0_311
JAVA_BIN=$JAVA_HOME/bin
JRE_HOME=$JAVA_HOME/jre
JRE_BIN=$JRE_HOME/bin
PATH=$JAVA_BIN:$JRE_BIN:$PATH
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
export JAVA_HOME JRE_HOME PATH CLASSPATH
[root@localhost ~]# source /etc/profile
[root@localhost ~]# java -version
java version "1.8.0_311"
Java(TM) SE Runtime Environment (build 1.8.0_311-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.311-b11, mixed mode)
[root@localhost ~]# 

注意:安装之前,如果系统中安装了openjdk,请提前将openjdk卸载

2.3 安装tomcat

[root@localhost ~]# tar zxvf apache-tomcat-8.5.72.tar.gz -C /usr/local/
[root@localhost local]# ln -s apache-tomcat-8.5.72 tomcat-8080

注意:可以将安装目录别名或者做软链接

2.4 启动Tomcat测试访问

[root@localhost local]# cd tomcat-8080/
[root@localhost tomcat-8080]# ls
bin  BUILDING.txt  conf  CONTRIBUTING.md  lib  LICENSE  logs  NOTICE  README.md  RELEASE-NOTES  RUNNING.txt  temp  webapps  work
[root@localhost tomcat-8080]# cd bin/
[root@localhost bin]# ls
bootstrap.jar  catalina-tasks.xml  commons-daemon.jar            configtest.sh  digest.sh         shutdown.bat  startup.sh            tool-wrapper.bat  version.sh
catalina.bat   ciphers.bat         commons-daemon-native.tar.gz  daemon.sh      setclasspath.bat  shutdown.sh   tomcat-juli.jar       tool-wrapper.sh
catalina.sh    ciphers.sh          configtest.bat                digest.bat     setclasspath.sh   startup.bat   tomcat-native.tar.gz  version.bat
[root@localhost bin]# ./startup.sh       #启动tomcat
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_311/jre
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:   
Tomcat started.    
[root@localhost bin]# netstat -antup |grep 8080
tcp6       0      0 :::8080                 :::*                    LISTEN      11914/java        
[root@localhost bin]# 
访问测试
http://ip:8080

image-20211115214054128

到此,Tomcat就部署完成了。

查看启动日志,检查是否启动过程有异常

[root@localhost tomcat-8080]# cd /usr/local/tomcat-8080/logs
[root@localhost logs]# ls
catalina.2021-11-15.log  catalina.out  host-manager.2021-11-15.log  localhost.2021-11-15.log  localhost_access_log.2021-11-15.txt  manager.2021-11-15.log
[root@localhost logs]# tailf catalina.out    #查看日志
15-Nov-2021 21:38:58.542 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat-8080/webapps/docs]
15-Nov-2021 21:38:58.554 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat-8080/webapps/docs] has finished in [12] ms
15-Nov-2021 21:38:58.555 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat-8080/webapps/examples]
15-Nov-2021 21:38:58.682 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat-8080/webapps/examples] has finished in [126] ms
15-Nov-2021 21:38:58.682 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat-8080/webapps/host-manager]
15-Nov-2021 21:38:58.696 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat-8080/webapps/host-manager] has finished in [14] ms
15-Nov-2021 21:38:58.696 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat-8080/webapps/manager]
15-Nov-2021 21:38:58.709 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat-8080/webapps/manager] has finished in [12] ms
15-Nov-2021 21:38:58.710 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
15-Nov-2021 21:38:58.715 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 353 ms

2.5 tomcat的目录结构

bin:存放启动和关闭tomcat脚本
conf:存放不同的配置文件(server.xml和web.xml,context.xml);
doc:存放Tomcat文档;
lib:存放Tomcat运行需要的库文件(JARS);
logs:存放Tomcat执行时的LOG文件;
src:存放Tomcat的源代码;
work:存放jsp编译后产生的class文件;
webapps:Tomcat的主要Web发布目录(包括应用程序示例);

项目管理目录

docs :tomcat的文档
examples:自带的一些案例
host-manager :tomcat的主机管理程序目录
manager:tomcat的管理应用程序目录
ROOT:网站的默认根目录,类似http:/var/www/html

2.6 制定tomcat启动脚本

#!/bin/bash
# description: Tomcat8 Start Stop Restart
# processname: tomcat8
# chkconfig: 2345 20 80
CATALINA_HOME=/usr/local/tomcat-8080

case $1 in
        start)
                sh $CATALINA_HOME/bin/startup.sh
                ;;
        stop)
                sh $CATALINA_HOME/bin/shutdown.sh
                ;;
        restart)
                sh $CATALINA_HOME/bin/shutdown.sh
                sleep 2
                sh $CATALINA_HOME/bin/startup.sh
                ;;
        *)
                echo 'please use : tomcat {start | stop | restart}'
        ;;
esac
exit 0
[root@localhost init.d]# chmod a+x tomcat    ##授权
[root@localhost init.d]# chkconfig --add tomcat    ##添加到自启动
[root@localhost init.d]# service tomcat stop    ##停止
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_311
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:
[root@localhost init.d]# service tomcat start     ##启动
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_311
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:
Tomcat started.
[root@localhost init.d]# service tomcat restart   ##重启
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_311
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_311
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:
Tomcat started.

注意:我们这里管理服务调用的是tomcat下的bin目录下的catalina.sh,因此需要给这个服务管理文件添加JAVA_HOME路径,否则会出现如下的报错:

[root@localhost init.d]# service tomcat stop
Neither the JAVA_HOME nor the JRE_HOME environment variable is defined
At least one of these environment variable is needed to run this program

解决办法如下:

[root@localhost ~]# vim /usr/local/tomcat-8080/bin/catalina.sh
添加如下一句代码:
JAVA_HOME=/usr/local/src/jdk1.8.0_311

2.7 启动配置文件管理

启动配置文件的位于bin/*下,我们常用的有如下几个

catalina.sh  :主配置文件
configtest.sh:配置检查文件
shutdown.sh:服务关闭文件
startup.sh:服务启动文件
version.sh:版本查看文件

例如:

[root@tomcat bin]# ./catalina.sh start
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_341/jre
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:   
Tomcat started.
[root@tomcat bin]# ./shutdown.sh 
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_341/jre
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:  
[root@tomcat bin]# ./version.sh 
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_341/jre
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:   
Server version: Apache Tomcat/8.5.72
Server built:   Oct 1 2021 15:15:33 UTC
Server number:  8.5.72.0
OS Name:        Linux
OS Version:     3.10.0-1160.el7.x86_64
Architecture:   amd64
JVM Version:    1.8.0_341-b10
JVM Vendor:     Oracle Corporation
[root@tomcat bin]# 

三、Tomcat配置文件

3.1 server.xml

image-20211115215005074

注意:在server.xml中,使用的部分表示注释的内容

比如:

image-20211115214603246

第一部分:

image-20211115214720295

1.元素

代表整个容器,是Tomcat实例的顶层元素.由org.apache.catalina.Server接口来定义,包含一个或多个<Service>元素。

port:指定Tomcat监听shutdown命令端口.终止服务器运行时,必须在Tomcat服务器所在的机器上发出shutdown命令.该属性必须设置。

shutdown:指定终止Tomcat服务器运行时,发给Tomcat服务器的shutdown监听端口的字符串.该属性必须设置

2.元素

包含一个<Engine>元素,以及一个或多个<Connector>元素,这些<Connector>元素共享同一个<Engine>元素。

3.元素

接收客户端连接请求,创建Request和Response对象用于和请求端交换数据,然后分配线程让Engine来处理这个请求,并把产生的Request和Response对象传给Engine,通过配置Connector,可以控制请求Service的协议及端口号。

image-20211115215216444

port:服务端监听的端口号,即客户请求的端口号;

protocol:规定了请求协议;

redirectPort:表示当请求是https时,重定向至端口号为8443的Connector;

connectionTimeout:表示连接的超时时间,单位毫秒;

minSpareThreads:表示Connector最小等待客户请求的线程数,每个请求由一个线程负责,默认10;

maxThreads:表示此连接器要创建的请求处理线程的最大数量,即可以处理的最大并发请求数,默认200;

maxConnections:表示服务器在任何给定时间接受和处理的最大连接数。当达到这个数字时,客户端请求会被放到请求队列,默认最大队列数为acceptCount参数值,BIO模式下默认为maxThreads,NIO模式下默认10000;

acceptCount:maxConnections达到最大值即所有请求线程正在使用时,传入连接请求的最大队列长度, 当队列满时收到的任何请求都将被拒绝, 默认值为100。

4.元素

Engine组件在Service组件中有且只有一个,Engine是Service组件中的请求处理组件,Engine组件从一个或多个Connector中接收请求并处理,并将完成的响应返回给Connector,最终传递给客户端。

image-20211115215415114

name:属性用于日志和错误信息,在整个Server中应该唯一。

defaultHost:属性指定了默认的host名称,当发往本机的请求指定的host名称不存在时,一律使用defaultHost指定的host进行处理,因此defaultHost的值,必须与Engine中的一个Host组件的name属性值匹配

5.元素

Engine元素至少包含一个或多个Host元素.每个Host元素定义了一个虚拟主机,它可以包含一个或多个Web应用,其中一个Host的name必须与Engine组件的defaultHost属性相匹配。

image-20211115215623402

name:虚拟主机的名字。

appBase:指定虚拟主机的目录,可以指定绝对目录,也可以指定相对于<CATALINA_HOME>的相对目录, 如果此项没有设定,默认值为<CATALINA_HOME>/webapps。

unpackWARs:如果此项设为true,表示将把Web应用的war文件先解压为开放目录结构后再运行.如果设为false,将直接运行war文件。

autoDeploy:如果此项设为true,表示当Tomcat服务器处于运行状态时,能够监测appBase下的文件,如果有新的Web应用加入进来,会自动发布这个Web应用。

6.元素

每个Context元素代表了运行在虚拟主机上的单个Web应用,一个元素中可以包含多个元素。

image-20211115220144551

path:指定访问该Web应用的URL入口。

docBase:指定Web应用的文件路径,可以为绝对路径,也可以为相对于Host的appBase属性的相对路径,如果Web应用采用开放目录结构,那就指定Web应用的根目录;如果Web应用是个WAR文件,那就指定WAR文件的路径。

reloadable:如果这个属性设为true,Tomcat服务器在运行状态下会监视在WEB-INF/class和WEB-INF/lib目录下CLASS文件的改动,如果检测到有calss文件被更新,服务器会自动重新加载Web应用

3.2 context.xml

image-20211115220607800

Context.xml 是 Tomcat 公用的环境配置,tomcat 服务器会定时去扫描这个文件。一旦发现文件被修改(时间戳改变了),就会自动重新加载这个文件,而不需要重启服务器。推荐在 $CATALINA_BASEconf/context.xml 中进行独立的配置。因为 server.xml 是不可动态重加载的资源,服务器一旦启动了以后,要修改这个文件,就得重启服务器才能重新加载,而context.xml 文件则不然。

比如下面这个配置案例:

 <Context path="/simple" docBase="simple" debug="0" reloadbale="true" privileged="true">  
      
 <WatchedResource>WEB-INF/web.xml</WatchedResource>    
 <WatchedResource>WEB-INF/eml.xml</WatchedResource> #监控资源文件,如果web.xml || eml.xml改变了,则自动重新加载改应用。  
  <Resource name="jdbc/testSiteds"   #表示指定的jndi名称  
      auth="Container"   #表示认证方式,一般为Container  
      type="javax.sql.DataSource"  
     maxActive="100"   #连接池支持的最大连接数  
     maxIdle="40"     #连接池中最多可空闲maxIdle个连接  
     maxWait="30000"   #连接池中连接用完时,新的请求等待时间,毫秒  
     username="txl"    #表示数据库用户名  
     password="123456"   #表示数据库用户的密码  
     driverClassName="com.mysql.jdbc.Driver"   #表示JDBC DRIVER  
     url="jdbc:mysql://localhost:3306/testSite" />   #表示数据库URL地址
</Context>

3.3 tomcat-users.xml(用户管理)

Tomcat Manager是Tomcat自带的、用于对Tomcat自身以及部署在Tomcat上的应用进行管理的web应用。Tomcat是Java领域使用最广泛的服务器之一,因此Tomcat Manager也成为了使用非常普遍的功能应用。

在默认情况下,Tomcat Manager是处于禁用状态的。准确地说,Tomcat Manager需要以用户角色进行登录并授权才能使用相应的功能,不过Tomcat并没有配置任何默认的用户,因此需要我们进行相应的用户配置之后才能使用Tomcat Manager。

Tomcat Manager的用户配置是在 Tomcat安装目录/conf/tomcat-users.xml文件中进行管理的

image-20211116204538158

默认点击进入提示是403的报错,因此需要配置管理用户才能查看,添加如下配置:

  <role rolename="manager-gui"/>
  <role rolename="manager-script"/>
  <user username="tomcat" password="tomcat" roles="manager-gui"/>
  <user username="admin" password="123456" roles="manager-script"/>

tomcat的用户权限有4种,如下:

1:“manager-gui”:Allows access to the html interface(允许通过web的方式登录查看服务器信息)。
2:“manager-script”: Allows access to the plain text interface(允许以纯文本的方式访问)。
3:“manager-jmx”: Allows access to the JMX proxy interface(允许jmx的代理访问)。
4:“manager-status”: Allows access to the read-only status pages(允许以只读状态访问)。

注意:对于Tomcat8来说,只有127网段局域网的机器,能够有访问权限,如果是其他网段登陆,如172.10网段,仍会报403。

需要修改app文件夹manager和host-mananger两个项目下META-INF中的context.xml文件,如下:

image-20230529093115735

修改成自己的访问来源IP地址端即可,如下:

image-20230529093144467

文件修改完成后,重启机器,登录测试

image-20211116211347905

image-20211116211543827

3.4 应用程序管理

3.4.1 通过管理界面部署war包程序

这里我准备的是jenkins的war包来测试

image-20221106220113241

image-20221106220150428

image-20221106220228332

image-20221106220816375

3.4.2 浏览器中访问测试

image-20221106220910893

注意:由于默认manager限制最大50M的文件上传,因此会出现部署失败,请按照如下的方式解除限制。

image-20221106220306496

只须要修改一个配置文件即可,配置文件位置:$tomcat_home\webapps\manager\WEB-INF\web.xml,看到没,这里限制了最大都50MB而已,然后根据自己的需要修改即可

image-20221106220452418

改成如下200MB:

image-20221106220712518

3.5 主机管理

注意:主机管理也需要在 /webapps/host-mananger项目下META-INF中的 context.xml文件解除限制,同时在 tomcat-users.xml增加访问用户

 <role rolename="admin-gui"/>
  <user username="tomcat" password="123456" roles="admin-gui"/>

image-20221106221320194

image-20221106221648843

四、虚拟主机

多时候公司会有多个项目需要运行,一般不会是在一台服务器上运行多个Tomcat服务,这样会消耗太多的系统资源。此时,就需要使用到Tomcat虚拟主机。列如现在新增两个域名www.sina.com和www.sohu.com,希望通过这两个域名访问到不同的项目内容

配置2个域名分别是:

www.sina.com    192.168.1.101
www.sohu.com    192.168.1.101

4.1 编辑tomcat的主配置文件:server.xml

 在末尾的</Engine>添加下面这几行
  <Engine name="Catalina" defaultHost="localhost">
         <Host name="www.sina.com"  appBase="/var/www/html">
        <Context path="" docBase="/var/www/html/sina" />
        </Host>
        <Host name="www.sohu.com"  appBase="/var/www/html">
        <Context path="" docBase="/var/www/html/sohu" />
        </Host>
      
说明:
appBase指定应用程序(网站)的基本路径,这里可以存放多个程序(网站),一般是相对路径,相对于tomcat的安装目录。
Context path=""为虚拟目录,如果是空,表示直接就是/,如果是如path="aa",那么访问的时候就是site:8080/aa
docBase="……" 为实际目录,就是可以是绝对路径,如果是相对路径就是基于appBase

4.2 新建对应的虚拟主机的网站根目录

[root@localhost conf]# mkdir -p /var/www/html
[root@localhost conf]# cd /var/www/html/
[root@localhost html]# mkdir sina
[root@localhost html]# mkdir sohu
[root@localhost html]# ls
sina  sohu
[root@localhost html]# echo sina >./sina/index.html
[root@localhost html]# echo sohu >./sohu/index.html
[root@localhost html]# 

4.3 重启tomcat

注意:tomcat没有restart指令,需要先stop,再start;

[root@localhost html]# /etc/init.d/tomcat stop
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_311/jre
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:   
[root@localhost html]# /etc/init.d/tomcat start
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_311/jre
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:   
Tomcat started.
[root@localhost html]# 

4)修改本地hosts目录,添加dns解析

image-20211116214130147

5)测试访问

image-20211116214438957

image-20211116214503411

补充:真实的生产环境中,需要一个合法的域名和一个合法的公网IP,把域名解析到IP上,然后在web服务器里修改配置文件

五、部署jpress站点

JPress 是一个使用 Java 开发的、开源免费的建站神器,灵感来源于 WordPress,目前已经有超过 10w+ 的网站使用 JPress 搭建,其中包括多个政府机构,200+上市公司,中科院、红十字会等。

5.1 部署数据库

注意:mysql数据库的版本要求大于5.6

这里我们采用源码编译安装的方式来安装。
#!/bin/bash
#指定脚本运行环境

#源码编译安装mysql数据库
install_mysql(){
systemctl disable --now firewalld
setenforce 0
sed -i "7c SELINUX=disabled" /etc/sysconfig/selinux

cd /opt
#安装环境依赖包
yum -y install gcc gcc-c++ ncurses ncurses-devel bison cmake expect

tar zxvf /opt/mysql-5.7.19.tar.gz -C /opt/
mkdir -p /usr/local/src/mysql-5.7.19/boost
tar zxvf boost_1_59_0.tar.gz -C /usr/local/src/mysql-5.7.19/boost
useradd -M -s /sbin/nologin  mysql

cd /opt/mysql-5.7.19
cmake \
-DCMAKE_INSTALL_PREFIX=/usr/local/mysql \
-DMYSQL_UNIX_ADDR=/usr/local/mysql/mysql.sock \
-DSYSCONFDIR=/etc \
-DSYSTEMD_PID_DIR=/usr/local/mysql \
-DDEFAULT_CHARSET=utf8  \
-DDEFAULT_COLLATION=utf8_general_ci \
-DWITH_EXTRA_CHARSETS=all \
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
-DWITH_ARCHIVE_STORAGE_ENGINE=1 \
-DWITH_BLACKHOLE_STORAGE_ENGINE=1 \
-DWITH_PERFSCHEMA_STORAGE_ENGINE=1 \
-DMYSQL_DATADIR=/usr/local/mysql/data \
-DWITH_SYSTEMD=1 \
-DDOWNLOAD_BOOST=1 \
-DWITH_BOOST=/usr/local/src/mysql-5.7.19/boost/boost_1_59_0

#编译安装
make -j 8 && make -j 8 install

#定义my.cnf文件
cat > /etc/my.cnf <<EOF
[client]
port = 3306
socket = /usr/local/mysql/mysql.sock

[mysql]
port = 3306
socket = /usr/local/mysql/mysql.sock
auto-rehash

[mysqld]
user = mysql
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data
port = 3306
character-set-server=utf8
pid-file = /usr/local/mysql/mysqld.pid
socket=/usr/local/mysql/mysql.sock
bind-address = 0.0.0.0
skip-name-resolve
max_connections=2048
default-storage-engine=INNODB
max_allowed_packet=16M
server-id = 1

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_AUTO_VALUE_ON_ZERO,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,PIPES_AS_CONCAT,ANSI_QUOTES
EOF

chown -R mysql:mysql /usr/local/mysql/
chown mysql:mysql /etc/my.cnf

cat >> /etc/profile <<EOF
export PATH=/usr/local/mysql/bin:/usr/local/mysql/lib:$PATH
EOF

source /etc/profile

cd /usr/local/mysql/bin/

#数据库初始化
./mysqld \
--initialize-insecure \
--user=mysql \
--basedir=/usr/local/mysql \
--datadir=/usr/local/mysql/data

cp /usr/local/mysql/usr/lib/systemd/system/mysqld.service /usr/lib/systemd/system/
systemctl daemon-reload
systemctl enable --now mysqld

#重置密码
/usr/bin/expect <<EOF
spawn mysqladmin -u root -p password "abc123"
expect "Enter password" {send "\r"}
expect eof
EOF

/usr/bin/expect <<EOF
spawn mysql -u root -p
expect "Enter password" {send "abc123\r"}
expect "mysql" {send "grant all privileges on *.* to 'root'@'%' identified by 'abc123';\r"}
expect "mysql" {send "quit\r"}
expect eof 
EOF
}
install_mysql

5.2 部署项目

5.2.1 准备环境
  1. 首先保证以上环境都配置好,可以正常运行。
  2. 下载jpress官方的war包,将其放置在tomcat的webapps下,并命名为jpress。可直接放war包,tomcat会帮你解压
[root@tomcat ROOT]# jar -xvf jpress-v4.2.0.war 
5.2.2 重启tomcat
[root@tomcat ROOT]# service tomcat restart
5.2.3 访问

http://IP:8080

image-20230526145328457

image-20230526145513878

image-20230526145618823

image-20230526145659716

image-20230526145805167

5.3 文章管理

5.3.1 写文章

image-20230526150817497

5.3.2 管理文章

image-20230526150855149

5.3.3 查看文章

image-20230526150933985

六、Tomcat优化

6.1 JVM优化

1、 Tomcat内存优化主要是对 tomcat 启动参数优化,我们可以在 tomcat 的启动脚本 catalina.sh 中设置 java_OPTS 参数
 
2、 JAVA_OPTS参数说明

参数说明:

file.encoding 默认文件编码

-Xmx1024m 设置JVM最大可用内存为1024MB;

-Xms1024m 设置JVM最小内存为1024m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存;

-XX:NewSize 设置年轻代大小;

-XX:MaxNewSize 设置最大的年轻代大小;

-XX:PermSize 设置永久代大小;

-XX:MaxPermSize 设置最大永久代大小;

-XX:NewRatio=4 设置年轻代(包括Eden和两个Survivor区)与终身代的比值(除去永久代)。设置为4,则年轻代与终身代所占比值为1:4,年轻代占整个堆栈的1/5;

-XX:MaxTenuringThreshold=0 设置垃圾最大年龄,默认为:15。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率

-XX:+DisableExplicitGC 这个将会忽略手动调用GC的代码使得System.gc()的调用就会变成一个空调用,完全不会触发任何GC。
 
3、配置示例:
 JAVA_OPTS=’-Xms1024m -Xmx2048m -XX: PermSize=256M -XX:MaxNewSize=256m -XX:MaxPermSize=256m’
 说明:其内存的配置需要根据服务器(或虚拟机)的实际内存来配置

6.2 Tomcat并发优化

6.2.1 调整连接器connector的并发处理能力
【redirectPort】如果某连接器支持的协议是HTTP,当接收客户端发来的HTTPS请求时,则转发至此属性定义的 8443 端口。
 
【maxThreads】Tomcat使用线程来处理接收的每个请求,这个值表示Tomcat可创建的最大的线程数,即支持的最大并发连接数,默认值是 200。
 
【minSpareThreads】最小空闲线程数,Tomcat 启动时的初始化的线程数,表示即使没有人使用也开这么多空线程等待,默认值是 10。
 
【maxSpareThreads】最大备用线程数,一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。默认值是-1(无限制)。一般不需要指定。
 
【processorCache】进程缓冲器,可以提升并发请求。默认值是200,如果不做限制的话可以设置为-1,一般采用maxThreads的值或者-1。
 
【URIEncoding】指定 Tomcat 容器的 URL 编码格式,网站一般采用UTF-8作为默认编码。
 
【connnectionTimeout】网络连接超时,单位:毫秒,设置为 0 表示永不超时,这样设置有隐患的。通常默认 20000 毫秒就可以。
 
【enableLookups】是否反查域名,以返回远程主机的主机名,取值为:true 或 false,如果设置为 false,则直接返回 IP 地址,为了提高处理能力,应设置为 false。
 
【disableUploadTimeout】上传时是否使用超时机制。应设置为 true。
 
【connectionUploadTimeout】上传超时时间,毕竟文件上传可能需要消耗更多的时间,这个根据你自己的业务需要自己调,以使Servlet有较长的时间来完成它的执行,
需要与上一个参数一起配合使用才会生效。
 
【acceptCount】指定当所有可以使用的处理请求的线程数都被使用时,可传入连接请求的最大队列长度,超过这个数的请求将不予处理,默认为 100 个。
 
【maxKeepAliveRequests】指定一个长连接的最大请求数。默认长连接是打开的,设置为1时,代表关闭长连接;为-1时,代表请求数无限制
 
【compression】是否对响应的数据进行GZIP压缩,off:表示禁止压缩;on:表示允许压缩(文本将被压缩)、force:表示所有情况下都进行压缩,默认值为 off,
压缩数据后可以有效的减少页面的大小,一般可以减小 1/3 左右,节省带宽。
 
【compressionMinSize】表示压缩响应的最小值,只有当响应报文大小大于这个值的时候才会对报文进行压缩,如果开启了压缩功能,默认值就是 2048。
 
【compressableMimeType】压缩类型,指定对哪些类型的文件进行数据压缩。
 
【noCompressionUserAgents="gozilla, traviata"】对于以下的浏览器,不启用压缩

#如果已经进行了动静分离处理,静态页面和图片等数据就不需做 Tomcat 处理,也就不要在 Tomcat 中配置压缩了。
6.2.2 压缩优化及参数
compression="on"   打开压缩功能
compressionMinSize="2048"启用压缩的输出内容大小,默认为2KB
noCompressionUserAgents="gozilla,traviata" 对于以下的浏览器,不启用压缩
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" 哪些资源类型需要压缩

生产环境配置示例:

<Connector port="8080" protocol="HTTP/1.1" 
connectionTimeout="20000" 
redirectPort="8443" 
--71行--插入
minSpareThreads="50" 
enableLookups="false" 
disableUploadTimeout="true" 
acceptCount="300" 
maxThreads="500" 
processorCache="500"
URIEncoding="UTF-8" 
maxKeepAliveRequests="100"
compression="on" 
compressionMinSize="2048" 
compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain,image/gif,image /jpg,image/png"/>

Tomcat 的压缩是在客户端请求服务器对应资源后,从服务器端将资源文件压缩,再输出到客户端,由客户端的浏览器负责解压缩并浏览。相对于普通的浏览过程 HTML、CSS、Javascript和Text,它可以节省40% 左右的流量。更为重要的是,它可以对动态生成的,包括CGI、PHP、JSP、ASP、Servlet, SHTML等输出的网页也能进行压缩,压缩效率也很高。但是, 压缩会增加 Tomcat 的负担,因此最好采用Nginx + Tomcat 或者 Apache + Tomcat 方式,将压缩的任务交由 Nginx/Apache 去做。

七、性能测试

使用Apache Jmeter 进行测试,Apache Jmeter 是开源的压力测试工具,我们借助此工具进行测试,将测试出tomcat的吞吐量等信息

7.1 下载Apache Jmeter

Apache Jmeter下载地址:https://jmeter.apache.org/

image-20230526142725664

7.2 安装jemter

进入bin目录,找到jmeter.bat文件,双击打开即可启动

提前安装好windows版本的jdk

image-20230526151603132

7.3 配置Jmeter

7.3.1 新建测试用例

image-20230526153433001

image-20230526153519363

7.3.2 添加线程组,使用线程模拟用户的并发

image-20230526153552351

1000个线程,每个线程循环10次,也就是tomcat会接收到10000个请求

image-20230526153644347

7.3.3 添加http请求

这里我使用的是我的测试项目jpress

image-20230526153816410

image-20230526154057906

7.3.4 添加请求监控

image-20230526154327964

7.3.5 开始测试

image-20230526154403967

image-20230526154658151

其中我们需要关注的就是 聚合报告 的数据,可以看到tomcat的平均并发在222个左右,并且结果没有异常

image-20230526154851470

表格查看可以直观看到每次请求的延时和平均延时结果。

7.4 优化tomcat后压测

7.4.1 线程池优化

我们优化一下tomcat的线程数的大小进行测试tomcat的性能,默认是200,我们调整到500

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
  maxThreads="500" minSpareThreads="10"/>
      
<Connector executor="tomcatThreadPool" 
            port="8080"
            protocol="org.apache.coyote.http11.Http11AprProtocol"
            connectionTimeout="20000"
            redirectPort="8443" />

完成后,重启tomcat再次测试

image-20230526160021865

可以看到吞吐量增加到了3698.2/s,说明优化的结果是可以增加吞吐的。延时也相应的减少了,平均延时降低至147ms,比优化之前的367ms大幅度降低。

image-20230526160324368

思考:是否是线程越多,速度越快呢?

在实际测试中,并不是线程越多性能越高,单靠提升线程数量是不能一直得到性能提升的。

7.4.2 优化运行模式

默认tomcat8采用的运行模式是nio的模式,为了优化性能,我们采用apr的模式

[root@tomcat logs]# tail catalina.out |grep nio
26-May-2023 15:53:49.280 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]

1)安装tomcat-Native

Tomcat 可以使用 apr 来提供更好的伸缩性、性能和集成到本地服务器技术。用来提高 tomcat 的性能。 tomcat native 在具体的运行平台上,提供了一种优化技术,它本身是基于 ARP(Apache Portable(轻便) Runtime)技术

我们应用了 tomcat native 技术之后,tomcat 在跟操作系统级别的交互方面可以做得更好,并且它更像apache 一样,可以更好地作为一台 web server。 tomcat 可以利用 apache 的 apr 接口,使用操作系统的部分本地操作,从而提升性能APR 提升的是静态页面处理能力

关于tomcat-native的相关介绍及下载:

http://tomcat.apache.org/download-native.cgi

2)安装依赖包

yum -y install apr-devel gcc gcc-c++ openssl-devel openssl   

3)我们的tomcat8 在bin下已有tomcat-native,开始解压

image-20211116215141391

[root@localhost bin]# tar zxvf tomcat-native.tar.gz -C /usr/local/src/

4)编译安装tomcat-native

[root@localhost bin]# cd /usr/local/src/tomcat-native-1.2.31-src/native
[root@localhost native]# ./configure --with-apr=/usr --with-java-home=/usr/local/src/jdk1.8.0_311 --with-ssl
[root@localhost native]# make && make install

image-20211116215907586

5)编译安装需要添加函数库支持

[root@localhost native]# vim /etc/ld.so.conf 
/usr/local/apr/lib          ##添加此行
[root@localhost native]# ldconfig   ##重新加载函数库
[root@localhost native]# echo "ldconfig" >>/etc/rc.local

6)重启tomcat,查看日志:tomcat-8080/logs/catalina.out

image-20211116223145654

可以看到,目前的模式任然是nio的模式(非阻塞模式),而不是apr模式

这是因为安装完成后,libtcnative.1.so被放在了/usr/local/apr/,但是Tomcat的java.library.path并不包含这个目录,可以使用下面命令解决。

ln -s /usr/local/apr/lib/libtcnative-1.so /usr/lib64/libtcnative-1.so

同时修改Tomcat 下 conf/server.xml protocol的值 HTTP/1.1为:org.apache.coyote.http11.Http11AprProtocol

image-20211116223529347

7)修改完成后再次重启tomcat,查看日志切换成了"apr"模式了

[root@tomcat conf]# service tomcat restart
[root@tomcat logs]# tail catalina.out |grep apr
26-May-2023 16:15:59.808 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-apr-8080"]

8)再次进行压测

image-20230526162300474

image-20230526162436624

可以看到apr模式下的吞吐量相较于nio模式下得到了大幅度的增加,达到5224/s,平均延时也降低到了67ms,说明优化是有效果的。

7.4.3 调整JVM参数进行优化

设置并行垃圾回收器,修改 bin/catalina.sh

JAVA_OPTS="-XX:+UseParallelGC -XX:+UseParallelOldGC -Xms1g -Xmx1g -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:../logs/gc.log"

参数说明:

XX:+UseParallelGC:这是一个标志,用于启用并行垃圾回收机制。启用并行垃圾回收可以提高垃圾回收的效率,尤其是在大型应用程序中。
XX:+UseParallelOldGC:这是一个标志,用于启用并行老年代垃圾回收机制。在 Java 8 中,老年代垃圾回收机制已经从串行改为并行,从而提高了垃圾回收的效率。
Xms1g:这是一个参数,用于设置虚拟机初始内存大小为 1GB。
Xmx1g:这是一个参数,用于设置虚拟机最大内存大小为 1GB。
XX:+PrintGCDetails:这是一个标志,用于打印垃圾回收的详细信息,包括垃圾回收发生的时间、堆内存的使用状况等。
XX:+PrintGCTimeStamps:这是一个标志,用于打印垃圾回收发生的时间戳,以便追踪垃圾回收的效率。
XX:+PrintGCDateStamps:这是一个标志,用于打印垃圾回收的日期戳,以便追踪垃圾回收的效率。
XX:+PrintHeapAtGC:这是一个标志,用于在垃圾回收期间打印堆内存的使用状况,以便分析垃圾回收的效率。
Xloggc:这是一个参数,用于将垃圾回收日志记录到指定的文件中。/logs/gc.log 是一个默认的文件名,可以自定义文件名。

重启tomcat,进行测试

[root@tomcat logs]# service tomcat restart

image-20230526165402290

image-20230526165440670

可以看到并发吞吐量已经达到了5998/s,系统的平均延时时间降低至了53ms、

尝试将线程数提高到2000,轮询20次,总并发数在40000,多次测试发现系统的吞吐一直维持在5000/s左右,并且还出现了异常的错误,说明有部分的请求异常了,由此判断当前系统规格(8C2G)优化下,tomcat的平均的吞吐量在5000/s左右,实际上由于DNS的解析时间,网络抖动和服务器的网络优化和性能优化不同,tomcat的实际吞吐量也不相同,以实际为准。

image-20230526165621912

image-20230526170054393

八、tomcat配合nginx实现负载均衡

注意:利用上面已经部署好了的tomcat的服务器,在重新部署一台tomcat的服务器,形成2台Tomcat的服务器

8.1 部署一台Nginx服务器,用于做负载均衡服务器

[root@localhost yum.repos.d]# yum -y install nginx

8.2 配置nginx负载均衡策略

[root@localhost nginx]# vim /etc/nginx/nginx.conf
   upstream web01 {
     server 192.168.1.102:8080;    ##配置负载均衡服务器组
     server 192.168.1.105:8080;

       }
    server {
        listen       80;
        listen       [::]:80;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
           location / {
        proxy_pass http://web01;  ## 反向代理到web01
        index index.jsp;
       }
         }

8.3 启动服务

[root@localhost nginx]# systemctl start nginx

8.4 测试

image-20220317225022802

image-20220317225041964

8.5 搭建一台redis服务器用于存放用户访问的session信息

1、安装gcc
[root@redis ~]# yum -y install gcc
2、解压安装
[root@redis ~]# tar zxvf redis-4.0.9.tar.gz
[root@redis redis-4.0.9]# make MALLOC=libc
[root@redis redis-4.0.9]# make install

8.6 配置reids数据库

[root@redis redis-4.0.9]# grep -v '^#' redis.conf
bind 192.168.1.110  #绑定IP
protected-mode yes
port 6379    #端口
daemonize yes
requirepass 123456   #认证密码

8.7 启动redis

[root@redis src]# ./redis-server /root/redis-4.0.9/redis.conf
3225:C 17 Mar 23:09:01.279 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
3225:C 17 Mar 23:09:01.279 # Redis version=4.0.9, bits=64, commit=00000000, modified=0, pid=3225, just started
3225:C 17 Mar 23:09:01.279 # Configuration loaded
[root@redis src]# netstat -antup |grep 6379
tcp        0      0 192.168.1.110:6379      0.0.0.0:*               LISTEN      3226/./redis-server
[root@redis src]#

8.8 将连接redis的jar包放置到tomcat的lib目录下

[root@localhost redis]# unzip tomcat8_redis_session.zip  ##提前上传压缩包
Archive:  tomcat8_redis_session.zip
  inflating: commons-pool2-2.2.jar
  inflating: jedis-2.5.2.jar
  inflating: tomcat8.5-redis-session-manager.jar
[root@localhost redis]# ls
commons-pool2-2.2.jar  jedis-2.5.2.jar  tomcat8.5-redis-session-manager.jar  tomcat8_redis_session.zip
[root@localhost redis]#

##将上述的三个jar包都放置到2个tomcat的lib目录下
[root@localhost redis]# cp -a commons-pool2-2.2.jar jedis-2.5.2.jar tomcat8.5-redis-session-manager.jar /usr/local/tomcat-8080/lib/

##然后修改config目录下context.xml文件,新增如下配置(2台tomcat都需要添加)
<Context>

    <!-- Default set of monitored resources. If one of these changes, the    -->
    <!-- web application will be reloaded.                                   -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>

    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
    <!--
    <Manager pathname="" />
    -->
  
    ###添加如下代码##
   <Valve className="com.s.tomcat.redissessions.RedisSessionHandlerValve" />

<Manager className="com.s.tomcat.redissessions.RedisSessionManager"

        host="192.168.1.110"

        port="6379"

        database="0"

        password="123456"

        maxInactiveInterval="60" />
</Context>

8.9 新建index.jsp

2台tomcat服务器各自在webapp目录下ROOT目录下新建index.jsp,添加如下代码查看session Id的变化

##192.168.1.102配置
<head>
<title>hello world</title>
</head>
<body>
       Hello world!<br/>
        SessionID is
        <%=session.getId()%>
        <BR> SessionIP is
        <%=request.getServerName()%>
        <BR> SessionPort is
        <%=request.getServerPort()%>
        <%
                out.println("Response from" +request.getRemoteAddr());
        %>
 </body>

##192.168.1.105配置   
<head>
<title>hello world</title>
</head>
<body>
       Hello world!<br/>
        SessionID is
        <%=session.getId()%>
        <BR> SessionIP is
        <%=request.getServerName()%>
        <BR> SessionPort is
        <%=request.getServerPort()%>
        <%
                out.println("Response from" +request.getRemoteAddr());
        %>
 </body> 

8.10 重启2台tomcat

[root@localhost ROOT]# service tomcat restart
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_311
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_311
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:
Tomcat started.

8.11 测试

查看访问的过程中session id是否有变化

image-20220317232929609

image-20220317232943860

可以发现此时无论访问到哪一台服务器,他的SessionID都是一样的。

8.12 查看redis中的session 键值数据

[root@redis src]# ./redis-cli -h 192.168.1.110
192.168.1.110:6379> auth 123456
OK
192.168.1.110:6379> keys *
1) "67F36E922E01D31F4E0E52CB7140D434"
2) "460B0126D753B97DE2B916F57D896808"
3) "D1444A2F509CF31162D8DEA5EA00D46A"   ##此ID就是当前使用的session_id
4) "7D3381CC5F71204EDD8C362476E15615"
5) "A2E26686B7680D363F2305B99F0827EE"
6) "0175D43DFCFC475BBDB8D3520B238A8F"
7) "02D292A2ECA99E69FF2D14E4BAE3B9E9"
192.168.1.110:6379>

8.13 新建测试数据

mysql> create database tomcat;        //测试数据库,为了和后面方便测试,这里创建tomcat
mysql> grant all on tomcat.* to tomcat@localhost identified by 'tomcat';              //授权用户
mysql> grant all on tomcat.* to tomcat@127.0.0.1 identified by 'tomcat';   
mysql> flush privileges;

8.14 使用tomcat测试登录

[root@localhost ~]# mysql -utomcat -p     #登录成功
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.7.36 MySQL Community Server (GPL)

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

8.15 下载mysql-connector

mysql-connector下载地址:https://www.mysql.com/cn/products/connector/

image-20211116225009254

image-20220317220424729

image-20220317220308483

8.16 安装mysql-connector

#只需要复制jar文件到tomcat的lib目录下,重启tomcat就可以生效

[root@localhost ~]# tar zxvf mysql-connector-java-5.1.25.tar.gz -C /usr/local/src/  ##解压即可
[root@localhost mysql-connector-java-5.1.25]# cp mysql-connector-java-5.1.25-bin.jar /usr/local/tomcat-8080/lib/

8.17 重启tomcat

[root@localhost mysql-connector-java-5.1.25]# service tomcat restart
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_311
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:
Using CATALINA_BASE:   /usr/local/tomcat-8080
Using CATALINA_HOME:   /usr/local/tomcat-8080
Using CATALINA_TMPDIR: /usr/local/tomcat-8080/temp
Using JRE_HOME:        /usr/local/src/jdk1.8.0_311
Using CLASSPATH:       /usr/local/tomcat-8080/bin/bootstrap.jar:/usr/local/tomcat-8080/bin/tomcat-juli.jar
Using CATALINA_OPTS:
Tomcat started.
[root@localhost mysql-connector-java-5.1.25]#

8.18 建立测试页信息

在2个tomcat的服务器上添加如下的内容

[root@localhost src]# cd /usr/local/tomcat-8080/webapps/ROOT
[root@localhost ROOT]# cat mysql.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@page import="java.sql.DriverManager"%>
<%@page import="java.sql.Connection"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<% 
    try{
        Class.forName("com.mysql.jdbc.Driver");   //加载数据库驱动,注册到驱动管理器
        String url="jdbc:mysql://localhost:3306/tomcat";  //根据自己的情况修改mysql数据库ip和端口,tomcat是数据库上存在的一个库
        String username="tomcat";   //登录账号
        String password="tomcat";  //登录密码
        Connection conn=DriverManager.getConnection(url,username,password);
        if(conn!=null){
            out.println("mysql数据库连接成功!!!");
        }else{
            out.println("数据库连接失败!!!");
        }
    }catch(ClassNotFoundException e){
        e.printStackTrace();
    }
%>
</body>
</html>

image-20211117215040489

扩展:

sessionid是一个会话的key,浏览器第一次访问服务器会在服务器端生成一个session,有一个sessionid和它对应。tomcat生成的sessionid叫做jsessionid。

session在访问tomcat服务器HttpServletRequest的getSession(true)的时候创建,tomcat的ManagerBase类提供创建sessionid的方法:随机数+时间+jvmid;

存储在服务器的内存中,tomcat的StandardManager类将session存储在内存中,也可以持久化到file,数据库,memcache,redis等。客户端只保存sessionid到cookie中,而不会保存session,session销毁只能通过invalidate或超时,关掉浏览器并不会关闭session

8.19 配置SSL证书

8.19.1 生成证书
第一步:生成私钥
[root@nginx ~]# openssl genrsa -out hopu.key 1024
Generating RSA private key, 1024 bit long modulus
...............................++++++
..........++++++
e is 65537 (0x10001)


第二步:生成证书信息
[root@nginx ~]# openssl req -new -key hopu.key -out hopu.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:cn
State or Province Name (full name) []:hb
Locality Name (eg, city) [Default City]:wh
Organization Name (eg, company) [Default Company Ltd]:it
Organizational Unit Name (eg, section) []:it
Common Name (eg, your name or your server's hostname) []:www.bestit.com
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:


第三步:生成签名证书
[root@nginx ~]# openssl x509 -req -days 365 -sha256 -in hopu.csr -signkey hopu.key -out hopu.crt
Signature ok
subject=/C=cn/ST=hb/L=wh/O=it/OU=it/CN=www.bestit.com
Getting Private key
8.19.2 将证书复制到指定位置
[root@nginx nginx]# mkdir ssl
[root@nginx ~]# cp hopu* /etc/nginx/ssl/
[root@nginx ~]# ll /etc/nginx/ssl/
total 12
-rw-r--r-- 1 root root 814 May 29 15:04 hopu.crt
-rw-r--r-- 1 root root 631 May 29 15:04 hopu.csr
-rw-r--r-- 1 root root 887 May 29 15:04 hopu.key
8.19.3 配置nginx
 server {
        listen       443 ssl;
        server_name  www.bestit.com;   #域名信息
       ssl_certificate /etc/nginx/ssl/hopu.crt;
       ssl_certificate_key /etc/nginx/ssl/hopu.key;
       ssl_session_timeout 5m;
       ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
       ssl_prefer_server_ciphers on;
       ssl_ciphers "EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5";

        location / {
        proxy_pass http://web;  ## 反向代理到web
        index index.jsp;
       }
}
8.19.4 重启tomcat测试

image-20230529151816111

image-20230529151841576