TOMCAT02 TOMCAT优化和Java编译工具maven, 私有仓库Nexus
Tomcat 性能优化
test.jsp --------> test_jsp.java --------> test_jsp.class #tomcat性能优化本质上就是jvm性能优化, 即优化jvm中的GC垃圾回收机制
分代堆内存GC策略
Heap堆内存分为 年轻代Young:Young Generation 伊甸园区eden: 只有一个,刚刚创建的对象 幸存(存活)区Servivor Space:有2个幸存区,一个是from区,一个是to区。大小相 等、地位相同、可互换。 from 指的是本次复制数据的源区 to 指的是本次复制数据的目标区 老年代Tenured:Old Generation, 长时间存活的对象 #实际计算中Heap没有永久代(方法区Permanent)
默认空间大小比例:
默认JVM试图分配最大内存的总内存的1/4,初始化默认总内存为总内存的1/64,年青代中heap的1/3,老 年代占2/3
永久代:JDK1.7之前使用, 即Method Area方法区,保存JVM自身的类和方法,存储JAVA运行时的环境信息, JDK1.8后 改名为 MetaSpace,此空间不存在垃圾回收,关闭JVM会释放此区域内存,此空间物理上不属于 heap内存,但逻辑上存在于heap内存 永久代必须指定大小限制,字符串常量JDK1.7存放在永久代,1.8后存放在heap中 MetaSpace 可以设置,也可不设置,无上限
范例: 查看JVM内存分配情况
# 用程序查看 [root@ubuntu ~]# cat Heap.java public class Heap { public static void main(String[] args){ //返回虚拟机试图使用的最大内存,字节单位 long max = Runtime.getRuntime().maxMemory(); //返回JVM初始化总内存 long total = Runtime.getRuntime().totalMemory(); System.out.println("max="+max+"字节\t"+(max/(double)1024)+"KB"); System.out.println("total="+total+"字节\t"+(total/(double)1024)+"KB"); } } [root@ubuntu ~]# javac Heap.java [root@ubuntu ~]# java Heap max=243269632字节 232.0MB total=16252928字节 15.5MB
GC 触发条件
#本质上,垃圾回收,都会暂停程序,等垃圾回收完成,在继续程序执行 年轻代满了,年轻代做垃圾回收。老年代满了,年轻代和老年代都做一次垃圾回收 full GC
Java 内存调整相关参数
#添加启动项,修改内存 [root@ubuntu ~]# cat /usr/local/tomcat/bin/catalina.sh ...... ...... #添加此行,使用 JAVA_OPTS 也可以 #堆内存初始值1G,最大值1G,年轻代和老年代内存比例为 1:2,eden区和幸存区的内存比例为6:1:1 CATALINA_OPTS='-Xms1g -Xmx1g -XX:NewRatio=2 -XX:SurvivorRatio=6' ...... ...... -Xms和-Xmx: 初始值和最大值生产建议设置一样(因为后续申请资源要等待时间,还能保证内存空间连续) #重启服务 [root@ubuntu ~]# systemctl restart tomcat.service #刷新状态页,再次查看内存分配情况 #说明: 年轻代为 (Eden+Survivor) #也可以直接命令下启动 [root@ubuntu ~]# java -Xms1g -Xmx1g -XX:NewRatio=2 -XX:SurvivorRatio=6 -jar abc.jar
使用 jvisualvm 监控内存 (java老版本有,新版本没有了)
[root@ubuntu ~]# apt install libxrender1 libxrender1 libxtst6 libxi6 fontconfig -y [root@ubuntu ~]# wget https://github.com/oracle/visualvm/releases/download/2.1.8/visualvm_218.zip [root@ubuntu ~]# apt install unzip
垃圾收集方式和调整策略
#jvm8默认为垃圾回收策略关注吞吐率,可以选关注交互(STW间隔短)的 # -XX:+UseConcMarkSweepGC 这个选项关注交互 #jvm8上垃圾回收策略G1属于测试阶段,jvm11开始正式使用(用上面原来的策略更稳妥些) 以后垃圾回收器配置一般不用动,现在都用G1 #jvm11 查看默认垃圾回收器配置(下面为G1) [root@ubuntu ~]# java -XX:+PrintCommandLineFlags 2>/dev/null -XX:G1ConcRefinementThreads=2 -XX:GCDrainStackTargetSize=64 - XX:InitialHeapSize=31592512 -XX:MaxHeapSize=505480192 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache - XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
范例:定位 JAVA 程序占用CPU率高的问题
#获取16进程线程ID(这里随便输入一个高占用率的线程id) [root@ubuntu ~]# printf '0x%x\n' 6869 0x1ad5 #定位代码,显示第15行(输入进程id 6852) 这里定位15行问题 [root@ubuntu ~]# jstack -l 6852 | grep -A10 0x1ad5 "Thread-0" #10 prio=5 os_prio=0 cpu=442964.71ms elapsed=444.97s tid=0x00007fc0841a4800 nid=0x1ad5 runnable [0x00007fc05c88f000] java.lang.Thread.State: RUNNABLE at CPUIntensiveTask$IntensiveTask.run(CPUIntensiveTask.java:15) at java.lang.Thread.run(java.base@11.0.22/Thread.java:834) Locked ownable synchronizers: - None "Thread-1" #11 prio=5 os_prio=0 cpu=443123.08ms elapsed=444.97s tid=0x00007fc0841a6000 nid=0x1ad6 runnable [0x00007fc05c78f000] java.lang.Thread.State: RUNNABLE at CPUIntensiveTask$IntensiveTask.run(CPUIntensiveTask.java:15)
使用图形化工具来查看JAVA进程信息
#windows中装了java环境,里面会有jconsole
jconsole 和 JMX
图形化工具,可以用来查看Java进程信息
JMX(Java Management Extensions,即Java管理扩展)是一个为JAVA应用程序、设备、系统等植入管
理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无
缝集成的系统、网络和服务管理应用。
JMX最常见的场景是监控Java程序的基本信息和运行情况,任何Java程序都可以开启JMX,然后使用
JConsole或Visual VM进行预览。
Java 程序编译
Maven 仓库使用流程
Maven 安装和配置
#maven是基于java编写的,如果没有java会顺带先安装java [root@ubuntu ~]# apt list maven Listing... Done maven/jammy 3.6.3-5 all [root@ubuntu ~]# apt install maven -y
主要配置文件
settings.xml 用来配置 maven 项目中的各种参数文件,包括本地仓库、远程仓库、私服、认证等信息
#settings.xml位置,apt和源码安装不同 /etc/maven/settings.xml #包安装(apt安装) /usr/local/maven/conf/settings.xml #源码安装 #offline 是否可以在没有网络的情况下进行编译,默认 false #如果改成true就不管了,download不下来就强制编译 # mirror 配置依赖仓库地址,可以有多个仓库配置 #镜像仓库,官方仓库找不到找镜像仓库
修改maven仓库
#修改默认仓库为国内源,在 mirrors 标签内添加阿里源配置 [root@ubuntu ~]# vim /usr/local/maven/conf/settings.xml <mirror> <id>aliyunmaven</id> <mirrorOf>*</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror> </mirrors>
Maven 命令和编译流程
项目编译范例
#clone 代码到本地 [root@ubuntu ~]# git clone https://gitee.com/lbtooth/spring-boot-helloworld.git [root@ubuntu spring-boot-helloworld]#ls deploy Dockerfile Jenkinsfile LICENSE pom.xml README.md sonar-project.properties src #pom.xml通常是开发写给运维的,运维直接跑就行 #当前还没有 .m2 目录(说明没有编译过) [root@ubuntu ~]# ls .m2 ls: cannot access '.m2': No such file or directory #开始编译,清理旧的编译内容,重新构建,并跳过测试(注意目录,要有pom.xml) [root@ubuntu spring-boot-helloworld]# mvn clean package -Dmaven.test.skip=true #clean曾经编译过就清理下 -Dmaven.test.skip=true跳过测试(默认会测),java有单元测试的习惯 #编译完出现.m2文件, 依赖包都下载到本地了 [root@ubuntu ~]# ls .m2 repository [root@ubuntu ~]# du -sh .m2 52M .m2 #编译完原项目目录多target文件夹 [root@ubuntu spring-boot-helloworld]# ls deploy Dockerfile Jenkinsfile LICENSE pom.xml README.md sonar-project.properties src target ##spring-boot-helloworld-0.9.0-SNAPSHOT.jar 就是构建好的目标(pom文件中定义了是jar包) [root@ubuntu target]#ls classes maven-archiver spring-boot-helloworld-0.3-SNAPSHOT.jar generated-sources maven-status spring-boot-helloworld-0.3-SNAPSHOT.jar.original #直接启动java项目即可 [root@ubuntu spring-boot-helloworld]#java -jar target/spring-boot-helloworld-0.3-SNAPSHOT.jar #运行,如果需要修改端口,可加 --server.port=8888 [root@ubuntu spring-boot-helloworld]#java -jar target/spring-boot-helloworld-0.3-SNAPSHOT.jar --server.port=8888 # 在浏览器中测试 # http://10.0.0.208:8080/ # http://10.0.0.208:8080/version # http://10.0.0.208:8080/hello #在有依赖缓存的情况下再次编译 [root@ubuntu ~]# cd spring-boot-helloworld/ #打包成功,耗时 1-2秒 [root@ubuntu spring-boot-helloworld]# mvn clean package -Dmaven.test.skip=true #如果把 .m2 文件删了,再次编译要重新下载,耗时和第一次一样比较久
项目编译范例
[root@ubuntu ~]# git clone https://gitee.com/JPressProjects/jpress.git [root@ubuntu ~]# cd jpress/ #当前项目中有大量的 pom.xml,可以分开编译 [root@ubuntu jpress]# find -name "pom.xml" #整体编译 [root@ubuntu jpress]# mvn clean package -Dmaven.test.skip=true [root@ubuntu jpress]# find -name "*war" ./starter-tomcat/target/starter-tomcat-5.0.war [root@ubuntu jpress]# ls starter-tomcat/target/ classes maven-archiver starter-tomcat-5.0 starter-tomcat-5.0-classes.jar starter-tomcat-5.0.war #在tomcat 中部署 [root@ubuntu jpress]# cat /usr/local/tomcat/conf/server.xml <Host name="java.m99-magedu.com" appBase="/data/webapps" unpackWARs="true" autoDeploy="true"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="java.m99-magedu.com_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b %{x-forwarded-for}i" /> </Host> [root@ubuntu jpress]# cp starter-tomcat/target/starter-tomcat-5.0.war /data/webapps/jpress.war #己经自动部署 [root@ubuntu jpress]# ls /data/webapps/ jpress jpress.war ROOT #在浏览器中访问 # http://java.m99-magedu.com:8080/jpress
私有仓库 Nexus
Nexus 是一个强大的 Maven 和其它仓库的管理器,它极大地简化了自己内部仓库的维护和外部仓库的 访问。
Nexus 安装
#基于java开发,内存不得小于4g,否则Nexus Server起不来 Nexus Server 既是仓库又是代理,如果没有,会去阿里等仓库拉取 #安装 Nexus,并配置 Maven 仓库 [root@ubuntu ~]#apt install openjdk-11-jdk #可能需要fanqiang下载 [root@ubuntu ~]# wget https://download.sonatype.com/nexus/3/nexus-3.70.1-02-java11-unix.tar.gz [root@ubuntu1804 ~]#tar xf nexus-3.29.2-02-unix.tar.gz -C /usr/local/ [root@ubuntu1804 ~]#ln -s /usr/local/nexus-3.29.2-02/ /usr/local/nexus [root@ubuntu1804 ~]#ln -s /usr/local/nexus/bin/nexus /usr/bin/ [root@ubuntu1804 ~]#file /usr/local/nexus/bin/nexus bin/nexus: POSIX shell script, ASCII text executable, with very long lines #指定运行身份 [root@ubuntu1804 ~]#vim /usr/local/nexus/bin/nexus.rc run_as_user="root" #查看配置文件,可以在此文件中修改端口等配置 [root@ubuntu1804 ~]#cat /usr/local/nexus/etc/nexus-default.properties ## DO NOT EDIT - CUSTOMIZATIONS BELONG IN $data-dir/etc/nexus.properties ## # Jetty section application-port=8081 application-host=0.0.0.0 nexus-args=${jetty.etc}/jetty.xml,${jetty.etc}/jettyhttp.xml,${jetty.etc}/jetty-requestlog.xml nexus-context-path=/ # Nexus section nexus-edition=nexus-pro-edition nexus-features=\ nexus-pro-feature nexus.hazelcast.discovery.isEnabled=true #查看JVM配置文件 [root@ubuntu1804 ~]#cat /usr/local/nexus/bin/nexus.vmoptions -Xms2703m -Xmx2703m ...... #前台运行 [root@ubuntu1804 ~]#nexus run ------------------------------------ #注意: 如果内存太小,可能会出现下面提示错误 [root@ubuntu1804 ~]#nexus run WARNING: ************************************************************ WARNING: Detected execution as "root" user. This is NOT recommended! WARNING: ************************************************************ OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000717000000, 1890582528, 0) failed; error='Cannot allocate memory' (errno=12) #后台运行 [root@ubuntu1804 ~]#nexus start #查看状态 [root@ubuntu1804 ~]#nexus status #停止服务 [root@ubuntu1804 ~]#nexus stop #查看端口 [root@ubuntu1804 ~]#ss -ntlp|grep java LISTEN 0 50 0.0.0.0:8081 0.0.0.0:* users:(("java",pid=20564,fd=798)) LISTEN 0 1 127.0.0.1:38147 0.0.0.0:* users:(("java",pid=20564,fd=125)) #创建service文件 #参考链接https://help.sonatype.com/repomanager3/installation/system-requirements [root@ubuntu1804 ~]#cat /lib/systemd/system/nexus.service [Unit] Description=nexus service After=network.target
[Service] Type=forking LimitNOFILE=65536 ExecStart=/usr/local/nexus/bin/nexus start ExecStop=/usr/local/nexus/bin/nexus stop User=root #User=nexus Restart=on-abort
[Install] WantedBy=multi-user.target [root@ubuntu1804 ~]#systemctl daemon-reload [root@ubuntu1804 ~]#systemctl enable --now nexus.service [root@ubuntu1804 ~]#tail /var/log/syslog
登录 Web 界面初始化
http://nexus主机:8081/
配置 Maven 仓库
#proxy模式 问Nexus要,如Nexus没有,就从其他地方下,自己保存下次就有了(指向设置国内源) #hosted模式 问Nexus要,如果没有就没有了 #group模式 多个仓库的组合