tomcat

本文最后更新于:2023年12月5日 晚上

JAVA 基础

Java 动态网页技术

servlet

就是前端代码和 java 代码混合的 java 文件,hello.java 示例:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorld extends HttpServlet {
  private String message;

  public void init() throws ServletException {
    message = "Hello World";
  }

  public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOException {
    response.setContentType("text/html"); //响应报文内容类型
    PrintWriter out = response.getWriter();  //构建响应报文内容
    out.println("<h1>" + message + "</h1>");
    out.println("<p><a href=http://blog.lujinkai.cn/>LJKのBlog</a>欢迎你</p>");
 }

  public void destroy(){}

}

jsp (Java Server Pages)

jsp 是 html 模板,后缀是 jsp,hello.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("你的 IP 地址 " + request.getRemoteAddr());
    %>
</body>
</html>

浏览器访问 jsp 文件:

  1. tomcat 会将 .jsp 文件转为 .java 文件(servlet 类文件)
  2. servlet 类文件转为字节码文件(class 文件),
  3. jvm 解析执行 class 文件

例如:

hello.jsp --> hello.java --> hello.class --> jvm执行hello.class

JDK

JRE:Java Runtime Environment,指 Java 运行时环境, 包含 JVM + Java 核心类库
JDK:Java Development Kit,即 Java 语言的软件开发工具包,JDK 协议基于 JRL(JavaResearch License)协议

简单地说,JRE 就是运行 Java 字节码的虚拟机。但是,如果只有 Java 源码,要编译成 Java 字节码,就需要 JDK,因为 JDK 除了包含 JRE,还提供了编译器、调试器等开发工具。

Oracle JDK

JDK 有很多版本,Oracle JDK 是官方版,也是用的最多的

Oracle JDK 早期称为 J2SE(标准版)、J2EE(企业版)、J2ME(微型版),现在叫做 JavaEE

早期的 Oracle JDK 的版本以 1.x 的方式命令:JDK 1.0、JDK 1.1、JDK 1.2、JDK 1.3、JDK 1.4

2004 年 9 月 30 日 18:00PM,J2SE1.5 发布,是 Java 语言的发展史上的又一里程碑事件。为了表示这个版本的重要性,J2SE1.5 更名为 J2SE5.0

2005 年 6 月,JavaOne 大会召开,SUN 公司公开 Java SE 6。此时,Java 的各种版本已经更名以取消其中的数字”2”:J2EE 更名为 Java EE, J2SE 更名为 Java SE,J2ME 更名为 Java ME

Open JDK

Oracle JDK 收费,OpenJDK 是第三方版本的 JDK 中最常用的

OpenJDK 7 是基于 JDK7 的 beta 版开发,但为了也将 Java SE 6 开源,从 OpenJDK7 的 b20 构建反向分支开发,从中剥离了不符合 Java SE 6 规范的代码,发布 OpenJDK 6。所以 OpenJDK6 和 JDK6 没什么关系,只是 API 兼容而已

相对来说,Oracle jDK 具有更好的响应能力和 JVM 性能,更加稳定

Tomcat 基础

tomcat 既是轻量级 web 服务器,可以处理 http 请求,又是 servlet 容器,可以处理 java 程序

tomcat 使用 java 开发,所以依赖 JRE 或者 JDK 环境

安装/升级/回滚

安装

# 下载二进制包
[root@c71 src]$wget https://mirrors.bfsu.edu.cn/apache/tomcat/tomcat-8/v8.5.61/bin/apache-tomcat-8.5.61.tar.gz
# 无需编译,解压即可使用
[root@c71 src]$tar zxf apache-tomcat-8.5.61.tar.gz
[root@c71 src]$mv apache-tomcat-8.5.61 /usr/local/tomcat
# 创建tomcat用户
[root@c71 src]$useradd -r -s /sbin/nologin tomcat
[root@c71 src]$chown -R tomcat:tomcat /usr/local/tomcat/
[root@c71 src]$cd /usr/local/tomcat/
[root@c72 bin]$ll catalina.sh startup.sh shutdown.sh
-rwxr-x--- 1 tomcat tomcat 25121 Jan 30 17:19 catalina.sh # 管理tomcat脚本
-rwxr-x--- 1 tomcat tomcat  1902 Jan 30 17:19 shutdown.sh # 停止tomcat脚本
-rwxr-x--- 1 tomcat tomcat  1904 Jan 30 17:19 startup.sh # 启动tomcat脚本
# 提供tomcat所需的环境变量
[root@c71 tomcat]$echo "JAVA_HOME=/usr/local/jdk" >/usr/local/tomcat/conf/tomcat.conf
# 创建tomcat.service文件
[root@c71 tomcat]$vim /usr/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/tomcat/conf/tomcat.conf
ExecStart=/usr/local/tomcat/bin/startup.sh
ExecStop=/usr/local/tomcat/bin/shutdown.sh
PrivateTmp=true
User=tomcat
Group=tomcat

[Install]
WantedBy=multi-user.target
# 设置开机自启
[root@c71 tomcat]$systemctl daemon-reload
[root@c71 tomcat]$systemctl enable --now tomcat

升级和回滚

安装新的 tomcat 版本,然后删除旧的软连接,创建新的软连接,最后重启服务

tomcat 的文件结构和组成

目录 说明
bin 服务启动、停止等相关程序和文件
conf 配置文件
lib 库目录
logs 日志目录
webapps 应用程序,应用部署目录
work jsp 编译后的结果文件,建议提前预热访问

配置文件

文档:http://tomcat.apache.org/tomcat-8.5-doc/index.html

文件名 说明
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 日志系统相关的配置,可以修改日志级别和日志路径等

注意:配置文件大小写敏感

日志

