一起聊聊Tomcat的核心组件,主要配置和怎么优化

178 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

tomcat

连接器组件Connect

和socket通信, Http容器

Coyote

  • Coyote 封装了底层的⽹络通信(Socket 请求及响应处理)
  • Coyote 使Catalina 容器(容器组件)与具体的请求协议及IO操作⽅式完全解耦
  • Coyote 将Socket 输⼊转换封装为 Request 对象,进⼀步封装后交由Catalina 容器进⾏处理,处理请求完成后, Catalina 通过Coyote 提供的Response 对象将结果写⼊输出流
  • Coyote 负责的是具体协议和IO相关内容

内部组件

  • EndPoint EndPoint 是 Coyote 通信端点,即通信监听的接⼝,是具体Socket接收和发送处理器,是对传输层的抽象,因此EndPoint⽤来实现TCP/IP协议的
  • Processor Processor 是Coyote 协议处理接⼝ ,Processor⽤来实现HTTP协议, Processor接收来⾃EndPoint的Socket,读取字节流解析成Tomcat Request和Response对象,并通过Adapter将其提交到容器处理, Processor是对应⽤层协议的抽象
  • ProtocolHandler Coyote 协议接⼝, 通过Endpoint 和Processor , 实现针对具体协议的处理能⼒。
  • Adapter负责将Tomcat Request转成ServletRequest,再调⽤容器

容器组件Container

Servlet 容器

Catalina

一个Catalina实例

一个Server实例

多个Service实例

多个Connector实例和一个Container实例

  • Catalina 负责解析Tomcat的配置⽂件(server.xml) , 以此来创建服务器Server组件并进⾏管理
  • Server 服务器表示整个Catalina Servlet容器以及其它组件,负责组装并启动Servlet引擎,Tomcat连接器。 Server通过实现Lifecycle接⼝,提供了⼀种优雅的启动和关闭整个系统的⽅式
  • Service 服务是Server内部的组件,⼀个Server包含多个Service。它将若⼲个Connector组件绑定到⼀个Container
  • Container 容器,负责处理⽤户的servlet请求,并返回对象给web⽤户的模块

Container结构

Container组件下有⼏种具体的组件,分别是Engine、 Host、 Context和Wrapper。这4种组件(容器)是⽗⼦关系。 Tomcat通过⼀种分层的架构,使得Servlet容器具有很好的灵活性

  • Engine 表示整个Catalina的Servlet引擎,⽤来管理多个虚拟站点,⼀个Service最多只能有⼀个Engine,但是⼀个引擎可包含多个Host
  • Host 代表⼀个虚拟主机,或者说⼀个站点,可以给Tomcat配置多个虚拟主机地址,⽽⼀个虚拟主机下可包含多个Context
  • Context 表示⼀个Web应⽤程序, ⼀个Web应⽤可包含多个Wrapper
  • Wrapper 表示⼀个Servlet, Wrapper 作为容器中的最底层,不能包含⼦容器配置其实就体现在conf/server.xml中

配置

server.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<!-- Note:  A "Server" is not itself a "Container", so you may not
     define subcomponents such as "Valves" at this level.
     Documentation at /docs/config/server.html
 -->
