tomcat(java容器)

1、安装

  • 环境

    • jdk+tomcat

      # 免安装
      jdk-8u261-linux-x64.tar.gz
      # 安装
      jdk-8u261-linux-x64.rpm
      
    • tomcat软件

      # 免安装
      apache-tomcat-8.5.60.tar.gz
      
  • 安装

    • jdk

      # rpm包
      [root@node105 ~]# rpm -ivh jdk-8u261-linux-x64.rpm
      # 二进制包
      [root@node105 ~]# tar xf jdk-8u261-linux-x64.tar.gz -C /jdk
      [root@node105 ~]# cat >> /etc/profile << 'EOF'
      > export JAVA_HOME=/jdk/jdk1.8.0_261
      > export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
      > export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar
      > EOF
      [root@node105 ~]# source /etc/profile
      [root@node105 ~]# java -version
      java version "1.8.0_261"
      Java(TM) SE Runtime Environment (build 1.8.0_261-b12)
      Java HotSpot(TM) 64-Bit Server VM (build 25.261-b12, mixed mode)
      
    • tomcat

      # 解压即可使用
      [root@node105 ~]# tar xf apache-tomcat-8.5.60.tar.gz
      # 验证
      [root@node105 ~]# ./apache-tomcat-8.5.60/bin/version.sh 
      Using CATALINA_BASE:   /root/apache-tomcat-8.5.60
      Using CATALINA_HOME:   /root/apache-tomcat-8.5.60
      Using CATALINA_TMPDIR: /root/apache-tomcat-8.5.60/temp
      Using JRE_HOME:        /jdk/jdk1.8.0_261
      Using CLASSPATH:       /root/apache-tomcat-8.5.60/bin/bootstrap.jar:/root/apache-tomcat-8.5.60/bin/tomcat-juli.jar
      Using CATALINA_OPTS:   
      Server version: Apache Tomcat/8.5.60
      Server built:   Nov 12 2020 17:31:55 UTC
      Server number:  8.5.60.0
      OS Name:        Linux
      OS Version:     3.10.0-862.el7.x86_64
      Architecture:   amd64
      JVM Version:    1.8.0_261-b12
      JVM Vendor:     Oracle Corporation
      
    • tomcat启停脚本

      #!/bin/bash
      # chkconfig:2345 10 90
      source /etc/profile
      source /etc/init.d/functions
      CATALINA_HOME=/usr/local/tomcat
      function tomcat_cmd {
          $CATALINA_HOME/bin/catalina.sh $1
      }
      case $1 in
          status)
              if ps -ef | grep tomcat | grep -Ev 'grep|service|init.d' &>/dev/null; then
                  action "tomcat is running !" /bin/true
              else
                  action "tomcat is stopped !" /bin/true
              fi
              ;;
          start)
              tomcat_cmd start &>/dev/null
              [ $? -eq 0 ] && action "tomcat is running !" /bin/true
              ;;
          stop)
              tomcat_cmd stop &>/dev/null
              [ $? -eq 0 ] && action "tomcat is stopped !" /bin/true
              ;;
          restart)
              tomcat_cmd stop &>/dev/null && \
              tomcat_cmd start &>/dev/null
              action "tomcat restart complete !" /bin/true
              ;;
          *)
              action "Usage: service tomcat start|stop|restart|status" /bin/true
              ;;
      esac
      

