jvm与Tomcat调优【详解】——有这一篇就够了

一、JVM性能调优

1.1 什么是JVM?

JVM就是Java虚拟机,Java虚拟机就是JVM

在这里插入图片描述

JVM调优目标: 使用较小的内存占用来获得较高的吞吐量或者较低的延迟。

程序在上线前的测试或运行中有时会出现一些大大小小的JVM问题,比如cpu load过高、请求延迟、tps降低等,甚至出现内存泄漏(每次垃圾收集使用的时间越来越长,垃圾收集频率越来越高,每次垃圾收集清理掉的垃圾数据越来越少)、内存溢出导致系统崩溃,因此需要对JVM进行调优,使得程序在正常运行的前提下,获得更高的用户体验和运行效率。

这里有几个比较重要的指标:

内存占用:程序正常运行需要的内存大小。

延迟:由于垃圾收集而引起的程序停顿时间。

吞吐量:用户程序运行时间占用户程序和垃圾收集占用总时间的比值。

当然,和CAP原则一样,同时满足一个程序内存占用小、延迟低、高吞吐量是不可能的,程序的目标不同,调优时所考虑的方向也不同,在调优之前,必须要结合实际场景,有明确的的优化目标,找到性能瓶颈,对瓶颈有针对性的优化,最后进行测试,通过各种监控工具确认调优后的结果是否符合目标。

1.2 JVM调优工具

(1)调优可以依赖、参考的数据有系统运行日志、堆栈错误信息、gc日志、线程快照、堆转储快照等。

  • 系统运行日志: 系统运行日志就是在程序代码中打印出的日志,描述了代码级别的系统运行轨迹(执行的方法、入参、返回值等),一般系统出现问题,系统运行日志是首先要查看的日志。

  • 堆栈错误信息: 当系统出现异常后,可以根据堆栈信息初步定位问题所在,比如根据“java.lang.OutOfMemoryError: Java heap space”可以判断是堆内存溢出;根据“java.lang.StackOverflowError”可以判断是栈溢出;根据“java.lang.OutOfMemoryError: PermGen space”可以判断是方法区溢出等。

  • GC日志: 程序启动时用 -XX:+PrintGCDetails 和 -Xloggc:/data/jvm/gc.log 可以在程序运行时把gc的详细过程记录下来,或者直接配置“-verbose:gc”参数把gc日志打印到控制台,通过记录的gc日志可以分析每块内存区域gc的频率、时间等,从而发现问题,进行有针对性的优化。

比如如下一段GC日志:

2018-08-02T14:39:11.560-0800: 10.171: [GC [PSYoungGen: 30128K->4091K(30208K)] 51092K->50790K(98816K), 0.0140970 secs] [Times: user=0.02 sys=0.03, real=0.01 secs] 
2018-08-02T14:39:11.574-0800: 10.185: [Full GC [PSYoungGen: 4091K->0K(30208K)] [ParOldGen: 46698K->50669K(68608K)] 50790K->50669K(98816K) [PSPermGen: 2635K->2634K(21504K)], 0.0160030 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 
2018-08-02T14:39:14.045-0800: 12.656: [GC [PSYoungGen: 14097K->4064K(30208K)] 64766K->64536K(98816K), 0.0117690 secs] [Times: user=0.02 sys=0.01, real=0.01 secs] 
2018-08-02T14:39:14.057-0800: 12.668: [Full GC [PSYoungGen: 4064K->0K(30208K)] [ParOldGen: 60471K->401K(68608K)] 64536K->401K(98816K) [PSPermGen: 2634K->2634K(21504K)], 0.0102020 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]

上面一共是4条GC日志,来看第一行日志,“2018-08-02T14:39:11.560-0800”是精确到了毫秒级别的UTC 通用标准时间格式,配置了“-XX:+PrintGCDateStamps”这个参数可以跟随gc日志打印出这种时间戳,“10.171”是从JVM启动到发生gc经过的秒数。第一行日志正文开头的“[GC”说明这次GC没有发生Stop-The-World(用户线程停顿),第二行日志正文开头的“[Full GC”说明这次GC发生了Stop-The-World,所以说,[GC和[Full GC跟新生代和老年代没关系,和垃圾收集器的类型有关系,如果直接调用System.gc(),将显示[Full GC(System)。接下来的“[PSYoungGen”、“[ParOldGen”表示GC发生的区域,具体显示什么名字也跟垃圾收集器有关,比如这里的“[PSYoungGen”表示Parallel Scavenge收集器,“[ParOldGen”表示Serial Old收集器,此外,Serial收集器显示“[DefNew”,ParNew收集器显示“[ParNew”等。再往后的“30128K->4091K(30208K)”表示进行了这次gc后,该区域的内存使用空间由30128K减小到4091K,总内存大小为30208K。每个区域gc描述后面的“51092K->50790K(98816K), 0.0140970 secs”进行了这次垃圾收集后,整个堆内存的内存使用空间由51092K减小到50790K,整个堆内存总空间为98816K,gc耗时0.0140970秒。

  • ④线程快照:顾名思义,根据线程快照可以看到线程在某一时刻的状态,当系统中可能存在请求超时、死循环、死锁等情况是,可以根据线程快照来进一步确定问题。通过执行虚拟机自带的“jstack pid”命令,可以dump出当前进程中线程的快照信息,更详细的使用和分析网上有很多例。

  • ⑤堆转储快照:程序启动时可以使用 “-XX:+HeapDumpOnOutOfMemory” 和 “-XX:HeapDumpPath=/data/jvm/dumpfile.hprof”,当程序发生内存溢出时,把当时的内存快照以文件形式进行转储(也可以直接用jmap命令转储程序运行时任意时刻的内存快照),事后对当时的内存使用情况进行分析。

(2)JVM调优工具

①用 jps(JVM process Status)可以查看虚拟机启动的所有进程、执行主类的全名、JVM启动参数,比如当执行了JPSTest类中的main方法后(main方法持续执行),执行 jps -l可看到下面的JPSTest类的pid为31354,加上-v参数还可以看到JVM启动参数。

3265 
32914 sun.tools.jps.Jps
31353 org.jetbrains.jps.cmdline.Launcher
31354 com.danny.test.code.jvm.JPSTest
380

②用jstat(JVM Statistics Monitoring Tool)监视虚拟机信息
jstat -gc pid 500 10 :每500毫秒打印一次Java堆状况(各个区的容量、使用容量、gc时间等信息),打印10次。

S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
11264.0 11264.0 11202.7  0.0   11776.0   1154.3   68608.0    36238.7     -      -      -      -        14    0.077   7      0.049    0.126
11264.0 11264.0 11202.7  0.0   11776.0   4037.0   68608.0    36238.7     -      -      -      -        14    0.077   7      0.049    0.126
11264.0 11264.0 11202.7  0.0   11776.0   6604.5   68608.0    36238.7     -      -      -      -        14    0.077   7      0.049    0.126
11264.0 11264.0 11202.7  0.0   11776.0   9487.2   68608.0    36238.7     -      -      -      -        14    0.077   7      0.049    0.126
11264.0 11264.0  0.0    0.0   11776.0   258.1    68608.0    58983.4     -      -      -      -        15    0.082   8      0.059    0.141
11264.0 11264.0  0.0    0.0   11776.0   3076.8   68608.0    58983.4     -      -      -      -        15    0.082   8      0.059    0.141
11264.0 11264.0  0.0    0.0   11776.0    0.0     68608.0     390.0      -      -      -      -        16    0.084   9      0.066    0.149
11264.0 11264.0  0.0    0.0   11776.0    0.0     68608.0     390.0      -      -      -      -        16    0.084   9      0.066    0.149
11264.0 11264.0  0.0    0.0   11776.0   258.1    68608.0     390.0      -      -      -      -        16    0.084   9      0.066    0.149
11264.0 11264.0  0.0    0.0   11776.0   3012.8   68608.0     390.0      -      -      -      -        16    0.084   9      0.066    0.149

jstat还可以以其他角度监视各区内存大小、监视类装载信息等,具体可以google jstat的详细用法。

③用jmap(Memory Map for Java)查看堆内存信息
执行jmap -histo pid可以打印出当前堆中所有每个类的实例数量和内存占用,如下,class name是每个类的类名([B是byte类型,[C是char类型,[I是int类型),bytes是这个类的所有示例占用内存大小,instances是这个类的实例数量:

num     #instances         #bytes  class name
----------------------------------------------
  1:          2291       29274080  [B
  2:         15252        1961040  <methodKlass>
  3:         15252        1871400  <constMethodKlass>
  4:         18038         721520  java.util.TreeMap$Entry
  5:          6182         530088  [C
  6:         11391         273384  java.lang.Long
  7:          5576         267648  java.util.TreeMap
  8:            50         155872  [I
  9:          6124         146976  java.lang.String
 10:          3330         133200  java.util.LinkedHashMap$Entry
 11:          5544         133056  javax.management.openmbean.CompositeDataSupport

执行 jmap -dump 可以转储堆内存快照到指定文件,比如执行 jmap -dump:format=b,file=/data/jvm/dumpfile_jmap.hprof 3361 可以把当前堆内存的快照转储到dumpfile_jmap.hprof文件中,然后可以对内存快照进行分析。

④利用jconsole、jvisualvm分析内存信息(各个区如Eden、Survivor、Old等内存变化情况),如果查看的是远程服务器的JVM,程序启动需要加上如下参数:

"-Dcom.sun.management.jmxremote=true" 
"-Djava.rmi.server.hostname=12.34.56.78" 
"-Dcom.sun.management.jmxremote.port=18181" 
"-Dcom.sun.management.jmxremote.authenticate=false" 
"-Dcom.sun.management.jmxremote.ssl=false"

下图是jconsole界面,概览选项可以观测堆内存使用量、线程数、类加载数和CPU占用率;内存选项可以查看堆中各个区域的内存使用量和左下角的详细描述(内存大小、GC情况等);线程选项可以查看当前JVM加载的线程,查看每个线程的堆栈信息,还可以检测死锁;VM概要描述了虚拟机的各种详细参数。(jconsole功能演示)
在这里插入图片描述
下图是jvisualvm的界面,功能比jconsole略丰富一些,不过大部分功能都需要安装插件。概述跟jconsole的VM概要差不多,描述的是jvm的详细参数和程序启动参数;监视展示的和jconsole的概览界面差不多(CPU、堆/方法区、类加载、线程);线程和jconsole的线程界面差不多;抽样器可以展示当前占用内存的类的排行榜及其实例的个数;Visual GC可以更丰富地展示当前各个区域的内存占用大小及历史信息。

⑤分析堆转储快照

前面说到配置了 “-XX:+HeapDumpOnOutOfMemory” 参数可以在程序发生内存溢出时dump出当前的内存快照,也可以用jmap命令随时dump出当时内存状态的快照信息,dump的内存快照一般是以.hprof为后缀的二进制格式文件。
可以直接用 jhat(JVM Heap Analysis Tool) 命令来分析内存快照,它的本质实际上内嵌了一个微型的服务器,可以通过浏览器来分析对应的内存快照,比如执行 jhat -port 9810 -J-Xmx4G /data/jvm/dumpfile_jmap.hprof 表示以9810端口启动 jhat 内嵌的服务器:

Reading from /Users/dannyhoo/data/jvm/dumpfile_jmap.hprof...
Dump file created Fri Aug 03 15:48:27 CST 2018
Snapshot read, resolving...
Resolving 276472 objects...
Chasing references, expect 55 dots.......................................................
Eliminating duplicate references.......................................................
Snapshot resolved.
Started HTTP server on port 9810
Server is ready.

在控制台可以看到服务器启动了,访问 http://127.0.0.1:9810/ 可以看到对快照中的每个类进行分析的结果(界面略low),下图是我随便选择了一个类的信息,有这个类的父类,加载这个类的类加载器和占用的空间大小,下面还有这个类的每个实例(References)及其内存地址和大小,点进去会显示这个实例的一些成员变量等信息:
在这里插入图片描述
jvisualvm也可以分析内存快照,在jvisualvm菜单的“文件”-“装入”,选择堆内存快照,快照中的信息就以图形界面展示出来了,如下,主要可以查看每个类占用的空间、实例的数量和实例的详情等:

还有很多分析内存快照的第三方工具,比如eclipse mat,它比jvisualvm功能更专业,出了查看每个类及对应实例占用的空间、数量,还可以查询对象之间的调用链,可以查看某个实例到GC Root之间的链,等等。可以在eclipse中安装mat插件,也可以下载独立的版本。

1.3 JVM调优经验

JVM配置方面,一般情况可以先用默认配置(基本的一些初始参数可以保证一般的应用跑的比较稳定了),在测试中根据系统运行状况(会话并发情况、会话时间等),结合gc日志、内存监控、使用的垃圾收集器等进行合理的调整,当老年代内存过小时可能引起频繁Full GC,当内存过大时Full GC时间会特别长。

那么JVM的配置比如新生代、老年代应该配置多大最合适呢?答案是不一定,调优就是找答案的过程,物理内存一定的情况下,新生代设置越大,老年代就越小,Full GC频率就越高,但Full GC时间越短;相反新生代设置越小,老年代就越大,Full GC频率就越低,但每次Full GC消耗的时间越大。建议如下:

  • -Xms和-Xmx的值设置成相等,堆大小默认为-Xms指定的大小,默认空闲堆内存小于40%时,JVM会扩大堆到-Xmx指定的大小;空闲堆内存大于70%时,JVM会减小堆到-Xms指定的大小。如果在Full GC后满足不了内存需求会动态调整,这个阶段比较耗费资源。

  • 新生代尽量设置大一些,让对象在新生代多存活一段时间,每次Minor GC 都要尽可能多的收集垃圾对象,防止或延迟对象进入老年代的机会,以减少应用程序发生Full GC的频率。

  • 老年代如果使用CMS收集器,新生代可以不用太大,因为CMS的并行收集速度也很快,收集过程比较耗时的并发标记和并发清除阶段都可以与用户线程并发执行。

  • 方法区大小的设置,1.6之前的需要考虑系统运行时动态增加的常量、静态变量等,1.7只要差不多能装下启动时和后期动态加载的类信息就行。

代码实现方面,性能出现问题比如程序等待、内存泄漏除了JVM配置可能存在问题,代码实现上也有很大关系:

  • 避免创建过大的对象及数组:过大的对象或数组在新生代没有足够空间容纳时会直接进入老年代,如果是短命的大对象,会提前出发Full GC。

  • 避免同时加载大量数据,如一次从数据库中取出大量数据,或者一次从Excel中读取大量记录,可以分批读取,用完尽快清空引用。

  • 当集合中有对象的引用,这些对象使用完之后要尽快把集合中的引用清空,这些无用对象尽快回收避免进入老年代。

  • 可以在合适的场景(如实现缓存)采用软引用、弱引用,比如用软引用来为ObjectA分配实例:SoftReference objectA=new SoftReference(); 在发生内存溢出前,会将objectA列入回收范围进行二次回收,如果这次回收还没有足够内存,才会抛出内存溢出的异常。
    避免产生死循环,产生死循环后,循环体内可能重复产生大量实例,导致内存空间被迅速占满。

  • 尽量避免长时间等待外部资源(数据库、网络、设备资源等)的情况,缩小对象的生命周期,避免进入老年代,如果不能及时返回结果可以适当采用异步处理的方式等。

1.4常用JVM参数参考:

参数 说明 实例
-Xms 初始堆大小,默认物理内存的1/64 -Xms512M
-Xmx 最大堆大小,默认物理内存的1/4 -Xms2G
-Xmn 新生代内存大小,官方推荐为整个堆的3/8 -Xmn512M
-Xss 线程堆栈大小,jdk1.5及之后默认1M,之前默认256k -Xss512k
-XX:NewRatio=n 设置新生代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4 -XX:NewRatio=3
-XX:SurvivorRatio=n 年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:8,表示Eden:Survivor=8:1:1,一个Survivor区占整个年轻代的1/8 -XX:SurvivorRatio=8
-XX:PermSize=n 永久代初始值,默认为物理内存的1/64 -XX:PermSize=128M
-XX:MaxPermSize=n 永久代最大值,默认为物理内存的1/4 -XX:MaxPermSize=256M
-verbose:class 在控制台打印类加载信息
-verbose:gc 在控制台打印垃圾回收日志
-XX:+PrintGC 打印GC日志,内容简单
-XX:+PrintGCDetails 打印GC日志,内容详细
-XX:+PrintGCDateStamps 在GC日志中添加时间戳
-Xloggc:filename 指定gc日志路径 -Xloggc:/data/jvm/gc.log
-XX:+UseSerialGC 年轻代设置串行收集器Serial
-XX:+UseParallelGC 年轻代设置并行收集器Parallel Scavenge
-XX:ParallelGCThreads=n 设置Parallel Scavenge收集时使用的CPU数。并行收集线程数。 -XX:ParallelGCThreads=4
-XX:MaxGCPauseMillis=n 设置Parallel Scavenge回收的最大时间(毫秒) -XX:MaxGCPauseMillis=100
-XX:GCTimeRatio=n 设置Parallel Scavenge垃圾回收时间占程序运行时间的百分比。公式为1/(1+n) -XX:GCTimeRatio=19
-XX:+UseParallelOldGC 设置老年代为并行收集器ParallelOld收集器
-XX:+UseConcMarkSweepGC 设置老年代并发收集器CMS
-XX:+CMSIncrementalMode 设置CMS收集器为增量模式,适用于单CPU情况。

1.5 Java文件编译的过程

因此就需要编译:

程序员编写的.java文件
由javac编译成字节码文件.class:(为什么编译成class文件,因为JVM只认识.class文件)
在由JVM编译成电脑认识的文件 (对于电脑系统来说 文件代表一切)
(这是一个大概的观念 抽象画的概念)

在这里插入图片描述

1.6 为什么说java是跨平台语言?

这个夸平台是中间语言(JVM)实现的夸平台
java有JVM从软件层面屏蔽了底层硬件、指令层面的细节让他兼容各种系统

难道 C 和 C++ 不能夸平台吗 其实也可以
C和C++需要在编译器层面去兼容不同操作系统的不同层面,写过C和C++的就知道不同操作系统的有些代码是不一样。

1.7 Jdk和Jre和JVM的区别

看Java官方的图片,Jdk中包括了Jre,Jre中包括了JVM

Jvm在倒数第二层 由他可以在(最后一层的)各种平台上运行

Jre大部分都是 C 和 C++ 语言编写的,他是我们在编译java时所需要的基础的类库

Jdk还包括了一些Jre之外的东西 ,就是这些东西帮我们编译Java代码的, 还有就是监控Jvm的一些工具
在这里插入图片描述

二、Tomcat概述与优化

2.1 Tomcat概述

Tomcat 是 Java 语言开发的,Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器,是 Apache 软件基金会的 Jakarta 项目中的一个核心项目,由 Apache、Sun 和其他一些公司及个人共同开发而成。
Tomcat 属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试 JSP 程序的首选。一般来说,Tomcat 虽然和 Apache 或者 Nginx 这些 Web 服务器一样,具有处理 HTML 页面的功能,然而由于其处理静态 HTML 的能力远不及 Apache 或者 Nginx,所以 Tomcat 通常是作为一个 Servlet 和 JSP 容器,单独运行在后端。

2.2 Tomcat核心组件

1)Web 容器:完成 Web 服务器的功能。
(2)Servlet 容器:名字为 catalina,用于处理 Servlet 代码。
(3)JSP 容器:用于将 JSP 动态网页翻译成 Servlet 代码。

因此 Tomcat 是 Web 应用服务器,也是一个 Servlet/JSP 容器。Tomcat 作为 Servlet 容器,负责处理客户请求,把请求传送给 Servlet,并将 Servlet 的响应传送回给客户。

1、Web容器 :①接受、响应请求
		     ② 展示动态页面      
		       入口  出口
2、JSP容器:翻译java--> servlet
index.html --》 nginx可以直接展示index.php

index.jsp --> java环境---》和数据库怎么进行交互,怎么连接到后端???

index.jsp---》java类代码---》就是执行对接后端的执行代码,所以我们需要在进行与用户交互时,将用户发出的请求、数据、传输到后端 
但是index.jsp中的一些java代码无法直接执行对接或者调用,需要先翻译魏可以可直接执行的代码方式---》格式serverlst 代码格式

3、serverlet容器:serverlet代码格式就是用JSP容器来处理

简介:
web容器:1、接受、响应请求 2 、 展示动态页面
JSP容器:翻译java—>servlet
Serverlet容器:借助于catalina来执行servelet代码(动态任务执行的标准格式)

2.2.1 Tomcat 功能组件结构

Tomcat 的核心功能有两个,分别是负责接收和反馈外部请求的连接器 Connector,和负责处理请求的容器 Container。 其中连接器和容器相辅相成,一起构成了基本的 web 服务 Service。每个 Tomcat 服务器可以管理多个 Service。

  • Connector:负责对外接收和响应请求。它是Tomcat与外界的交通枢纽,监听端口接收外界请求,并将请求处理后传递给容器做业务处理,最后将容器处理后的结果响应给外界。
  • Container:负责对内处理业务逻辑。其内部由 Engine、Host、Context和Wrapper 四个容器组成,用于管理和调用 Servlet 相关逻辑。
  • Service:对外提供的 Web 服务。主要包含 Connector 和 Container 两个核心组件,以及其他功能组件。Tomcat 可以管理多个 Service,且各 Service 之间相互独立。

2.2.2 Container 结构分析

每个 Service 会包含一个 Container 容器。在 Container 内部包含了 4 个子容器:
4个子容器的作用分别是:

1)Engine:引擎,用来管理多个虚拟主机,一个 Service 最多只能有一个 Engine;
(2)Host:代表一个虚拟主机,也可以叫站点,通过配置 Host 就可以添加站点;
(3)Context:代表一个 Web 应用,包含多个 Servlet 封装器;
(4)Wrapper:封装器,容器的最底层。每一 Wrapper 封装着一个 Servlet,负责对象实例的创建、执行和销毁功能。

Engine、Host、Context 和 Wrapper,这四个容器之间属于父子关系
容器 由一个引擎可以管理多个虚拟主机。每个虚拟主机可以管理多个 Web 应用。每个 Web 应用会有多个 Servlet 封装器。

2.3 什么是 servlet?

Servlet 是Java Servlet 的简称,可以理解为是一个服务连接器,是用 Java 编写的服务器端程序,具有独立于平台和协议的特性, 简单的理解:servlet 就是一个中间件,包含了接口和方法,将客户端和数据库连接,从而实现动态网页的创建。

2.4 什么是 JSP?

  • JSP 全称 Java Server Pages,是一种动态网页开发技术。它使用 JSP 标签在HTML网页中插入 Java 代码。标签通常以 <% 开头,以 %> 结束。
  • JSP 是一种 Java servlet,主要用于实现 Java web 应用程序的用户界面部分。
  • JSP 通过网页表单获取用户输入数据、访问数据库及其他数据源,然后动态地创建网页

2.5 Tomcat请求过程

1、用户在浏览器中输入网址,请求被发送到本机端口 8080,被在那里监听的 Connector 获得;
2、Connector把该请求交给它所在的 Service 的 Engine(Container)来处理,并等待 Engine 的回应;
3、请求在 Engine、Host、Context 和 Wrapper 这四个容器之间层层调用,最后在 Servlet中执行对应的业务逻辑、数据存储等。
4、执行完之后的请求响应在 Context、Host、Engine 容器之间层层返回,最后返回给Connector,并通过 Connector 返回给客户端。

2.5.1 Tomcat工作过程详解:

假设来自客户的请求为: http://localhost:8080/wsota/wsota_index.jsp 
1) 请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector获得 
2) Connector把该请求交给它所在的Service的Engine来处理,并等待来自Engine的回应 
3) Engine获得请求localhost/wsota/wsota_index.jsp,匹配它所拥有的所有虚拟主机Host 
4) Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机) 
5) localhost Host获得请求/wsota/wsota_index.jsp,匹配它所拥有的所有Context 
6) Host匹配到路径为/wsota的Context(如果匹配不到就把该请求交给路径名为""的Context去处理) 
7) path="/wsota"的Context获得请求/wsota_index.jsp,在它的mapping table中寻找对应的servlet 
8) Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类 
9) 构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法 
10)Context把执行完了之后的HttpServletResponse对象返回给Host 
11)Host把HttpServletResponse对象返回给Engine 
12)Engine把HttpServletResponse对象返回给Connector 
13)Connector把HttpServletResponse对象返回给客户browser

在这里插入图片描述
在这里插入图片描述

2.6 Tomcat 服务部署

在部署 Tomcat 之前必须安装好 jdk,因为 jdk 是 Tomcat 运行的必要环境。

2.6.1 关闭防火墙,将安装 Tomcat 所需软件包传到/opt目录下

jdk-8u201-linux-x64.rpm
apache-tomcat-9.0.16.tar.gz

systemctl stop firewalld
systemctl disable firewalld
setenforce 0

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.6.2 安装JDK

cd /opt
rpm -qpl jdk-8u201-linux-x64.rpm
rpm -ivh jdk-8u201-linux-x64.rpm
java -version

在这里插入图片描述

2.6.3 设置JDK环境变量

vim /etc/profile.d/java.sh
export JAVA_HOME=/usr/java/jdk1.8.0_201-amd64
export CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar
export PATH=$JAVA_HOME/bin:$PATH

source /etc/profile.d/java.sh
java -version

在这里插入图片描述
在这里插入图片描述

2.6.4 拓展小知识

CLASSPATH:编译、运行Java程序时,JRE会去该变量指定的路径中搜索所需的类(.class)文件。
dt.jar:是关于运行环境的类库,主要是可视化的 swing 的包。
tools.jar:主要是一些jdk工具的类库,包括javac、java、javap(jdk自带的一个反编译工具)、javadoc等。
JDK :java development kit (java开发工具)
JRE :java runtime environment (java运行时环境)
JVM :java virtuak machine (java虚拟机),使java程序可以在多种平台上运行class文件。

2.6.5 安装 启动Tomcat

cd /opt
tar zxvf apache-tomcat-9.0.16.tar.gz
mv apache-tomcat-9.0.16 /usr/local/tomcat
##启动tomcat ##
#后台启动
/usr/local/tomcat/bin/startup.sh 
或
/usr/local/tomcat/bin/catalina.sh start	
	
#前台启动
/usr/local/tomcat/bin/catalina.sh run		

netstat -natp | grep 8080

浏览器访问Tomcat的默认主页 http://192.168.10.12:8080

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.6.6 将tomcat添加到服务

1. #先关闭服务
/usr/local/tomcat/bin/shutdown.sh
 
2. #添加用户设置属主属组
useradd -s /sbin/nologin tomcat
chown tomcat:tomcat /usr/local/tomcat -R

3. #新建服务文件
vim /etc/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
ExecStart=/usr/local/tomcat/bin/startup.sh
ExecStop=/usr/local/tomcat/bin/shutdown.sh
RestartSec=3
PrivateTmp=true
User=tomcat
Group=tomcat
 
[Install]
WantedBy=multi-user.target
 
4. #重新加载服务,并开启,查看是否成功启动
systemctl daemon-reload
systemctl start tomcat
ss -ntap |grep 8080

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.7 Tomcat服务优化

2.7.1 Tomcat 虚拟主机配置

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

1. #创建 zzh 和 accp 项目目录和文件
cd /usr/local/tomcat/webapps
mkdir zzh accp
 
2. #创建文件
echo "欢迎来到张张的世界!" > zzh/index.html
echo "这里是accp的世界!!" > accp/index.html
 
3. #修改主配置文件[165行下面添加该内容]
这里面你多一个标点符号都不行,不要问为什么,因为就是这么规定的!修改配置文件之后,一定要重新启动服务!!!
vim /usr/local/tomcat/conf/server.xml 
 
     <Host name="www.zzh.com"  appBase="/usr/local/tomcat/webapps" unpackWARs="true" autoDeploy="true"
              xmlValidation="false" xmlNamespaceAware="false">
              <Context docBase="/usr/local/tomcat/webapps/zzh"
                path="" reloadable="true" />
     </Host>
        <Host name="www.accp.com"  appBase="/usr/local/tomcat/webapps" unpackWARs="true" autoDeploy="true"
              xmlValidation="false" xmlNamespaceAware="false">
              <Context docBase="/usr/local/tomcat/webapps/accp"
                path="" reloadable="true" />
     </Host>
 
5. #重启服务
systemctl restart tomcat
 
6. #再开启一台centos虚拟机,更改host文件
vim /etc/hosts
192.168.10.12  www.zzh.com  www.accp.com
 
7. #在网页中测试
http://www.accp.com:8080/accp/
http://www.zzh.com:8080/zzh/

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

当你去访问浏览器时,出现报错!你要去考虑到一下几个问题!

当你的虚拟机里面可以访问地址,但是在浏览器无法访问时,要做一下步骤:

1.你需要去你的本机window机器里面添加域名!
在这里插入图片描述
在这里插入图片描述
2.你输入的内容是中文时,需要在Nginx以及Tomcat里面添加配置文件 +字符集
cd /usr/local/nginx/conf/nginx.conf
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.7.2 Tomcat的缺省端口是多少,怎么修改

  1. 找到Tomcat目录下的conf文件夹
  2. 进入conf文件夹里面找到server.xml文件
  3. 打开server.xml文件
  4. 在server.xml文件里面找到下列信息
  5. 把Connector标签的8080端口改成你想要的端口
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" />

2.7.3 内存优化

严重: Exception invoking periodic operation: java.lang.OutOfMemoryError: Java heap space  
严重: Error processing request java.lang.OutOfMemoryError: GC overhead limit exceeded 

说明Tomcat已经无力支持访问处理,内部GC也已经“无能无力”。所以一般情况下我们需要重新配置Tomcat的相关内存大小。

在bin/catalina.bat/sh中,在catalina.sh中添加:

JAVA_OPTS="-server -Xms1G -Xmx2G -Xss256K -Djava.awt.headless=true -Dfile.encoding=utf-8 -XX:MaxPermSize=256m -XX:PermSize=128M -XX:MaxPermSize=256M"

其中:

  • -server:启用jdk的server版本。

  • -Xms:虚拟机初始化时的最小堆内存。

  • -Xmx:虚拟机可使用的最大堆内存。 #-Xms与-Xmx设成一样的值,避免JVM因为频繁的GC导致性能大起大落

  • -XX:PermSize:设置非堆内存初始值,默认是物理内存的1/64。

  • -XX:MaxNewSize:新生代占整个堆内存的最大值。

  • -XX:MaxPermSize:Perm(俗称方法区)占整个堆内存的最大值,也称内存最大永久保留区域。

    验证

  设置成功后我们可以利用JDK自带的工具进行验证,这些工具都在JAVA_HOME/bin目录下:

​    1)jps:用来显示本地的java进程,以及进程号,进程启动的路径等。

​    2)jmap:观察运行中的JVM 物理内存的占用情况,包括Heap size , Perm size [下载地址](http://item.taobao.com/item.htm?id=41222768202) 等。

​    进入命令行模式后,进入JAVA_HOME/bin目录下,然后输入jps命令:

jps
#显示以下结果
2340 Bootstrap
6696 Jps

其中 Bootstrap进程就是我们启动了的 Tomcat,其进程号为2340.

​ 然后我们利用 jmap工具查看其内存相关配置:

jmap -heap 2340
#显示以下结果 Attaching to process ID 2340, please wait… Debugger attached successfully. Server compiler detected. JVM version is
24.65-b04 using thread-local object allocation. Parallel GC with 4 thread(s)

若内存不足,显示500错误,一般调整Xms和Xmx,可以将Xms和Xmx设置成一样

使用:set JAVA_OPTS=-Xms512m-Xmx512m

2.7.4 Tomcat 优化

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

小编真的是尽力了!!!

猜你喜欢

转载自blog.csdn.net/weixin_59663288/article/details/125021005