参考文档: https://cwiki.apache.org/confluence/display/TOMCAT/Logging
日志格式: https://tomcat.apache.org/tomcat-9.0-doc/config/valve.html#Access_Logging

日志文件位于 logs 目录下,日志的格式在 conf/server.xml 中定义

[root@c72 tomcat]$vim conf/server.xml
...
164         <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
165                prefix="localhost_access_log" suffix=".txt"
166                pattern="%h %l %u %t &quot;%r&quot; %s %b" />
...

组件

每一个组件都一个对象,这些组件大体可分为以下几个类型:

  • 顶级组件:Server,代表整个 Tomcat 容器;

  • 服务类组件:Service,可以创建多个 Service,但一般也只创建一个,用来组织 Engine 和 Connector 的对应关系,一个 service 中只有一个 Engine;

  • 连接器组件:Connector,负责客户端的 HTTP、HTTPS、AJP 等协议连接。一个 Connector 只属于某一个 Engine;

    AJP(Apache Jserv protocol)是一种基于 TCP 的二进制通讯协议

  • 容器类组件:Engine、Host、Context 都是容器类组件;

    • Engine:引擎,真正的处理请求的入口,其内部定义多个虚拟主机 Host。一个 Engine 上可以绑定多个 Connector
    • Host:虚拟主机
    • Context:应用的上下文,配置特定 url 路径映射和目录的映射关系:url => directory
  • 内嵌类组件:valve、logger、realm、loader、manager 等。可以内嵌到其他组件内,在不同容器组件内分别定义。

  • 集群类组件:listener、cluster

多个组件关系 /conf/server.xml 示例:

<?xml version='1.0' encoding='utf-8'?>
<Server port="8015" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener" />
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
  <GlobalNamingResources>
    <Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase" />
  </GlobalNamingResources>
  <Service name="Catalina">
    <Connector connectionTimeout="20000" port="6804" protocol="HTTP/1.1" redirectPort="8443" />
    <Engine defaultHost="localhost" name="Catalina">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase" />
      </Realm>
      <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log" suffix=".txt" />
      </Host>
    </Engine>
  </Service>
</Server>

tomcat 处理请求过程

假设来自客户的请求为:http://localhost:8080/test/index.jsp

  1. 浏览器端的请求被发送到服务端端口 8080,Tomcat 进程监听在此端口上。通过侦听的 HTTP/1.1 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,匹配它所拥有的所有 Context
  6. Host 匹配到路径为/test 的 Context
  7. path=/test 的 Context 获得请求 index.jsp,在它的 mapping table 中寻找对应的 servlet
  8. Context 匹配到 URL PATTERN 为 *.jsp 的 servlet,对应于 JspServlet 类构造 HttpServletRequest 对象和 HttpServletResponse 对象,作为参数调用 JspServlet 的 doGet 或 doPost 方法。
  9. Context 把执行完了之后的 HttpServletResponse 对象返回给 Host
  10. Host 把 HttpServletResponse 对象返回给 Engine
  11. Engine 把 HttpServletResponse 对象返回给 Connector
  12. Connector 把 HttpServletResponse 对象返回给浏览器端

应用部署

默认网站根目录是 webapps,其下的每个目录都是一个 WebApp

[root@c72 tomcat]$pwd
/usr/local/tomcat
[root@c72 tomcat]$ll webapps/  # 默认网站根目录
total 24
drwxr-x--- 2 tomcat tomcat 4096 Jan 30 22:50 docs
drwxr-x--- 7 tomcat tomcat 4096 Jan 30 22:50 examples
drwxr-x--- 6 tomcat tomcat 4096 Jan 30 17:19 host-manager
drwxr-x--- 6 tomcat tomcat 4096 Jan 30 17:19 manager
drwxr-x--- 3 tomcat tomcat 4096 Jan 30 22:52 ROOT  # 网站默认根目录

JSP WebApp 目录结构

目录结构一般由开发用工具自动生成

index.html # 主页配置:默认按以下顺序查找主页文件 index.html,index.htm、index.jsp
WEB-INF\ # 当前目录WebApp的私有资源路径,通常存储当前应用使用的web.xml和context.xml配置文件
META-INF\ # 类似于WEB-INF,也是私有资源的配置信息,和WEB-INF/目录一样浏览器无法访问
classes\ # 类文件,当前webapp需要的类
lib\  # 当前应用依赖的jar包