2、目录

  • 主目录

    tree -L 1 /app/tomcat/
    

    mark

  • webapps目录

    tree /app/tomcat/webapps/ -L 1
    

    mark

  • 日志

    tree /app/tomcat/logs
    

    mark

    • catalina.out日志是tomcat的实时运行日志,文件会越来越大,需要定时清理。
    • catalina.out重点关注的三个字段:info为信息,error为报错,startup、finish为启动时间。
    • catalina.时间.log是每天自动进行日志切割后的文件
    • 站点日志: 每个站点运行都会写入到站点日志中
  • 配置文件

    tree -L 1 /app/tomcat/conf
    

    mark

    • server.xml组件类别:

      • 顶级组件: 位于整个配置的顶层,如server。
      • 容器类组件: 可以包含其它组件的组件,如service、engine、host、context。
      • 连接器组件: 连接用户请求至tomcat,如connector。
      • 被嵌套类组件:位于一个容器当中,不能包含其他组件,如Valve、logger。
      • 示例模板:
      <server>
           <service>
           <connector />
           <engine>
           <host>
           <context></context>
           </host>
           <host>
           <context></context>
           </host>
           </engine>
           </service>
      </server>
      
    • 组件详解

      • engine:
        核心容器组件,catalina引擎,负责通过connector接收用户请求,并处理请求,将请求转至对应的虚拟主机host。
      • host:
        类似于虚拟主机。
      • context:
        定义一个应用程序,配置context的主要目的指定对应对的webapp的根目录,类似于httpd的alias,其还能为webapp指定额外的属性,如部署方式等。
      • connector:
        接收用户请求,类似于listen配置监听端口。
      • service:
        将connector关联至engine,一般情况下一个server内部只有一个service,一个service内部只有一个engine,但一个service内部可以有多个connector。
      • server:
        表示一个运行于JVM中的tomcat实例。
      • Valve:
        阀门,拦截请求并在将其转至对应的webapp前进行某种处理操作,可以用于任何容器中,比如记录日志(access log valve)、基于IP做访问控制(remote address filter valve)。
      • logger:
        日志记录器,用于记录组件内部的状态信息,可以用于除context外的任何容器中。
  • 管理功能

    • 仅测试环境可用,生产环境禁用
    • tomcat8.5以后,限制只能在本地使用127.0.0.1访问管理。解除限制需修改配置文件
[root@node105 apache-tomcat-8.5.60]# vim conf/tomcat-users.xml
…………
<role rolename="manager-gui"/>
<role rolename="admin-gui"/>
<user username="tomcat" password="tomcat" roles="manager-gui,admin-gui"/>
</tomcat-users>  #→在此行前加入上面三行
root@node105 apache-tomcat-8.5.60]# find ./webapps/ -type f -name "*.xml" | xargs grep '127'
./webapps/host-manager/META-INF/context.xml:         allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
./webapps/host-manager/WEB-INF/manager.xml:         allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
./webapps/manager/META-INF/context.xml:         allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
[root@node105 apache-tomcat-8.5.60]# sed -i 's#127#\\d+#g' ./webapps/host-manager/META-INF/context.xml ./webapps/host-manager/WEB-INF/manager.xml ./webapps/manager/META-INF/context.xml
[root@node105 apache-tomcat-8.5.60]# find ./webapps/ -type f -name "*.xml" | xargs grep 'allow='
./webapps/host-manager/META-INF/context.xml:         allow="\d+\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
./webapps/host-manager/WEB-INF/manager.xml:         allow="\d+\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
./webapps/manager/META-INF/context.xml:         allow="\d+\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />

  • 自定义默认网站目录

    • 方法1:

      将程序放在tomcat/webapps/ROOT目录下即可,因为默认网站根目录为tomcat/webapps/ROOT。

    • 方法2:

      [root@node105 apache-tomcat-8.5.60]# vim conf/server.xml
      <Host name="localhost" appBase="webapps"
            unpackWARs="true" autoDeploy="true">  #在下面添加:
           <Context path="" docBase="/app/tomcat/webapps/memtest" debug="0" reloadable="false" crossContext="true"/>
      

3、主配置文件

[root@node105 apache-tomcat-8.5.60]# cat conf/server.xml
<?xml version='1.0' encoding='utf-8'?>
<!--
<Server>元素代表整个容器,是Tomcat实例的顶层元素.由org.apache.catalina.Server接口来定义.它包含一个<Service>元素.并且它不能做为任何元素的子元素.
    port指定Tomcat监听shutdown命令端口.终止服务器运行时,必须在Tomcat服务器所在的机器上发出shutdown命令.该属性是必须的.
    shutdown指定终止Tomcat服务器运行时,发给Tomcat服务器的shutdown监听端口的字符串.该属性必须设置