<!-- 关闭服务器的监听端口 shutdown的指令 Server标识org.apache.catalina.Server接口的类 默认org.apache.catalina.core.StandardServer-->
<Server port="8005" shutdown="SHUTDOWN">
  <!-- 日志形式输出服务器 操作系统 JVM版本信息-->
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <!-- Security listener. Documentation at /docs/config/listeners.html
  <Listener className="org.apache.catalina.security.SecurityListener" />
  -->
  <!-- APR library loader. Documentation at /docs/apr.html -->
  <!-- 加载和销毁 APR 如果找不到, 不影响启动-->
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <!-- 避免JRE内存溢出-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <!-- 全局命名服务-->
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <!-- Context停止时,重建Executor的线程,避免ThreadLocal内存溢出-->
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

  <!-- Global JNDI resources
       Documentation at /docs/jndi-resources-howto.html
  -->
  <!--全局命名服务-->
  <GlobalNamingResources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <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>

  <!-- A "Service" is a collection of one or more "Connectors" that share
       a single "Container" Note:  A "Service" is not itself a "Container",
       so you may not define subcomponents such as "Valves" at this level.
       Documentation at /docs/config/service.html
   -->
  <Service name="Catalina">

    <!--The connectors can use a shared executor, you can define one or more named thread pools-->
    <!-- 连接器Connector的线程池-->
    <!--
    threadPriority 从1 到15

    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
         <Executor name="commonThread" namePrefix="thread-exec-"
         maxTreads="200" minSpareThreads="100" maxIdleTime="60000"
         maxQueueSize="Integer.MAX_SIZE" prestartminSpareThreads="false"
         threadPriority="5" className="org.apache.catalina.core.StandardThreadExecutor"/>
    -->


    <!-- A "Connector" represents an endpoint by which requests are received
         and responses are returned. Documentation at :
         Java HTTP Connector: /docs/config/http.html
         Java AJP  Connector: /docs/config/ajp.html
         APR (HTTP/AJP) Connector: /docs/apr.html
         Define a non-SSL/TLS HTTP/1.1 Connector on port 8080



      <Connector port="8080" protocol="HTTP/1.1"
              executor="tomcatThreadPool"
               connectionTimeout="20000"
               redirectPort="8443"
               maxThreads="150"
               minSpareThreads="100"
               acceptCount="1000"
               maxConnections="1000"
               compression="on"
               compressionMinSize="2048"
               disableUploadTimeout="true"
               URIEncoding="UTF-8"
               SSLEnabled="true"
                />
    -->
    <!-- 连接器 端口号 协议 超时时间 重定向 -->
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <!-- A "Connector" using the shared thread pool-->
    <!--
    <Connector executor="tomcatThreadPool"
               port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    -->
    <!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443
         This connector uses the NIO implementation. The default
         SSLImplementation will depend on the presence of the APR/native
         library and the useOpenSSL attribute of the
         AprLifecycleListener.
         Either JSSE or OpenSSL style configuration may be used regardless of
         the SSLImplementation selected. JSSE style configuration is used below.
    -->
    <!--
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true">
        <SSLHostConfig>
            <Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
                         type="RSA" />
        </SSLHostConfig>
    </Connector>
    -->
    <!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
         This connector uses the APR/native implementation which always uses
         OpenSSL for TLS.
         Either JSSE or OpenSSL style configuration may be used. OpenSSL style
         configuration is used below.
    -->
    <!--
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
               maxThreads="150" SSLEnabled="true" >
        <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
        <SSLHostConfig>
            <Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
                         certificateFile="conf/localhost-rsa-cert.pem"
                         certificateChainFile="conf/localhost-rsa-chain.pem"
                         type="RSA" />
        </SSLHostConfig>
    </Connector>
    -->

    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <!--
    <Connector protocol="AJP/1.3"
               address="::1"
               port="8009"
               redirectPort="8443" />
    -->

    <!-- An Engine represents the entry point (within Catalina) that processes
         every request.  The Engine implementation for Tomcat stand alone
         analyzes the HTTP headers included with the request, and passes them
         on to the appropriate Host (virtual host).
         Documentation at /docs/config/engine.html -->

    <!-- You should set jvmRoute to support load-balancing via AJP ie :
    org.apahce.catalina.Engine
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
    -->
    <Engine name="Catalina" defaultHost="localhost">

      <!--For clustering, please take a look at documentation at:
          /docs/cluster-howto.html  (simple how to)
          /docs/config/cluster.html (reference documentation) -->
      <!--
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
      -->

      <!-- Use the LockOutRealm to prevent attempts to guess user passwords
           via a brute-force attack -->
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <!-- 配置一个虚拟主机
      appBase: 目录
      unpackWARs: 解压war包
      autoDeploy: 自动部署
      -->
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
        <!--Log-->
        <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" />
        <Context docBase="/USERS/demo" path="/demo" reloadable="false"></Context>
      </Host>
    </Engine>
  </Service>
</Server>
源码

org.apache.catalina.startup.Bootstrap#main

类加载机制

BootStrapClassLoader ExtClassLoader AppClassLoader

Commons ClassLoader

Catalina ClassLoader ShareClassLoader -- WebApp ClassLoader

  • 系统类加载器正常情况下加载的是 CLASSPATH 下的类,但是 Tomcat 的启动脚本并未使⽤该变量,⽽是加载tomcat启动的类,⽐如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定位于CATALINA_HOME/bin下
  • Common 通⽤类加载器加载Tomcat使⽤以及应⽤通⽤的⼀些类,位于CATALINA_HOME/lib下,⽐如servlet-api.jar
  • Catalina ClassLoader ⽤于加载服务器内部可⻅类,这些类应⽤程序不能访问
  • Shared ClassLoader ⽤于加载应⽤程序共享类,这些类服务器不会依赖
  • Webapp ClassLoader,每个应⽤程序都会有⼀个独⼀⽆⼆的Webapp ClassLoader,他⽤来加载本应⽤程序 /WEB-INF/classes 和 /WEB-INF/lib 下的类。

tomcat 8.5做出了改变 ⾸先从 Bootstrap Classloader加载指定的类 如果未加载到,则从 /WEB-INF/classes加载 如果未加载到,则从 /WEB-INF/lib/*.jar 加载 如果未加载到,则依次从 System、 Common、 Shared 加载(在这最后⼀步,遵从双亲委派机制)

HTTPS

<Connector  port="8443"
protocol="org.apache.coyote.http11.Http11NioProtocol" SSLEnabled="true"
maxThreads="150"
scheme="https"
secure="true"
clientAuth="true"
sslProtocol="TLS"
keystoreFile="D:/keys/tomcat.keystore"
keystorePass="123456"
truststoreFile="D:/keys/tomcat.keystore"
truststorePass="123456" />

性能优化

JVM虚拟机优化

catalina.sh

JAVA_OPTS="-server -Xms2048m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"

JAVA_OPTS="-XX:+UseConcMarkSweepGC "

配置优化

  • 线程池调优

  • 禁用AJP连接器

  • 调整IO模式, 开启APR模式, 从操作系统级别解决异步IO问题, 使用时操作系统要安装APR和Native.

  • 动静分离 Nginx +Tomcat

  • 禁用DNS查询,设置server.xml文件中的Connector下的enableLookups为false。

其他

  • 如何禁止列目录下的文件

    web.xml文件中

    <init-param>
          <param-name>listings</param-name>
          <param-value>false</param-value>
    </init-param>
    
  • Tomcat的部署方式

    • conf目录中,server.xml的host节点中添加context
    • 拷贝项目文件到webapps中
    • 在conf目录下新建Catalina/localhost目录,目录中新建一个xml文件,xml中内容为部署一中的context内容
    • 使用在线后台管理器,上传war文件。 配置conf/tomcat-users.xml文件,然后访问http://localhost:8080/host-manager/html