主页设置

  • 全局配置:修改 conf/web.xml 中的 <welcome-file-list> 标签,配置修改后,需要重启 tomcat

    # 略...
  • WebApp 的专用配置文件:以 ROOT 为例,将上面主配置文件 conf/web.xml 中的 <welcome-file-list> 标签,复制到 webapps/ROOT/WEB-INF/web.xml 中,配置修改后,无需重启 tomcat 服务

    [root@c72 tomcat]$vim webapps/ROOT/WEB-INF/web.xml
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" metadata-complete="true">
        <display-name>Welcome to Tomcat</display-name>
      <description>Welcome to Tomcat</description>
        <welcome-file-list>
            <welcome-file>index.html</welcome-file>  <!-- 调整三个文件顺序 -->
            <welcome-file>index.htm</welcome-file>
            <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>
    </web-app>
    [root@c72 tomcat]$chown -R tomcat:tomcat ./webapps/ROOT/* # 不要忘记修改属主

应用部署实现

WebApp 应用的归档格式.war.jar

  • .war:WebApp 打包,类 zip 格式文件,通常包括一个应用的所有资源,比如 jsp、html、配置文件等
  • .jar:EJB 类文件的打包压缩类 zip 格式文件,包括很多的 class 文件,网景公司发明

部署方式:自动部署 和 手动部署

  • 自动部署:Tomcat 一旦发现多了一个 web 应用 APP.war 包,默认会自动把它解压缩,加载并启动起来

    # conf/server.xml中文件配置
    <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
  • 手动部署:冷部署 和 热部署

    • 冷部署:将 webapp 放到指定目录,才去启动 Tomcat 服务
    • 热部署:Tomcat 服务不停止,需要依赖 manager、ant 脚本、tcd(tomcat client deployer)等工具
  • 反部署 undeploy:停止 webapp 运行,并从 JVM 上清除已经加载的类,从 Tomcat 应用目录中移除部署的文件

  • 启动 start:是 webapp 能够访问

  • 停止 stop:webapp 不能访问,不能提供服务,但是 JVM 并不清除它

部署基于 JAVA 的博客系统 JPress
[root@c72 webapps]$pwd
/usr/local/tomcat/webapps
[root@c72 webapps]$mv /usr/local/src/jpress-v3.3.0.war ./
[root@c72 webapps]$ln -s jpress-v3.3.0 jpress # 创建软连接,方便以后升级
[root@c72 webapps]$ll
total 70024
drwxr-x--- 2 tomcat tomcat     4096 Jan 30 22:50 docs
drwxr-x--- 7 tomcat tomcat     4096 Jan 30 22:50 examples
drwxr-x--- 6 tomcat tomcat     4096 Jan 30 17:19 host-manager
lrwxrwxrwx 1 root   root         13 Jan 31 11:35 jpress -> jpress-v3.3.0
drwxr-x--- 6 tomcat tomcat     4096 Jan 31 11:35 jpress-v3.3.0
-rw-r--r-- 1 root   root   71677863 Jan 31 11:31 jpress-v3.3.0.war
drwxr-x--- 6 tomcat tomcat     4096 Jan 30 17:19 manager
drwxr-x--- 6 tomcat tomcat     4096 Jan 30 23:01 ROOT
基于 WEB 的管理 Server status 和 Manager APP 实现应用部署

默认的管理页面被禁用,启用方法如下:

# server.xml中Resource的pathname参数指定了授权用户信息的文件:conf/tomcat-users.xml

[root@c72 conf]$vim tomcat-users.xml
#加下面两行,指定用户和密码
<role rolename="manager-gui"/>
<user username="admin" password="123456" roles="manager-gui"/>

[root@c72 manager]$pwd
/usr/local/tomcat/webapps/manage
[root@c72 manager]$vim META-INF/context.xml
# 当前访问地址是10.0.0.72,可以修改正则表达式为
allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1|10\.0\.0\.\d+"

基于 WEB 应用程序管理器实现 APP 的部署

访问 /manager 即可

常见配置详解

端口 8005/tcp 安全配置管理
$ss -ntlp | grep 8005
LISTEN   0   1    [::ffff:127.0.0.1]:8005    [::]:*    users:(("java",pid=3117,fd=66))

tomcat 默认监听 8005 端口,这是一个后门,可通过这个端口关闭 tomcat 服务

$nc 127.0.0.1 8005  # 连接到本机的8005端口
SHUTDOWN    # 输入SHUTDOWN,关闭tomcat
# server.xml
<Server port="8005" shutdown="SHUTDOWN"> # 配置后门的端口,和关闭服务的指令

此行不能被注释,否则无法启动 tomcat 服务

此管理功能建议禁用:

  • 将 SHUTDOWN 改为一串猜不出的字符串实现
  • port 修改成 0, 会使用随机端口,如:36913
  • port 设为-1 等无效端口,将关闭此功能
显示指定的 http 服务器版本信息

默认不显示 tomcat 的 http 的 Server 头信息, 可以指定 tomcat 的 http 的 Server 头信息

示例:冒充 nginx 服务器

# 给Connector标签添加Server参数
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" Server="nginx" />
# 重启tomcat
systemctl restart tomcat

其他配置

server.xml 中可以配置 service、connector、Engine、Host 等

# 一般情况下,一个Server实例配置一个Service,name属性相当于该Service的ID
<Service name="Catalina">
# redirectPort:如果访问HTTPS协议,自动转向这个连接器。但大多数时候,Tomcat并不会开启HTTPS,因为Tomcat往往部署在内部,HTTPS性能较差
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
# 引擎配置
<Engine name="Catalina" defaultHost="localhost">
# defaultHost配置,默认localhost。
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
多虚拟主机配置
# name:相当于 nginx 的 server_name
# appBase:网站根目录,可以使用相对路径或绝对路径
# unpackWARs:是否自动解压 war 格式
# autoDeploy:热部署,自动加载并运行应用
<Host name="test.ljk.cn" appBase="/data/wwwroot/test.ljk.cn" unpackWARs="true" autoDeploy="true">
 # className
 # directory:日志目录,可以使用相对路径或绝对路径
 # 日志文件:prefix.2021-01-31.suffix
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="/data/wwwlogs" prefix="test.ljk.cn_access_log" suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>

# 注意日志的权限问题
chown -R tomcat:tomcat /data/wwwlogs

[root@c72 test.ljk.cn]$pwd
/data/wwwroot/test.ljk.cn
[root@c72 test.ljk.cn]$cat ./ROOT/index.html  # 注意目录结构
hello test.ljk.cn

基于 web 方式的 Host Manager 虚拟主机管理

略…

Context 配置

Context 类似 Nginx 中的 location

# path
# docBase:相对、绝对路径均可
# reloadable:WEB-INF/classes或META-INF/lib目录下.class文件有改动,自动重载,生产建议关闭
<Context path="/test" docBase="/data/test" reloadable="true" />

# 相当于 Nginx 中:
location /test {
    alias /data/test;
}

还可以单独添加日志配置:

<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>

Valve 组件

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" />

# className
# 定义访问日志 org.apache.catalina.valves.AccessLogValve
# 定义访问控制 org.apache.catalina.valves.RemoteAddrValve
<Valve className="org.apache.catalina.valves.RemoteAddrValve" deny="10\.0\.0\.\d+"/>

反向代理和负载均衡

tomcat 的网络并发性能比较差,所以通常只用 tomcat 解析动态页面

两种常见的部署方式:

nginx 监听 80 端口,tomcat 监听 8080 端口,nginx 负责将请求转发给 tomcat 处理

反向代理:

server {
    listen 80;
    server_name to2b.cn;
    access_log off;

    location / {
        proxy_pass http://to2b.cn:8080;  # 反向代理
    }
}

负载均衡:

略…

Tomcat Session Replication Cluster

Tomcat 官方实现了 Session 的复制集群,将每个 Tomcat 的 Session 进行相互的复制同步,从而保证所有 Tomcat 都有相同的 Session 信息

这种同步 session 的方式缺点很明显,就是占用内存,所以不适用于 tomcat 服务器太多的情况,如果只有两三台,可以使用

session 共享服务器 msm

msm:memcached session manager,将 Tomcat 的 session 保持到 memcached 或 redis,实现高可用

github 网站链接: https://github.com/magro/memcached-session-manager

安装

参考链接:https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration

  1. 下载各种包

    注意:直接点击会报错 501,这时因为 maven 仓库废弃了 http 访问,手动更改为 https 即可

  2. 将相关的 jar 文件都放到 Tomcat 的 lib 目录中即可

    # Tomcat的Session管理类
    memcached-session-manager-2.3.2.jar
    memcached-session-manager-tc8-2.3.2.jar
    
    # 驱动类,memcached(spymemcached.jar)、Redis(jedis.jar)
    spymemcached-2.12.3.jar
    
    # Session数据的序列化、反序列化类,官方推荐 kryo
    msm-kryo-serializer-2.3.2.jar
    kryo-serializers-0.45.jar
    kryo-3.0.3.jar
    minlog-1.3.1.jar
    reflectasm-1.11.9.jar
    asm-5.2.jar
    objenesis-2.6.jar
    [root@c72 lib]$pwd
    /usr/local/tomcat/lib
    [root@c72 lib]$wget https://repo1.maven.org/maven2/de/javakaffee/msm/memcached-session-manager/2.3.2/memcached-session-manager-2.3.2.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/de/javakaffee/msm/memcached-session-manager-tc8/2.3.2/memcached-session-manager-tc8-2.3.2.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/net/spy/spymemcached/2.12.3/spymemcached-2.12.3.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/de/javakaffee/msm/msm-kryo-serializer/2.3.2/msm-kryo-serializer-2.3.2.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/de/javakaffee/kryo-serializers/0.45/kryo-serializers-0.45.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/com/esotericsoftware/kryo/3.0.3/kryo-3.0.3.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/org/ow2/asm/asm/5.2/asm-5.2.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/org/objenesis/objenesis/2.6/objenesis-2.6.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/com/esotericsoftware/reflectasm/1.11.9/reflectasm-1.11.9.jar
    [root@c72 lib]$wget https://repo1.maven.org/maven2/com/esotericsoftware/minlog/1.3.1/minlog-1.3.1.jar

sticky 模式

前端 tomcat 和后端 memcached**有关联(粘性)**关系

略…

non-sticky 模式

前端 tomcat 和后端 memcached**无关联(无粘性)**关系,msm 1.4.0 之后版本开始支持

Tomcat session 为中转 Session,对每一个 SessionID 随机选中后端的 memcached 节点 n1(或者 n2)为主 session,而另一个 memcached 节点 n2(或者是 n1)为备 session。产生的新的 Session 会发送给主、备 memcached,并清除本地 Session

如果 n1 下线,n2 则转正。n1 再次上线,n2 依然是主 Session 存储节点

实战案例: memcached 实现 non-sticky 模式

ip 服务 安装软件
10.0.0.71 tomcat1、memcached jdk8、tomcat8、memcached
10.0.0.72 tomcat1、memcached jdk8、tomcat8、memcached
10.0.0.73 负载均衡调度器 haproxy 或者 nginx

负载均衡调度器的安装配置过程:略…

10.0.0.71 和 10.0.0.72 做以下配置:

  1. 安装配置 memcached

    $yum install memcached
    $vim /etc/sysconfig/memcached
    PORT="11211"
    USER="memcached"
    MAXCONN="1024"
    CACHESIZE="64"
    OPTIONS=""
  2. 配置 tomcat 的 context.xml

    <Context>
     ...
     <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
      memcachedNodes="n1:10.0.0.72:11211,n2:10.0.0.73:11211"
      sticky="false"
      sessionBackupAsync="false"
      lockingMode="uriPattern:/path1|/path2"
      requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
      transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory" />
    </Context>

实战案例: redis 实现 non-sticky 模式

参考:https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration#example-for-non-sticky-sessions--kryo--redis

Session 问题方案总结

  1. session 绑定,基于 IP 或 session cookie 的。其部署简单,尤其基于 session 黏性的方式,粒度小,对负载均衡影响小。但一旦后端服务器有故障,其上的 session 丢失
  2. session 复制集群,基于 tomcat 实现多个服务器内共享同步所有 session。此方法可以保证任意一台后端服务器故障,其余各服务器上还都存有全部 session,对业务无影响。但是它基于多播实现心跳,TCP 单播实现复制,当设备节点过多,这种复制机制不是很好的解决方案。且并发连接多的时候,单机上的所有 session 占据的内存空间非常巨大,甚至耗尽内存
  3. session 服务器,将所有的 session 存储到一个共享的内存空间中,使用多个冗余节点保存 session,这样做到 session 存储服务器的高可用,且占据业务服务器内存较小。是一种比较好的解决 session 持久的解决方案

以上的方法都有其适用性。生产环境中,应根据实际需要合理选择

Tomcat 性能优化

tomcat 是运行在 jvm 虚拟机上的一个程序,所以 tomcat 的性能和 jvm 密切相关,所以要想优化 tomcat 的性能,除了优化 tomcat 本身的设置,还需要优化 jvm

JVM 组成

$java -version
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)

JVM:Java Virtual Machine,java 虚拟机

HotSpot 实现了 Java 虚拟机规范,java 虚拟机有好几种,HotSpot 是最主流的一种

  • 类加载子系统:将 java 代码编译成字节码文件,将所需所有类加载到内存,必要时将类实例化成实例
  • 运行时数据区:最消耗内存的空间,需要优化
  • 执行引擎:包括 JIT (JustInTimeCompiler)即时编译器,GC (Garbage Collector)垃圾回收器
  • 本地方法接口:通过 JNI(Java Native Interface)调用 C、C++ 库其他语言,扩展 Java 功能

运行时数据区的heap(堆)是我们关注的重点,这部分可以通过修改设置进行优化,其他部分没有优化的途径

  • MetaSpace:元空间,线程共享,存放已加载的类信息(构造方法、接口定义)、常量(final)、静态变量(static)、运行时常量池等。但实例变量存放在堆内存中。JDK8 之前叫 Method Area(方法区)
  • heap:堆,线程共享,虚拟机启动时创建,存放创建的所有对象信息。如果对象无法申请到可用内存将抛出 OOM 异常。堆是靠 GC 垃圾回收器管理的,通过 -Xmx -Xms 指定最大堆和最小堆空间大小
  • Java stack:java 栈,线程私有,每个线程会分配一个栈,存放 java 中 8 大基本数据类型、对象引用、实例的本地变量、方法参数和返回值等,基于 FILO()(First In Last Out),每个方法为一个栈帧
  • Program Counter Register:PC 寄存器,线程私有,就是一个指针,指向方法区中的方法字节码,每一个线程用于记录当前线程正在执行的字节码指令地址。由执行引擎读取下一条指令。因为线程需要切换,当一个线程被切换回来需要执行的时候,需要知道执行到哪里
  • Native Method stack:本地方法栈,线程私有,为本地方法执行构建的内存空间,存放本地方法执行时的局部变量、操作数等
    所谓本地方法,即使用 native 关健字修饰的方法,比如 Thread.sleep 方法。简单的说是非 Java 实现的方法,例如操作系统的 C 编写的库提供的本地方法,Java 调用这些本地方法接口执行。但是要注意,本地方法应该避免直接编程使用,因为 Java 可能跨平台使用,如果用了 Windows API,换到了 Linux 平台部署就有了问题

GC (Garbage Collection) 垃圾收集器

垃圾回收算法

  • 标记-清除 Mark-Sweep
  • 标记-压缩 Mark-Compact
  • 复制 Copying

分代堆内存 GC 策略

JVM 内存空间划分为 heap 区和 非 heap 区

heap 内存空间:

  • Eden Space:伊甸园区
  • Survivor Space:幸存者区,分为两部分,一个是 from 区,一个是 to 区。大小相等、地位相同、可互换
    • from:指的是本次复制数据的源区
    • to:指的是本次复制数据的目标区
  • Tenured Gen:养老区 或 老年代

规律:一般情况 99%的对象都是临时对象

默认空间大小比例:

非 heap 区内存划分:

  • Code Cache
  • Compressed Class Space
  • Metaspace

Minor GC

通常场景下,大多数对象都不会存活很久,而且创建活动非常多,伊甸园区和幸存者区的垃圾回收就非常频繁,所以采用 复制 Copying 算法

  1. 所有新建对象(特大对象直接进入养老区)都出生在 eden
  2. 当 eden 满了,启动 GC。这个称为 Young GC 或者 Minor GC,标记 eden 存活对象,将存活对象复制到幸存者者区(两个幸存者区之一),GC 完成
  3. 当 eden 满了,再次启动 GC,标记 eden 和幸存者区的存活对象,将存活对象复制到另一个幸存者区,GC 完成

以后就重复上面的步骤,每当 eden 满了,就将其中的存货对象和幸存者区(from 区)的存货对象复制到另一个幸存者区(to 区)

但是,如果一个对象一直存活,它最后就在 from、to 来回复制,如果 from 区中对象复制次数达到阈值(默认 15 次,CMS 为 6 次,可通过 java 的选项 -XX:MaxTenuringThreshold=N 指定),就直接复制到养老区

Major GC

进入养老区的数据较少,所以养老区被占满的速度较慢,所以垃圾回收也不频繁,所以较常采用 标记-压缩 Mark-Compact 算法

如果养老区也满了,会触养老区 GC,称为 Old GC 或者 Major GC

因为 Major GC 会对整个 heap 内存空间进行垃圾回收,影响所有的区域,所以 Major GC 又叫 FullGC

java 内存调整相关参数

Java 命令行参考文档: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

选项分类:

-选项名称  # 标准选项,所有HotSpot都支持
-X选项名称  # 稳定的非标准选项
-XX:选项名称 # 非标准的不稳定选项,下一个版本可能会取消

常用选项:

-Xms    # 设置堆内存初始大小,示例:-Xms2g
-Xmx    # 设置堆内存上限,示例:-Xms4g
-XX:NewSize   # 设置伊甸园区和幸存者区初始大小,示例:-XX:NewSize=128m
-XX:MaxNewSize  # 设置伊甸园区和幸存者区上限,示例:-XX:MaxNewSize=256m
-Xmnsize   # 同时设置-XX:NewSize 和 -XX:MaxNewSize,示例:-Xmn1g
-XX:NewRatio  # 以比例方式设置新生代和老年代,示例:-XX:NewRatio=2,即new/old=1/2
-XX:SurvivorRatio # 以比例方式设置eden和survivor,示例:-XX:SurvivorRatio=6,即eden/survivor=6/1、new/survivor=8/1
-Xss    # 设置每个线程私有的栈空间大小,依据具体线程大小和数量,示例:-Xss256k

注意:Xms 和 Xmx 通常设置为相同的值,防止内存碎片化

垃圾收集方式

回收线程 数:GC 线程串行(serial)还是并行(parallel)

  • 串行垃圾回收器:一个 GC 线程完成回收工作
  • 并行垃圾回收器:多个 GC 线程同时一起完成回收工作,充分利用 CPU 资源

工作模式 不同:GC 线程是否和工作线程一起运行

  • 独占垃圾回收器:只有 GC 在工作,STW 一直进行到回收完毕,工作线程才能继续执行

  • 并发垃圾回收器:GC 线程垃圾回收某些阶段可以和工作线程一起进行,如标记阶段并行,回收阶段仍然串行

调整策略

对 JVM 调整策略应用极广

  • 在 WEB 领域中 Tomcat 等

  • 在大数据领域 Hadoop 生态各组件

  • 在消息中间件领域的 Kafka 等

  • 在搜索引擎领域的 ElasticSearch、Solr 等

注意:在不同领域和场景对 JVM 需要不同的调整策略

  • 减少 STW 时长,串行变并行
  • 减少 GC 次数,要分配合适的内存大小

一般情况下,我们大概可以使用以下原则:

  • 客户端或较小程序,内存使用量不大,可以使用串行回收
  • 对于服务端大型计算,可以使用并行回收
  • 大型 WEB 应用,用户端不愿意等待,尽量少的 STW,可以使用并发回收

垃圾回收器

常用垃圾回收器

ParNew:parallel new,并行处理新生代

按分代设置不同垃圾回收器:

新生代:

  • 新生代串行收集器 Serial
  • 新生代并行回收收集器 PS(Parallel Scavenge)
  • 新生代并行收集器 ParNew

老年代:

  • 老年代串行收集器 Serial Old
  • 老年代并行回收收集器 Parallel Old
  • CMS(Concurrent Mark Sweep 并发标记清除算法)收集器

以下收集器不再按明确的分代单独设置:

  • G1(Garbage First)收集器
    • 最新垃圾回收器,从 JDK1.6 实验性提供,JDK1.7 发布,其设计目标是在多处理器、大内存服务器端提供优于 CMS 收集器的吞吐量和停顿控制的回收器。JDK9 将 G1 设为默认的收集器,建议 JDK9 版本以后使用
    • 基于 标记压缩 算法,不会产生大量的空间碎片,有利于程序的长期执行
    • 分为 4 个阶段:初始标记、并发标记、最终标记、筛选回收。并发标记并发执行,其它阶段 STW 只有 GC 线程并行执行
    • G1 收集器是面向服务端的收集器,它的思想就是首先回收尽可能多的垃圾(这也是 Garbage-First 名字的由来)
    • G1 能充分的利用多 CPU,多核环境下的硬件优势,使用多个 CPU 来缩短 STW 停顿的时间 (10ms 以内
    • 可预测的停顿:这是 G1 相对于 CMS 的另一大优势,G1 和 CMS 一样都是关注于降低停顿时间,但是 G1 能够让使用者明确的指定在一个 M 毫秒的时间片段内,消耗在垃圾收集的时间不得超过 N 毫秒
    • 通过此选项指定: +UseG1GC
  • ZGC 收集器:减少 SWT 时长(1ms 以内),可以媲美 C++ 的效率,目前实验阶段
  • Shenandoah 收集器:和 ZGC 竞争关系,目前实验阶段
  • Epsilon 收集器:调试 JDK 使用,内部使用,不用于生产环境
垃圾收集器设置

优化调整 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 达到老年代的大小的百分比多少时触发回收,默认 68
    -XX:+UseCMSCompactAtFullCollection 开启此值,在 CMS 收集后,进行内存碎片整理
    -XX:CMSFullGCsBeforeCompaction=N 设定多少次 CMS 后,进行一次内存碎片整理
    -XX:+CMSParallelRemarkEnabled 降低标记停顿

JAVA 参数总结

参数名称 含义 默认值 备注
-Xms 初始堆大小 物理内存的 1/64(<1GB) 默认(MinHeapFreeRatio 参数可以调整)空余堆内存小于 40%时,JVM 就会增大堆直到-Xmx 的最大限制.
-Xmx 最大堆大小 物理内存的 1/4(<1GB) 默认(MaxHeapFreeRatio 参数可以调整)空余堆内存大于 70%时,JVM 会减少堆直到-Xms 的最小限制
-Xmn 年轻代大小(1.4or lator) 注意:此处的大小是(eden+ 2 survivor space).与 jmap -heap 中显示的 New gen 是不同的。 整个堆大小=年轻代大小 + 年老代大小 + 持久代大小. 增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun 官方推荐配置为整个堆的 3/8
-XX:NewSize 设置年轻代大小(for 1.3/1.4)
-XX:MaxNewSize 年轻代最大值(for 1.3/1.4)
-XX:PermSize 设置持久代(perm gen)初始值 物理内存的 1/64
-XX:MaxPermSize 设置持久代最大值 物理内存的 1/4
-Xss -Xss JDK5.0 以后每个线程堆栈大小为 1M,以前每个线程堆栈大小为 256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在 3000~5000 左右 一般小的应用, 如果栈不是很深, 应该是 128k 够用的 大的应用建议使用 256k。这个选项对性能影响比较大,需要严格的测试。
-XX:ThreadStackSize Thread StackSize (0 means use defaultstack size) [Sparc: 512;Solaris x86: 320 (was 256prior in 5.0 and earlier);Sparc 64 bit: 1024; Linuxamd64: 1024 (was 0 in 5.0and earlier); all others 0.]
-XX:NewRatio 年轻代(包括 Eden 和两个 Survivor 区)与年老代的比值(除去持久代) -XX:NewRatio=4 表示年轻代与年老代所占比值为 1:4,年轻代占整个堆栈的 1/5Xms=Xmx 并且设置了 Xmn 的情况下,该参数不需要进行设置。
-XX:SurvivorRatio Eden 区与 Survivor 区的大小比值 设置为 8,则两个 Survivor 区与一个 Eden 区的比值为 2:8,一个 Survivor 区占整个年轻代的 1/10
-XX:LargePageSizeInBytes 内存页的大小不可设置过大, 会影响 Perm 的大小 =128m
-XX:+UseFastAccessorMethods 原始类型的快速优化
-XX:+DisableExplicitGC 关闭 System.gc() 这个参数需要严格的测试
-XX:MaxTenuringThreshold 垃圾最大年龄 如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在 Survivor 区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率 该参数只有在串行 GC 时才有效
-XX:+AggressiveOpts 加快编译
-XX:+UseBiasedLocking 锁机制的性能改善
-Xnoclassgc 禁用垃圾回收
-XX:SoftRefLRUPolicyMSPerMB 每兆堆空闲空间中 SoftReference 的存活时间 可达的对象在上次被引用后将保留一段时间。 缺省值是堆中每个空闲兆字节的生命周期的一秒钟
-XX:PretenureSizeThreshold 对象超过多大是直接在旧生代分配 0 单位字节 新生代采用 Parallel Scavenge GC 时无效 另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象
-XX:TLABWasteTargetPercent TLAB 占 eden 区的百分比 1%
-XX:+CollectGen0First FullGC 时是否先 YGC false

并行收集器相关参数:

参数名称 含义 默认值 备注
-XX:+UseParallelGC Full GC 采用 parallelMSC 选择垃圾收集器为并行收集器.此配置仅对年轻代有效.即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集
-XX:+UseParNewGC 设置年轻代为并行收集 可与 CMS 收集同时使用 JDK5.0 以上,JVM 会根据系统配置自行设置,所以无需再设置此值
-XX:ParallelGCThreads 并行收集器的线程数 此值最好配置与处理器数目相等 同样适用于 CMS
-XX:+UseParallelOldGC 年老代垃圾收集方式为并行收集(ParallelCompacting) 这个是 JAVA 6 出现的参数选项
-XX:MaxGCPauseMillis 每次年轻代垃圾回收的最长时间(最大暂停时间) 如果无法满足此时间,JVM 会自动调整年轻代大小,以满足此值
-XX:+UseAdaptiveSizePolicy 自动选择年轻代区大小和相应的 Survivor 区比例 设置此选项后,并行收集器会自动选择年轻代区大小和相应的 Survivor 区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开
-XX:GCTimeRatio 设置垃圾回收时间占程序运行时间的百分比 公式为 1/(1+n)
-XX:+ScavengeBeforeFullGC Full GC 前调用 YGC true

CMS 相关参数:

参数名称 含义 默认值 备注
-XX:+UseConcMarkSweepGC 使用 CMS 内存收集 测试中配置这个以后,-XX:NewRatio=4 的配置可能失效,所以,此时年轻代大小最好用-Xmn 设置
-XX:+AggressiveHeap 试图是使用大量的物理内存 长时间大内存使用的优化,能检查计算资源(内存, 处理器数量) 至少需要 256MB 内存 大量的 CPU/内存, (在 1.4.1 在 4CPU 的机器上已经显示有提升)
-XX:CMSFullGCsBeforeCompaction 多少次后进行内存压缩 由于并发收集器不对内存空间进行压缩,整理,所以运行一段时间以后会产生”碎片”,使得运行效率降低.此值设置运行多少次 GC 以后对内存空间进行压缩,整理
-XX:+CMSParallelRemarkEnabled 降低标记停顿
-XX+UseCMSCompactAtFullCollection 在 FULLGC 的时候,对年老代的压缩 CMS 是不会移动内存的, 因此, 这个非常容易产生碎片,导致内存不够用, 因此, 内存的压缩这个时候就会被启用。增加这个参数是个好习惯。 可能会影响性能,但是可以消除碎片
-XX:+UseCMSInitiatingOccupancyOnly 使用手动定义初始化定义开始 CMS 收集 禁止 hostspot 自行触发 CMSGC
-XX:CMSInitiatingOccupancyFraction=70 使用 cms 作为垃圾回收使用 70%后开始 CMS 收集 92
-XX:+UseConcMarkSweepGC 使用 CMS 内存收集 测试中配置这个以后,-XX:NewRatio=4 的配置可能失效,所以,此时年轻代大小最好用-Xmn 设置
-XX:CMSInitiatingPermOccupancyFraction 设置 PermGen 使用到达多少比率时触发 92
-XX:+CMSIncrementalMode 设置为增量模式 用于单 CPU 情况
-XX:+CMSClassUnloadingEnabled

辅助信息:

参数名称 含义 默认值 备注
-XX:+PrintGC 输出形式:[GC 118250K->113543K(130112K),0.0094143 secs] [Full GC121376K->10414K(130112K),0.0650971 secs]
-XX:+PrintGCDetails 输出形式:[GC [DefNew:8614K->781K(9088K),0.0123035 secs] 118250K->113543K(130112K),0.0124633 secs] [GC[DefNew: 8614K->8614K(9088K), 0.0000665secs] 121376K->10414K(130112K),0.0436268 secs]
-XX:+PrintGCTimeStamps
-XX:+PrintGC:PrintGCTimeStamps 可与-XX:+PrintGC -XX:+PrintGCDetails 混合使用输出形式:11.851: [GC98328K->93620K(130112K),0.0082960 secs]
-XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间.可与上面混合使用 输出形式:Total time forwhich application threadswere stopped: 0.0468229seconds
-XX:+PrintGCApplicationConcurrentTime 打印每次垃圾回收前,程序未中断的执行时间.可与上面混合使用 输出形式:Application time:0.5291524 seconds
-XX:+PrintHeapAtGC 打印 GC 前后的详细堆栈信息
-Xloggc:filename 把相关日志信息记录到文件以便分析. 与上面几个配合使用
-XX:+PrintGC 输出形式:[GC 118250K->113543K(130112K),0.0094143 secs] [Full GC121376K->10414K(130112K),0.0650971 secs]
-XX:+PrintClassHistogram garbagecollectsbeforeprintingthehistogram
-XX:+PrintTLAB 查看 TLAB 空间的使用情况
-XX:+PrintTenuringDistribution 查看每次 minor GC 后新的存活周期的阈值

JVM 相关工具

$JAVA_HOME/bin 下

命令 说明
jps 查看所有 jvm 进程
jinfo 查看进程的运行环境参数,主要是 jvm 命令行参数
jstat 对 jvm 应用程序的资源和性能进行实时监控
jstack 查看所有线程的运行状态,程序员常用堆栈情况查看工具
jmap 查看 jvm 占用物理内存的状态
jhat +UseParNew
jconsole 图形工具
jvisualvm 图形工具

JMX

JMX(Java Management Extensions,即 Java 管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX 可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。
JMX 最常见的场景是监控 Java 程序的基本信息和运行情况,任何 Java 程序都可以开启 JMX,然后使用 JConsole 或 Visual VM 进行预览。

为 Java 程序开启 JMX 很简单,只要在运行 Java 程序的命令后面指定如下命令即可

在 tomcat 开启远程 JMX 如下配置:

-Dcom.sun.management.jmxremote #启用远程监控JMX
-Djava.rmi.server.hostname=10.0.0.71 #tomcat主机自己的IP地址,不要写zabbix服务器的地址
-Dcom.sun.management.jmxremote.port=1100 #默认启动的JMX端口号,要和zabbix添加主机时候的端口一致
-Dcom.sun.management.jmxremote.ssl=false #不启用ssl认证
-Dcom.sun.management.jmxremote.authenticate=false #不启用用户名密码认证

jps

JVM 进程状态工具

jinfo

输出给定的 java 进程的所有配置信息

jstat

输出指定的 java 进程的统计信息

jstack

查看指定的 java 进程的线程栈的相关信息,程序员常用堆栈情况查看工具

jmap

Memory Map,用于查看堆内存的使用状态

jhat

Java Heap Analysis Tool 堆分析工具

jconsole

查看 Java 进程信息的图形化工具

  1. 配置 JMX,在服务器 10.0.0.71 上做如下配置

    $vim /usr/local/tomcat/bin/catalina.sh
    # OS specific support.  $var _must_ be set to either true or false.
    # 添加以下配置,注意位置,如果添加在文件末尾,则无法生效
    JAVA_OPTS="-Djava.rmi.server.hostname=10.0.0.71 -Dcom.sun.management.jmxremote.port=1100 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
    
    $systemctl restart tomcat.service
  2. 在 windows 上配置好 jdk,去 jdk/bin 目录下,启动 jconsole.exe

jvisualvm

监控 jvm 的图形化工具,可以本机监控,也可以远程监控,下面以远程监控为例:

  1. 配置 JMX,在服务器 10.0.0.71 上做如下配置

    $vim /usr/local/tomcat/bin/catalina.sh
    # OS specific support.  $var _must_ be set to either true or false.
    # 添加以下配置,注意位置,如果添加在文件末尾,则无法生效
    JAVA_OPTS="-Djava.rmi.server.hostname=10.0.0.71 -Dcom.sun.management.jmxremote.port=1100 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
    
    $systemctl restart tomcat.service
  2. 在 windows 上配置好 jdk,去 jdk/bin 目录下,启动 jvisualvm.exe,添加远程主机 -> 选择添加 jmx 连接

使用 jvisualvm 的 Visual GC 插件

安装完成后,重启即可,不过因为是远程连接,还需要做其他配置,这个问题百度解决,或者选择使用本机监控

Jprofiler

JProfiler 是一款功能强大的 Java 开发分析工具,它可以快速的帮助用户分析出存在的错误,软件还可对需要的显示类进行标记,包括了内存的分配情况和信息的视图等

这个工具是收费的,可以用来定位 OOM 的问题原因

JProfiler 官网:http://www.ej-technologies.com/products/jprofiler/overview.html

Tomcat 性能优化常用配置

内存空间优化

JAVA_OPTS="-server -Xms4g -Xmx4g -XX:NewSize= -XX:MaxNewSize= "

-server:服务器模式,VM运行在server模式,为在服务器端最大化程序运行速度而优化
-Xms:堆内存初始化大小
-Xmx:堆内存空间上限
-XX:NewSize=:新生代空间初始化大小
-XX:MaxNewSize=:新生代空间最大值

生产案例:

[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

线程池调整

[root@centos8 ~]#vim /usr/local/tomcat/conf/server.xml
......
<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/css、text/javascript

tomcat
http://blog.lujinkai.cn/运维/TomCat/tomcat/
作者
像方便面一样的男子
发布于
2021年2月1日
更新于
2023年12月5日
许可协议