-->
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>
  <!--service服务组件-->
  <Service name="Catalina">
    <!--
    connector:接收用户请求,类似于httpd的listen配置监听端口.
        port指定服务器端要创建的端口号,并在这个端口监听来自客户端的请求。
        address:指定连接器监听的地址,默认为所有地址(即0.0.0.0)
        protocol连接器使用的协议,支持HTTP和AJP。AJP(Apache Jserv Protocol)专用于tomcat与apache建立通信的, 在httpd反向代理用户请求至tomcat时使用(可见Nginx反向代理时不可用AJP协议)。
        minProcessors服务器启动时创建的处理请求的线程数
        maxProcessors最大可以创建的处理请求的线程数
        enableLookups如果为true,则可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名,若为false则不进行DNS查询,而是返回其ip地址
        redirectPort指定服务器正在处理http请求时收到了一个SSL传输请求后重定向的端口号
        acceptCount指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理
        connectionTimeout指定超时的时间数(以毫秒为单位)
    -->
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

    <!--engine,核心容器组件,catalina引擎,负责通过connector接收用户请求,并处理请求,将请求转至对应的虚拟主机host
        defaultHost指定缺省的处理请求的主机名,它至少与其中的一个host元素的name属性值是一样的
    -->
    <Engine name="Catalina" defaultHost="localhost">
      <!--Realm表示存放用户名,密码及role的数据库-->
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>
      <!--
      host表示一个虚拟主机
        name指定主机名
        appBase应用程序基本目录,即存放应用程序的目录.一般为appBase="webapps" ,相对于CATALINA_HOME而言的,也可以写绝对路径。
        unpackWARs如果为true,则tomcat会自动将WAR文件解压,否则不解压,直接从WAR文件中运行应用程序
        autoDeploy:在tomcat启动时,是否自动部署。
        xmlValidation:是否启动xml的校验功能,一般xmlValidation="false"。
        xmlNamespaceAware:检测名称空间,一般xmlNamespaceAware="false"。
      -->
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <!--
        Context表示一个web应用程序,通常为WAR文件
            docBase应用程序的路径或者是WAR文件存放的路径,也可以使用相对路径,起始路径为此Context所属Host中appBase定义的路径。
            path表示此web应用程序的url的前缀,这样请求的url为http://localhost:8080/path/****
            reloadable这个属性非常重要,如果为true,则tomcat会自动检测应用程序的/WEB-INF/lib 和/WEB-INF/classes目录的变化,自动装载新的应用程序,可以在不重启tomcat的情况下改变应用程序
        -->
        <Context path="" docBase="" debug=""/>

        <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" />
      </Host>
    </Engine>
  </Service>
</Server>

4、部署应用

  • java程序部署方式:

    • jar包:java -jar xxx.jar运行
    • war包: 把war包放入webapps目录下,自动解压,自动部署
  • jpress安装示例:

    • 创建jpress数据库和jpress用户
    • 上传war包到webapps,自动解压
    [root@node105 webapps]# ls
    docs  examples  host-manager  jpress-v3.3.0.war  manager  ROOT
    [root@node105 webapps]# pwd
    /root/apache-tomcat-8.5.60/webapps
    [root@node105 webapps]# ls
    docs  examples  host-manager  jpress-v3.3.0  jpress-v3.3.0.war  manager  ROOT
    
    • 配置web

    • 访问:

5、多实例与集群

  • 多实例:

    就是把主文件加多复制几份,然后修改配置文件中的端口号即可。

    # 复制多份
    [root@node105 ~]# cp -a apache-tomcat-8.0.27 tomcat8_1
    [root@node105 ~]# cp -a apache-tomcat-8.0.27 tomcat8_2
    # 修改配置文件
    [root@node105 ~]# sed -i 's#8005#8011#;s#8080#8081#' tomcat8_1/conf/server.xml
    [root@node105 ~]# sed -i 's#8005#8012#;s#8080#8082#' tomcat8_2/conf/server.xml
    
  • 集群

    就是使用nginx的upstream模块

    http {
    	......
    	upstream tomcat_pool {
    		server 192.168.1.104:8081;
    		server 192.168.1.104:8082;
    	}
    	server {
    		listen 80;
    		server_name localhost;
    		location / {
    			proxy_pass http://tomcat_pool;
    		}
    	}
    }
    

6、监控

  • jps

    • 功能:查看java进程
    [root@node105 ~]# jps -lvm | grep -v jps
    1518 org.apache.catalina.startup.Bootstrap start -Djava.util.logging.config.file=/root/apache-tomcat-8.5.60/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -Dcatalina.base=/root/apache-tomcat-8.5.60 -Dcatalina.home=/root/apache-tomcat-8.5.60 -Djava.io.tmpdir=/root/apache-tomcat-8.5.60/temp
    [root@node105 ~]# pstree -p | grep java
               |-java(1518)-+-{java}(1519)
               |            |-{java}(1520)
               |            |-{java}(1521)
               |            |-{java}(1522)
               |            |-{java}(1523)
               |            |-{java}(1524)
               |            |-{java}(1525)
               |            |-{java}(1526)
               |            |-{java}(1527)
               |            |-{java}(1528)
               |            |-{java}(1529)
               |            |-{java}(1530)
               |            |-{java}(1533)
               |            |-{java}(1544)
               |            |-{java}(1545)
               |            |-{java}(1546)
               |            |-{java}(1842)
               |            |-{java}(1843)
               |            |-{java}(1844)
               |            |-{java}(1846)
               |            |-{java}(1847)
               |            |-{java}(1848)
               |            |-{java}(1849)
               |            |-{java}(1850)
               |            |-{java}(1851)
               |            |-{java}(1852)
               |            |-{java}(1853)
               |            |-{java}(1854)
               |            |-{java}(1855)
               |            |-{java}(1856)
               |            |-{java}(1857)
               |            |-{java}(1858)
               |            |-{java}(1860)
               |            |-{java}(1861)
               |            |-{java}(1862)
               |            |-{java}(1863)
               |            |-{java}(1864)
               |            |-{java}(1866)
               |            |-{java}(1868)
               |            |-{java}(1870)
               |            |-{java}(1872)
               |            |-{java}(1873)
               |            |-{java}(1875)
               |            |-{java}(1876)
               |            |-{java}(1877)
               |            |-{java}(1881)
               |            |-{java}(1883)
               |            `-{java}(1893)
    
  • show-busy-java-threads脚本

    • 功能:显示cpu使用率最高的前几个java线程

      [root@node105 ~]# bash show-busy-java-threads.sh 
      [1] Busy(0.0%) thread(1518/0x5ee) stack of java process(1518) under user(root):
      
      [2] Busy(0.0%) thread(1519/0x5ef) stack of java process(1518) under user(root):
      "main" #1 prio=5 os_prio=0 tid=0x00007fe04800a000 nid=0x5ef runnable [0x00007fe0509ec000]
         java.lang.Thread.State: RUNNABLE
              at java.net.PlainSocketImpl.socketAccept(Native Method)
              at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:535)
              at java.net.ServerSocket.implAccept(ServerSocket.java:545)
              at java.net.ServerSocket.accept(ServerSocket.java:513)
              at org.apache.catalina.core.StandardServer.await(StandardServer.java:447)
              at org.apache.catalina.startup.Catalina.await(Catalina.java:776)
              at org.apache.catalina.startup.Catalina.start(Catalina.java:722)
              at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
              at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
              at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
              at java.lang.reflect.Method.invoke(Method.java:498)
              at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:342)
              at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:473)
      
      [3] Busy(0.0%) thread(1520/0x5f0) stack of java process(1518) under user(root):
      "VM Thread" os_prio=0 tid=0x00007fe04806e800 nid=0x5f0 runnable 
      
      [4] Busy(0.0%) thread(1521/0x5f1) stack of java process(1518) under user(root):
      "Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007fe048078800 nid=0x5f1 in Object.wait() [0x00007fe04d082000]
         java.lang.Thread.State: WAITING (on object monitor)
              at java.lang.Object.wait(Native Method)
              at java.lang.Object.wait(Object.java:502)
              at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
              - locked <0x00000000f5dd8228> (a java.lang.ref.Reference$Lock)
              at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
      
      [5] Busy(0.0%) thread(1522/0x5f2) stack of java process(1518) under user(root):
      "Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007fe04807d000 nid=0x5f2 in Object.wait() [0x00007fe04cf81000]
         java.lang.Thread.State: WAITING (on object monitor)
              at java.lang.Object.wait(Native Method)
              at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
              - locked <0x00000000f5dd8070> (a java.lang.ref.ReferenceQueue$Lock)
              at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
              at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
      
  • jstack

    • 功能:定位到线程堆栈,在JVM性能调优中使用得非常多。

    • 命令:

      jstack java进程pid >/xxx/jstack.log
      
  • jsconslse和jvisualvm

    • 开启tomcat监控

      # 修改tomcat/bin/catalina.sh,添加:
      CATALINA_OPTS="$CATALINA_OPTS \
      -Dcom.sun.management.jmxremote \
      -Dcom.sun.management.jmxremote.port=12345 \
      -Dcom.sun.management.jmxremote.authenticate=false \
      -Dcom.sun.management.jmxremote.ssl=false \
      -Djava.rmi.server.hostname=192.168.1.105"
      # 重启tomcat
      # 查看服务
      [root@node105 ~]# netstat -tnlp | grep java
      tcp6       0      0 :::45378                :::*                    LISTEN      2312/java           
      tcp6       0      0 :::41094                :::*                    LISTEN      2312/java           
      tcp6       0      0 :::8080                 :::*                    LISTEN      2312/java           
      tcp6       0      0 :::12345                :::*                    LISTEN      2312/java 
      
      • Dcom.sun.management.jmxremote:开启tomcat远程监控功能
      • Dcom.sun.management.jmxremote.port:指定特定端口端口,用于zabbix及其他监控,此外还有2个随机端口
      • Dcom.sun.management.jmxremote.authenticate:是否开启认证
      • Dcom.sun.management.jmxremote.ssl:是否开启https
      • Djava.rmi.server.hostname:实例所在服务器地址
    • jsconslse监控:

      • 路径:C:\Program Files\Java\jdk1.8.0_181\bin

      • GC按钮:垃圾回收,可以理解清空缓存

      • 连接示例:

    • jvisualvm监控

      • 路径:C:\Program Files\Java\jdk1.8.0_181\bin

      • 连接示例:

7、三种网络模型

  • java的IO概念:

    • 同步IO:Java自己处理IO读写(自己去银行取钱);
    • 异步IO:Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS,OS需要支持异步IO操作API(委托别人取钱并告知卡和密码);
    • 阻塞IO:Java调用会一直阻塞到读写完成才返回(ATM取款排队,前面的人没取完只能等);
    • 非阻塞IO:如果不能读写,Java调用会马上返回,当可读写时继续,此过程不断循环直到读写完成(柜台办业务取号,等通知过程中干别的事);
  • java的IO模式:

    • java BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销;
    • java NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
    • java AIO(NIO.2):异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
  • tomcat的IO模式:

    • BIO:即阻塞式I/O操作,表示Tomcat使用的是传统的Java I/O操作,是性能最低的一种。
    • NIO(NIO1/NIO2):NIO是一个基于缓冲区、并能提供非阻塞I/O操作的Java API,NIO1对应java NIO,NIO2对应java AIO。
    • APR:以JNI的形式调用Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作,从而大大地提高Tomcat对静态文件的处理性能,是在Tomcat上运行高并发应用的首选模式。
  • tomcat的IO配置:

    • BIO:tomcat 7以前的默认IO模式。

    • NIO1:tomcat 8默认IO方式为NIO1。默认配置:

          <Connector port="8080" protocol="HTTP/1.1"
                     connectionTimeout="20000"
                     redirectPort="8443" />
      
    • NIO2:

          <Connector port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
                     connectionTimeout="20000"
                     redirectPort="8443" />
      
    • APR:

      # 安装apr
      tar xf apr-xxx.tar.gz
      ./configure --prefix=/usr/local/apr && make && make install
      
      #安装apr-util
      tar xf apr-util-xxx.tar.gz
      ./configure --with-apr=/usr/local/apr/ --prefix=/usr/local/apr-utils && make && make install
      
      # 安装tomcat-native(tomcat/bin目录下有源码包)
      tar xf tomcat-native.tar.gz 
      ./configure --with-apr=/usr/local/apr --with-java-home=$JAVA_HOME && make && make install
      
      # cat >>/etc/profile << 'EOF'
      export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/apr/lib
      export LD_RUN_PATH=$LD_RUN_PATH:/usr/local/apr/lib
      EOF
      # source /etc/profile
      
          <Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol"
                     connectionTimeout="20000"
                     redirectPort="8443" />
      

8、常见故障

  • tomcat配置开机自启动,系统重启后tomcat未运行。

    • 原因:二进制安装的jdk,环境变量不在各种bin或sbin中。即使添加到/etc/profile也无法识别。
    # 原配置
    echo "/app/tomcat/bin/startup.sh" >> /etc/rc.local
    # 正确配置
    echo "source /etc/profile" >> /etc/rc.local
    echo "/app/tomcat/bin/startup.sh" >> /etc/rc.local
    
  • tomcat服务器运行占用大量swap,而物理内存占用较少

    • 原因:java代码问题
    • 运维角度解决:修改linux内核参数/etc/sysctl.conf,添加或修改{vm.swappiness=0}(控制系统是否优先使用物理内存,值越小,越优先使用物理内存),执行“sysctl -p”生效。
  • tomcat负载高

    • 排查思路:

      • vmstat查看系统运行。看proc,r表示cpu或内存使用,b代表磁盘IO使用。

        注:如果r经常大于4,id经常少于40,表示cpu的负荷很重;如果bi,bo长期不等于0,表示内存不足;如果proc中b的队列大于3,表示io性能不好。

        cpu内存可用top找出cpu或内存占用过高的pid,磁盘IO可用iotop -oP找出占用IO过高的pid。

      • 若确定为java进程,可用jps -lvm查看pid及启动信息

      • 线程定位:

        1. 通过top -Hp ${java进程pid},查看哪个线程占用cpu或内存过高

        2. 通过echo 'obase=16;${线程id}'| bc,将线程pid转换为16进制

        3. 通过jstack ${java进程pid} | grep -i ${java16进程线程id} -A10,导出java进程、线程信息,与开发沟通

      • 内存定位:

        1. 通过jmap -heap ${java进程pid},显示vm使用信息

        2. 通过jmap -dump:format=b,file=/root/tomcat.bin ${java进程pid},导出jvm内存内容

        3. 联系开发,用MAT工具进行分析,有个MemoryAnalyzer应用程序,加载导出文件,选择“Leak Suspects Report”查看是否内存溢出。

          操作步骤file--> open heapdump-->选择jmap导出的bin文件-->选择Leak Suspects Report(检查是否有内存溢出)

9、安全管理

  • telnet管理端口保护(强制)

    配置说明

    • 修改默认的8005管理端口为不易猜测的端口(大于1024);
    • 修改SHUTDOWN指令为其他字符串;
    • 要求端口配置在8000~8999之间。

    标准配置

    <Server port="8527" shutdown="dangerous">
    
  • 禁用管理端(强制)

    配置说明

    • 删除默认的{Tomcat安装目录}/conf/tomcat-users.xml文件,重启tomcat后将会自动生成新的文件;
    • 删除{Tomcat安装目录}/webapps下默认的所有目录和文件;
    • 将tomcat 应用根目录

    标准配置

    <Context path="" docBase="/home/work/local/tomcat_webapps" debug="0" reloadable="false" crossContext="true"/>
    

    对于前端web模块,Tomcat管理端属于tomcat的高危安全隐患,一旦被攻破,黑客通过上传web shell的方式将会直接取得服务器的控制权,后果极其严重;

  • 降权启动(强制)

    配置说明

    • tomcat启动用户权限必须为非root权限,尽量降低tomcat启动用户的目录访问权限;
    • 如需直接对外使用8080端口,可通过普通账号启动后,配置iptables规则进行转发;

    避免一旦tomcat 服务被入侵,黑客直接获取高级用户权限危害整个server的安全

  • 文件列表访问控制(强制)

    配置说明

    • conf/web.xml文件中default部分listings的配置必须为false;

    标准配置

    <init-param>
        <param-name>listings</param-name>
        <param-value>false</param-value>
    </init-param>
    

    false为不列出目录文件,true为允许列出,默认为false;

  • 版本信息隐藏(强制)

    配置说明

    • 修改conf/web.xml,重定向403、404以及500等错误到指定的错误页面;
    • 也可以通过修改应用程序目录下的WEB-INF/web.xml下的配置进行错误页面的重定向;

    标准配置

    <init-param>
        <param-name>listings</param-name>
        <param-value>false</param-value>
    </init-param>
    <error-page>
        <error-code>403</error-code>
     <location>/forbidden.jsp</location>
    </error-page>
    <error-page>
        <error-code>404</error-code>
        <location>/notfound.jsp</location>
    </error-page>
    <error-page>
        <error-code>500</error-code>
    <location>/systembusy.jsp</location>
    </error-page>
    

    备注: 在配置中对一些常见错误进行重定向,避免当出现错误时tomcat默认显示的错误页面暴露服务器和版本信息;必须确保程序根目录下的错误页面已经存在;此外的错误页面定义,每个项目工程目录下都要各自建立自己的相应的错误页面。

  • ajp连接端口保护(推荐)

    配置说明

    • 修改默认的ajp 8009端口为不易冲突的大于1024端口;
    • 通过iptables规则限制ajp端口访问的权限仅为线上机器;
    • 要求端口配置在8000~8999之间;
    • tomcat8默认ajp注释

    标准配置

    <Connector port="8528" protocol="AJP/1.3" />
    
  • Server header重写(强制)

    配置说明

    • 在HTTP Connector配置中加入server的配置;

    标准配置

    <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   server="webserver"
                   redirectPort="8443" />
    

    加入此配置,将会替换http 响应Server header部分的默认配置

  • 起停脚本权限回收(推荐)

    配置说明

    • 防止其他用户有起停线上Tomcat的权限;

    标准配置

    chmod -R 744 tomcat/bin/*
    
  • 访问日志格式规范(推荐)

    配置说明

    • 开启Tomcat默认访问日志中的Referer和User-Agent记录

    标准配置

    <Valve className="org.apache.catalina.valves.AccessLogValve"
        directory="logs"  prefix="localhost_access_log." suffix=".txt"
        pattern="%{X-Forwarded-For}i %l %u %t %r %s %b %{Referer}i %{User-Agent}i %D" resolveHosts="false"/>
    

    开启Referer和User-Agent是为了一旦出现安全问题能够更好的根据日志进行问题排查;
    X-Forwarded-For用于nginx作为反向代理服务器时,获取客户端真实的IP。

  • 访问限制(可选)

    配置说明

    • 通过配置,限定访问的ip来源

    标准配置

    <Context path="" docBase="/home/work/tomcat" debug="0" reloadable="false" crossContext="true">
        <Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="61.148.18.138,61.135.165.*" deny="*.*.*.*"/>
    </Context>
    

    通过配置信任ip的白名单,拒绝非白名单ip的访问,此配置主要是针对高保密级别的系统,一般产品线不需要.

  • 屏蔽DNS查询(强制)

    配置说明

    • enableLookups调用request、getRemoteHost()执行DNS查询

    标准配置

        <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   enableLookups="false"
                   redirectPort="8443" />
    

10、调优

  • 配置调优:

        <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   maxThreads="400"           #最大线程数,默认200
                   acceptCount="400"          #达到最大线程数量时的队列长度,默认100,与maxThreads一致即可
                   acceptorThreadCount="2"    #分为几队,与cpu核心总数一致,默认为1
                   minSpareThreads="20"       #空闲时候最小的线程数量
                   enableLookups="false"      #禁止DNS逆向查询,一般要关闭
                   
                   compression="on"           #开启tomcat压缩功能,针对静态文本资源
                   compressionMinSize="2048"  #启动压缩阈值
                   noCompressionUserAgents="gozilla, traviata"     #哪些客户端发出的请求不压缩,默认无限制
                   compressableMimeType="test/html,text/plain,text/css,application/javascript,application/json,application/x-font-ttf,application/x-font-otf"       #想压缩的数据类型
                   disableUploadTimeout="true"    #关闭上传文件超时时间
                   redirectPort="8443" />
    
    • 参数详解:

      • acceptCount:

        accept队列的长度;当accept队列中连接的个数达到acceptCount时,队列满,进来的请求一律被拒绝。默认值是100
        
      • maxConnections:

        Tomcat在任意时刻接收和处理的最大连接数。当Tomcat接收的连接数达到maxConnections时,Acceptor线程不会读取accept队列中的连接;这时accept队列中的线程会一直阻塞着,直到Tomcat接收的连接数小于maxConnections。如果设置为-1,则连接数不受限制。
        
        默认值与连接器使用的协议有关:NIO的默认值是10000,APR/native的默认值是8192,而BIO的默认值为maxThreads(如果配置了Executor,则默认值是Executor的maxThreads)。
        
        在windows下,APR/native的maxConnections值会自动调整为设置值以下最大的1024的整数倍;如设置为2000,则最大值实际是1024。
        
      • maxThreads:

        请求处理线程的最大数量。默认值是200(Tomcat7和8都是的)。如果该Connector绑定了Executor,这个值会被忽略,因为该Connector将使用绑定的Executor,而不是内置的线程池来执行任务。
        
        maxThreads规定的是最大的线程数目,并不是实际running的CPU数量;实际上,maxThreads的大小比CPU核心数量要大得多。这是因为,处理请求的线程真正用于计算的时间可能很少,大多数时间可能在阻塞,如等待数据库返回数据、等待硬盘读写数据等。因此,在某一时刻,只有少数的线程真正的在使用物理CPU,大多数线程都在等待;因此线程数远大于物理核心数才是合理的。
        
        换句话说,Tomcat通过使用比CPU核心数量多得多的线程数,可以使CPU忙碌起来,大大提高CPU的利用率。
        
    • 参数设置:

      • maxThreads:

        maxThreads的设置既与应用的特点有关,也与服务器的CPU核心数量有关。通过前面介绍可以知道,maxThreads数量应该远大于CPU核心数量;而且CPU核心数越大,maxThreads应该越大;应用中CPU越不密集(IO越密集),maxThreads应该越大,以便能够充分利用CPU。当然,maxThreads的值并不是越大越好,如果maxThreads过大,那么CPU会花费大量的时间用于线程的切换,整体效率会降低。
        
      • maxConnections:

        maxConnections的设置与Tomcat的运行模式有关。如果tomcat使用的是BIO,那么maxConnections的值应该与maxThreads一致;如果tomcat使用的是NIO,maxConnections值应该远大于maxThreads。
        
      • maxConnections:

        通过前面的介绍可以知道,虽然tomcat同时可以处理的连接数目是maxConnections,但服务器中可以同时接收的连接数为maxConnections+acceptCount 。acceptCount的设置,与应用在连接过高情况下希望做出什么反应有关系。如果设置过大,后面进入的请求等待时间会很长;如果设置过小,后面进入的请求立马返回connection refused。
        
    • 线程池Executor

      • 简述:

        Executor元素代表Tomcat中的线程池,可以由其他组件共享使用;要使用该线程池,组件需要通过executor属性指定该线程池。
        
        Executor是Service元素的内嵌元素。一般来说,使用线程池的是Connector组件;为了使Connector能使用线程池,Executor元素应该放在Connector前面。Executor与Connector的系统配置举例如下:
        
        # Executor示例
        <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
                maxThreads="150" minSpareThreads="4"/>
        # Connector示例    
            <Connector executor="tomcatThreadPool"
                port="8080" protocol="HTTP/1.1"
                connectionTimeout="20000"
                redirectPort="8443" />
        
      • Executor主要属性:

        - name:                该线程池的标记
        - maxThreads:          线程池中最大活跃线程数,默认值200(Tomcat7和8都是)
        - minSpareThreads:     线程池中保持的最小线程数,最小值是25
        - maxIdleTime:         线程空闲的最大时间,当空闲超过该值时关闭线程(除非线程数小于minSpareThreads),单位是ms,默认值60000(1分钟)
        - daemon:              是否后台线程,默认值true
        - threadPriority:      线程优先级,默认值5
        - namePrefix:          线程名字的前缀,线程池中线程名字为:namePrefix+线程编号
        
        • 优化示例
        <Executor name="tomcatThreadPool" 
                namePrefix="catalina-exec-"
                maxThreads="500" 
                minSpareThreads="20" 
                maxIdleTime="60000" 
                prestartminSpareThreads="true" 
                maxQueueSize="100" />
        
  • JVM调优:

    • 示例:

      JAVA_OPTS="-Djava.awt.headless=true \
      -Dfile.encoding=UTF-8 \
      -server -Xms1024m -Xmx1024m \
      -XX:NewSize=512m \
      -XX:MaxNewSize=512m \
      -XX:PermSize=512m \
      -XX:MaxPermSize=512m \
      -Xloggc:/var/log/tomcat_gc.log"
      
    • 示例详解:

      • server: 一定要作为第一个参数,在多个CPU时性能佳

      • Xms: jvm初始内存,默认为物理内存的1/64

      • Xmx: jvm最大内存,默认为物理内存的1/4。

        通常会将 -Xms 与 -Xmx两个参数的配置相同的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小而浪费资源。取决于硬件物理内存的大小,建议均设为物理内存的一半。

      • XX:PermSize: 设定内存的永久保存区域

      • XX:MaxPermSize: 设定最大内存的永久保存区域

      • XX:NewSize: 设定堆区域新生代内存的大小,应该小于 Xms的值

      • XX:MaxNewSize: 设定堆区域新生代内存的最大可分配大小,这个值应该小于 Xmx的值

      • Xloggc: 指定垃圾收集日志文件,目的是tomcat发生故障时定位分析

11、压力测试

  • 命令行ab

    # httpd-tools是安装包
    # 一次请求1000个,共请求20000个
    ab -c 1000 -n 20000 $URL 
    
  • jmeter

    jmeter使用指南:
    1)jdk环境
    2)启动jmeter/bin/
    3) 添加线程组,其中:
       线程数---并发访问的数量
       循环次数---测试多少次(永远)
    4)添加取样器
       选择http请求,修改:
       - 协议、IP、端口、路径、内容编码
    5)添加“察看结果树”、“聚合报告”
    
    聚合报告结果:
        - 平均值:平均响应时间ms
        - 吞吐量:每秒处理的请求数(并发访问量)
    
posted @ 2021-01-18 22:57  那就这样吧~  阅读(100)  评论(0编辑  收藏  举报