概要:

  1. Tomcat各核心组件认知
  2. Tomcat server.xml 配置详解
  3. Tomcat IO模型介绍

一、Tomcat各组件认知


知识点:

  1. Tomcat架构说明
  2. Tomcat组件及关系详情介绍
  3. Tomcat启动参数说明
  4. Tomcat架构说明

1. Tomcat 安装配置

1.安装JAVA(因为tomcat依赖于java)

  • 配置:JAVA_HOME
    • D:\Program Files (x86)\Java\jdk1.7.0
  • path
    • %JAVA_HOME%\bin
  • 验证:java -version

2.安装TOMCAT

  • CATALINA_BASE
    • D:\apache-tomcat-7.0.16
  • CATALINA_HOME
    • D:\apache-tomcat-7.0.16
  • CATALINA_TMPDIR
    • D:\apache-tomcat-7.0.16
  • path
    • %CATALINA_HOME%\bin
  • 验证:
    • startup
    • localhost:8080


ps:**

  • catalina.home(安装目录):指向公用信息的位置,就是 bin 和 lib 的父目录。
  • catalina.base(工作目录):指向每个 Tomcat 目录私有信息的位置,就是 conf、logs、temp、webapps 和work 的父目录。

仅运行一个Tomcat实例时,这两个属性指向的位置是相同的。如果我们想要运行Tomcat的 多个实例,但是不想安装多个Tomcat软件副本。那么我们可以配置多个工作目录,每个运行实例独占一个工作目录,但是共享同一个安装目录。

Tomcat 的目录结构

  • bin(运行脚本)
  • conf(配置文件)
    • image.png
  • lib (核心库文件)
  • logs (日志目录)
    • 存放tomcat运行过程中产生的日志文件,在linux环境中,控制台的输出日志在catalina.out文件中
  • temp (临时目录)
  • webapps (自动装载的应用程序的目录)
    • 用来存放应用程序,当tomcat启动时会去加载webapps目录下的应用程序。可以以文件夹、war包、jar包的形式发布应用。 当然,你也可以把应用程序放置在磁盘的任意位置,在配置文件中映射好就行。
  • work (JVM临时文件目录[java.io.tmpdir])
    • 存放tomcat在运行时的编译后文件,例如JSP编译后的文件。
    • 清空work目录,然后重启tomcat,可以达到清除缓存的作用。

2.Tomcat 各组件及关系

Tomcat是一个基于JAVA的WEB容器,是一个免费的开放源码的 Web 应用服务器。

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,它早期的名称为catalina,后来由Apache、Sun 和其他一些公司及个人共同开发而成,并更名为Tomcat。

最本质上讲,tomcat为一个jsp/servlet容器

其实现了JAVA EE中的 Servlet 与 jsp 规范,与Nginx apache 服务器不同在于一般用于动态请求处理。在架构设计上采用面向组件的方式设计。即整体功能是通过组件的方式拼装完成。另外每个组件都可以被替换以保证灵活性。

image.png
Tomcat - 图3

那么是哪些组件组成了Tomcat呢?

  • 1. 顶级组件:Server 和 Service
    • Server:表示一个Tomcat实例 (单例的);Server 代表整个 catalina servlet 容器;包含一个或多个service 子容器。主要是用来管理容器下各个 Serivce 组件的生命周期。
    • Service:代表 Tomcat 中一组提供服务、处理请求的组件。是一个分组结构,包括多个 Connector 和一个 Container。
  • 2. Connector 连接器
    • Connector 是客户端连接到 Tomcat 容器的服务点,它为引擎提供协议服务来将引擎与客户端各种协议隔离开来。如HTTP、SSL HTTPS、AJP协议( Apache JServ Protocol) apache 私有协议(二进制协议),用于apache 反向代理 Tomcat)
    • Connector的基本属性都是它所需要监听的IP地址及端口号,以及所支持的协议。还有一个关键属性就是并发处理传入请求的最大线程数。注意,Connector关键的有 连接器(HTTP HTTPS HTTP1.1 AJP SSL proxy) 运行模式(BIO NIO NIO2/AIO APR)多线程/线程池
    • 一个Connecter将在某个指定的端口上侦听客户请求,接收浏览器的发过来的 tcp 连接请求,创建一个 Request 和 Response 对象分别用于和请求端交换数据,然后会产生一个线程来处理这个请求并把产生的 Request 和 Response 对象传给处理Engine(Container中的一部分),从Engine出获得响应并返回客户。
  • 3. 容器组件 Container:

Container 是容器的父接口,用于封装和管理 Servlet,以及具体处理 Request 请求,该容器的设计用的是典型的责任链的设计模式,它由四个自容器组件构成,分别是 Engine、Host、Context、Wrapper。这四个组件是负责关系,存在包含关系。只包含一个引擎。

  • Engine 引擎 Catalina
    • 表示可运行的Catalina的servlet引擎实例,并且包含了servlet容器的核心功能。
    • 在一个服务中只能有一个引擎。同时,作为一个真正的容器。
    • Engine元素之下可以包含一个或多个虚拟主机。
    • 它主要功能是将传入请求委托给适当的虚拟主机处理。如果根据名称没有找到可处理的虚拟主机,那么将根据默认的Host来判断该由哪个虚拟主机处理。
  • Host 虚拟主机 基于域名 分发请求
    • 作用就是运行多个应用,它负责安装和展开这些应用,并且标识这个应用以便能够区分它们。
    • 它的子容器通常是 Context。一个虚拟主机下都可以部署一个或者多个 Web App,每个 Web App 对应于一个 Context,当 Host 获得一个请求时,将把该请求匹配到某个Context上,然后把该请求交给该Context来处理。
    • Host主要用来解析 web.xml。
  • Context 上下文 隔离各个WEB应用 每个Context的 ClassLoader都是独立
    • 代表 Servlet 的 Context,它具备了 Servlet 运行的基本环境,它表示Web应用程序本身。
    • Context 最重要的功能就是管理它里面的 Servlet 实例,一个Context对应于一个 Web Application,一个 Web Application 由一个或者多个Servlet实例组成。
  • Wrapper包装器: 代表一个 Servlet,它负责管理一个 Servlet,包括的 Servlet 的装载、初始化、执行以及资源回收。Wrapper 是最底层的容器,它没有子容器了,所以调用它的 addChild 将会报错。
    • Component 嵌套组件
  • Valve阀门:类似于Servlet规范中定义的过滤器,用来拦截请求并在将其转至目标之前进行某种处理操作。Valve可以定义在任何容器类的组件中。Valve常被用来记录客户端请求、客户端IP地址和服务器等信息,这种处理技术通常被称作请求转储(request dumping)。请求转储valve记录请求客户端请求数据包中的HTTP首部信息和cookie信息文件中,响应转储valve则记录响应数据包首部信息和cookie信息至文件中。
  • Logger日志记录器:用于记录组件内部的状态信息,可被用于除Context之外的任何容器中。日志记录的功能可被继承,因此,一个引擎级别的Logger将会记录引擎内部所有组件相关的信息,除非某内部组件定义了自己的Logger组件。
  • Loader类加载器:负责加载、解释Java类编译后的字节码。
  • Realm领域:用于用户的认证和授权;在配置一个应用程序时,管理员可以为每个资源或资源组定义角色及权限,而这些访问控制功能的生效需要通过Realm来实现。Realm的认证可以基于文本文件、数据库表、LDAP服务等来实现。Realm的效用会遍及整个引擎或顶级容器,因此,一个容器内的所有应用程序将共享用户资源。同时,Realm可以被其所在组件的子组件继承,也可以被子组件中定义的Realm所覆盖
  • Excutor执行器:执行器组件允许您配置一个共享的线程池,以供您的连接器使用。从tomcat 6.0.11版本开始。
  • Listener监听器:监听已注册组件的生命周期。
  • Manager会话管理器:用于实现http会话管理的功能,tomcat6种有5种会话管理的manager的实现(standardManager、persisentManager、DeltaManager、BackupManager、SimpleTcpReplicationManager)。会话让使用无状态HTTP协议的应用程序完成通信。会话表示客户端和服务器之间的通信,会话功能是由javax.servlet.http.HttpSession 的实例实现的,该实例存储在服务器上而且与一个唯一的标识符相关联,客户端在与服务器的每次交互中根据请求中的标识符找到它的会话。一个新的会话在客户端请求后被创建,会话一直有效直到一段时间后客户端连接超时,或者会话直接失效例如客户退出访问服务器。
  • Cluster集群:专用于配置Tomcat集群的元素,可用于Engine和Host容器中。

架构好处: 1. 便于组件生命周期的管理; 2. 简化配置,允许子容器继承父容器的配置。   

Tomcat - 图4

  • 一个 Server 可以有多个 Service
  • 一个 Service 有多个 连接器 Connector
  • 一个 Service 有一个引擎 Engine
  • 一个引擎有多个 Host
  • 一个 Host 有多个 Context

3.Tomcat启动参数说明

我们平时启动Tomcat过程是怎么样的?

  1. 复制WAR包至Tomcat webapp 目录。
  2. 执行starut.bat 脚本启动。
  3. 启动过程中war 包会被自动解压装载。

但是我们在Eclipse 或idea 中启动WEB项目的时候 也是把War包复杂至webapps 目录解压吗?显然不是,其真正做法是在Tomcat程序文件之外创建了一个部署目录,在一般生产环境中也是这么做的 即:Tomcat 程序目录和部署目录分开 。
我们只需要在启动时指定CATALINA_HOME 与 CATALINA_BASE 参数即可实现。

启动参数 描述说明
JAVA_OPTS jvm 启动参数 , 设置内存 编码等 -Xms100m -Xmx200m -Dfile.encoding=UTF-8
JAVA_HOME 指定jdk 目录,如果未设置从java 环境变量当中去找。
CATALINA_HOME Tomcat 程序根目录
CATALINA_BASE 应用部署目录,默认为$CATALINA_HOME
CATALINA_OUT 应用日志输出目录:默认$CATALINA_BASE/log
CATALINA_TMPDIR 应用临时目录:默认:$CATALINA_BASE/temp

可以编写一个脚本 来实现自定义配置:
演示自定义启动Tomcat

  • 下载并解压Tomcat
  • 创建并拷贝应用目录
  • 创建Tomcat.sh
  • 编写Tomcat.sh
  • chmod +x tomcat.sh 添加执行权限
  • 拷贝conf 、webapps 、logs至应用目录。
  • 执行启动测试。
  1. #!/bin/bash
  2. export JAVA_OPTS="-Xms100m -Xmx200m"
  3. export JAVA_HOME=/root/svr/jdk/
  4. export CATALINA_HOME=/usr/local/apache-tomcat-8.5.34
  5. export CATALINA_BASE="`pwd`"
  6. case $1 in
  7. start)
  8. $CATALINA_HOME/bin/catalina.sh start
  9. echo start success!!
  10. ;;
  11. stop)
  12. $CATALINA_HOME/bin/catalina.sh stop
  13. echo stop success!!
  14. ;;
  15. restart)
  16. $CATALINA_HOME/bin/catalina.sh stop
  17. echo stop success!!
  18. sleep 2
  19. $CATALINA_HOME/bin/catalina.sh start
  20. echo start success!!
  21. ;;
  22. version)
  23. $CATALINA_HOME/bin/catalina.sh version
  24. ;;
  25. configtest)
  26. $CATALINA_HOME/bin/catalina.sh configtest
  27. ;;
  28. esac
  29. exit 0

二、Tomcat server.xml 配置详解


Tomcat的配置文件默认存放在/usr/local/tomcat/conf目录中,主要有以下几个:

  1. server.xml: Tomcat的主配置文件,包含Service, Connector, Engine, Realm, Valve, Hosts主组件的相关配置信息;
  2. web.xml:遵循Servlet规范标准的配置文件,用于配置servlet,并为所有的Web应用程序提供包括MIME映射等默认配置信息;
  3. tomcat-user.xmlRealm认证时用到的相关角色、用户和密码等信息;Tomcat自带的manager默认情况下会用到此文件;在Tomcat中添加/删除用户,为用户指定角色等将通过编辑此文件实现;
  4. catalina.policyJava相关的安全策略配置文件,在系统资源级别上提供访问控制的能力;
  5. catalina.propertiesTomcat内部package的定义及访问相关的控制,也包括对通过类装载器装载的内容的控制;Tomcat6在启动时会事先读取此文件的相关设置;
  6. logging.properties: Tomcat6通过自己内部实现的JAVA日志记录器来记录操作相关的日志,此文件即为日志记录器相关的配置信息,可以用来定义日志记录的组件级别以及日志文件的存在位置等;
  7. context.xml:所有host的默认配置信息;

Server 的基本基本配置:

  1. <Server>
  2. <Listener /><!-- 监听器 -->
  3. <GlobaNamingResources> <!-- 全局资源 -->
  4. </GlobaNamingResources
  5. <Service> <!-- 服务 用于 绑定 连接器与 Engine -->
  6. <Connector 8080/> <!-- 连接器-->
  7. <Connector 8010 /> <!-- 连接器-->
  8. <Connector 8030/> <!-- 连接器-->
  9. <Engine> <!-- 执行引擎-->
  10. <Logger />
  11. <Realm />
  12. <host "www.tl.com" appBase=""> <!-- 虚拟主机-->
  13. <Logger /> <!-- 日志配置-->
  14. <Context "/luban" path=""/> <!-- 上下文配置-->
  15. </host>
  16. </Engine>
  17. </Service>
  18. </Server>

配置样例:

  1. <Server port=”8005″ shutdown=”SHUTDOWN”>
  2.   <Listener className=”org.apache.catalina.core.JasperListener” />
  3.   <Listener className=”org.apache.catalina.mbeans.ServerLifecycleListener” />
  4.   <Listener className=”org.apache.catalina.mbeans.GlobalResourcesLifecycleListener” />
  5.   <GlobalNamingResources>
  6.    <Resource name=”UserDatabase” auth=”Container”
  7.    type=”org.apache.catalina.UserDatabase”
  8.    description=”User database that can be updated and saved
  9.    factory=”org.apache.catalina.users.MemoryUserDatabaseFactory”
  10.    pathname=”conf/tomcat-users.xml”/>
  11.   </GlobalNamingResources>
  12.   <Service name=”Catalina”>
  13.     <Connector port=”8080″ protocol=”HTTP/1.1″
  14.       maxThreads=”150″ connectionTimeout=”20000″
  15.       redirectPort=”8443″/>
  16.     <Engine name=”Catalina” defaultHost=”localhost”>
  17.       <Host name=”localhost” appBase=”webapps”
  18.         unpackWARs=”true” autoDeploy=”true”
  19.         xmlValidation=”false” xmlNamespaceAware=”false”>
  20.       </Host>
  21.     </Engine>
  22.   </Service>
  23. </Server>
  24. <Server port=”8005″ shutdown=”SHUTDOWN”>
  25.   <Listener className=”org.apache.catalina.core.AprLifecycleListener” SSLEngine=”on” />
  26.   <Listener className=”org.apache.catalina.core.JasperListener” />
  27.   <Listener className=”org.apache.catalina.core.JreMemoryLeakPreventionListener” />
  28.   <Listener className=”org.apache.catalina.mbeans.GlobalResourcesLifecycleListener” />
  29.   <Listener className=”org.apache.catalina.core.ThreadLocalLeakPreventionListener” />
  30.   <GlobalNamingResources>
  31.     <Resource name=”UserDatabase” auth=”Container”
  32.       type=”org.apache.catalina.UserDatabase”
  33.       description=”User database that can be updated and saved
  34.       factory=”org.apache.catalina.users.MemoryUserDatabaseFactory”
  35.       pathname=”conf/tomcat-users.xml” />
  36.   </GlobalNamingResources>
  37.   <Service name=”Catalina”>
  38.     <Connector port=”8080″ protocol=”HTTP/1.1″
  39.       connectionTimeout=”20000″
  40.       redirectPort=”8443″ />
  41.     <Connector port=”8009″ protocol=”AJP/1.3″ redirectPort=”8443″ />
  42.     <Engine name=”Catalina” defaultHost=”localhost”>
  43.      <Realm className=”org.apache.catalina.realm.LockOutRealm”>
  44.        <Realm className=”org.apache.catalina.realm.UserDatabaseRealm”
  45.            resourceName=”UserDatabase”/>
  46.      </Realm>
  47.      <Host name=”localhost” appBase=”webapps”
  48.          unpackWARs=”true” autoDeploy=”true”>
  49.        <Valve className=”org.apache.catalina.valves.AccessLogValve” directory=”logs”
  50.            prefix=”localhost_access_log.” suffix=”.txt”
  51.            pattern=”%h %l %u %t &quot;%r&quot; %s %b/>
  52.      </Host>
  53.     </Engine>
  54.   </Service>
  55. </Server>

元素说明:
server

  • root元素:server 的顶级配置
  • 主要属性:

    • port:执行关闭命令的端口号
    • shutdown:关闭命令
  • [ ] 演示shutdown的用法

  1. <Server port="8005" shutdown="SHUTDOWN" debug="0">
  2. 1>className: 指定实现org.apache.catalina.Server接口的完全限定类的名称.默认值为org.apache.catalina.core.StandardServer
  3. 2>port: 服务器等待关机命令的TCP/IP端口号。设置为-1以禁用关闭端口。默认仅允许通过本机访问,默认为8005;
  4. 3>shutdown: 必须通过TCP/IP连接接收到指定端口号的命令字符串,以关闭Tomcat。默认为SHUTDOWN
  5. 4>address: 服务器等待关机命令的TCP/IP地址。如果没有指定地址,则使用localhost。
  6. #基于telent 执行SHUTDOWN 命令即可关闭
  7. telent 127.0.0.1 8005
  8. SHUTDOWN

service
服务:将多个connector 与一个Engine组合成一个服务,可以配置多个服务。

  1. **<Service name="Catalina"\>**
  2. **<Service name="Apache"\>**
  3. 第一个<Service>处理所有直接由Tomcat服务器接收的web客户请求.
  4. 第二个<Service>处理所有由Apahce服务器转发过来的Web客户请求 .
  5. 1>className 指定实现org.apahce.catalina.Service接口的类.默认为org.apahce.catalina.core.StandardService
  6. 2>name: 定义此服务的名称,此名字也会在产生相关的日志信息时记录在日志文件当中。与特定服务器关联的每个服务的名称必须是唯一的。

Connector
连接器:用于接收 指定协议下的连接 并指定给唯一的Engine 进行处理。
主要属性:

  • protocol 监听的协议,默认是http/1.1
  • port 指定服务器端要创建的端口号
  • minThread 服务器启动时创建的处理请求的线程数
  • maxThread 最大可以创建的处理请求的线程数
  • enableLookups 如果为true,则可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名,若为false则不进行DNS查询,而是返回其ip地址
  • redirectPort 指定服务器正在处理http请求时收到了一个SSL传输请求后重定向的端口号
  • acceptCount 指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理
  • connectionTimeout 指定超时的时间数(以毫秒为单位)
  • SSLEnabled 是否开启 sll 验证,在Https 访问时需要开启。

  • [ ] 演示配置多个Connector

在 /Users/zhangchuanqiang/work/ApacheTomcat/conf 下的 server.xml 文件里增加一个 Connector
每个增加的 Connector 对应的线程等参数都是独立的

  1. <Connector port="8860" protocol="org.apache.coyote.http11.Http11NioProtocol"
  2. connectionTimeout="20000"
  3. redirectPort="8862"
  4. URIEncoding="UTF-8"
  5. useBodyEncodingForURI="true"
  6. compression="on" compressionMinSize="2048"
  7. compressableMimeType="text/html,text/xml,text/plain,text/javascript,text/css,application/x-json,application/json,application/x-javascript"
  8. maxThreads="1024" minSpareThreads="200"
  9. acceptCount="800"
  10. enableLookups="false"
  11. />

Tomcat 支持三种协议:HTTP/1.1、HTTP/2.0、AJP

  1. (1) BIO HTTP/1.1 Connector配置
  2. 一个典型的配置如下:  
  3. <Connector port=”8080 protocol=”HTTP/1.1 maxThreads=”150 conn ectionTimeout=”20000 redirectPort=”8443 />
  4. 其它一些重要属性如下:
  5. acceptCount : 接受连接request的最大连接数目,默认值是10
  6. address : 绑定IP地址,如果不绑定,默认将绑定任何IP地址
  7. allowTrace : 如果是true,将允许TRACE HTTP方法
  8. compressibleMimeTypes : 各个mimeType, 以逗号分隔,如text/html,text/xml
  9. compression : 如果带宽有限的话,可以用GZIP压缩
  10. connectionTimeout : 超时时间,默认为60000ms (60s)
  11. maxKeepAliveRequest : 默认值是100
  12. maxThreads : 处理请求的Connector的线程数目,默认值为200
  13. 如果是SSL配置,如下:
  14. <Connector port="8181" protocol="HTTP/1.1" SSLEnabled="true"
  15. maxThreads="150" scheme="https" secure="true"
  16. clientAuth="false" sslProtocol = "TLS"
  17. address="0.0.0.0"
  18. keystoreFile="E:/java/jonas-full-5.1.0-RC3/conf/keystore.jks"
  19. keystorePass="changeit" />
  20. 其中,keystoreFile为证书位置,keystorePass为证书密码
  21. (2) NIO HTTP/1.1 Connector配置
  22. <Connector port=”8080 protocol=”org.apache.coyote.http11.Http11NioProtocol maxThreads=”150 connectionTimeout=”20000 redirectPort=”8443”/>
  23. (3) Native APR Connector配置
  24. ARP是用C/C++写的,对静态资源(HTML,图片等)进行了优化。所以要下载本地库tcnative-1.dllopenssl.exe,将其放在%tomcat%\bin目录下。
  25. 下载地址是:http://tomcat.heanet.ie/native/1.1.10/binaries/win32/
  26. server.xml中要配置一个Listener,如下图。这个配置tomcat是默认配好的。
  27. <!--APR library loader. Documentation at /docs/apr.html -->
  28. <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  29. 配置使用APR connector
  30. <Connector port=”8080 protocol=”org.apache.coyote.http11.Http11AprProtocol
  31. maxThreads=”150 connectionTimeout=”20000 redirectPort=”8443”/>
  32. 如果配置成功,启动tomcat,会看到如下信息: org.apache.coyote.http11.Http11AprProtocol init
  33. (4) AJP Connector配置
  34. <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

Engine
引擎:用于处理连接的执行器,默认的引擎是catalina。一个service 中只能配置一个Engine。
主要属性:name 引擎名称 defaultHost 默认host

Host
虚拟机:基于域名匹配至指定虚拟机。类似于nginx 当中的server,默认的虚拟机是localhost.
主要属性:

  • 演示配置多个Host
  1. <Host name="www.luban.com" appBase="/usr/www/luban"
  2. unpackWARs="true" autoDeploy="true">
  3. <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="www.luban.com.access_log" suffix=".txt"
  4. pattern="%h %l %u %t &quot;%r&quot; %s %b" />
  5. </Host>

Context
应用上下文:一个host 下可以配置多个Context ,每个Context 都有其独立的classPath。相互隔离,以免造成ClassPath 冲突。
主要属性:

  • 演示配置多个Context
  1. <Context docBase="hello" path="/h" reloadable="true"/>

Valve
阀门:可以理解成request 的过滤器,具体配置要基于具体的Valve 接口的子类。以下即为一个访问日志的Valve.

  1. <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
  2. prefix="www.luban.com.access_log" suffix=".txt"
  3. pattern="%h %l %u %t &quot;%r&quot; %s %b" />

三、Tomcat IO模型介绍


知识点:

  1. Tomcat支持的IO模型说明
  2. BIO 与NIO的区别
  3. IO模型源码解读

1、Tomcat支持的IO模型说明

描述
BIO 阻塞式IO,即Tomcat使用传统的java.io进行操作。该模式下每个请求都会创建一个线程,对性能开销大,不适合高并发场景。优点是稳定,适合连接数目小且固定架构。
NIO 非阻塞式IO,jdk1.4 之后实现的新IO。该模式基于多路复用选择器监测连接状态在通知线程处理,从而达到非阻塞的目的。比传统BIO能更好的支持并发性能。Tomcat 8.0之后默认采用该模式
APR 全称是 Apache Portable Runtime/Apache可移植运行库),是Apache HTTP服务器的支持库。可以简单地理解为,Tomcat将以JNI的形式调用Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作。使用需要编译安装APR 库
AIO 异步非阻塞式IO,jdk1.7后之支持 。与nio不同在于不需要多路复用选择器,而是请求处理线程执行完程进行回调调知,已继续执行后续操作。Tomcat 8之后支持。

使用指定IO模型的配置方式:
配置 server.xml 文件当中的 修改即可。
默认配置 8.0 protocol=“HTTP/1.1” 8.0 之前是 BIO 8.0 之后是NIO
BIO
protocol=“org.apache.coyote.http11.Http11Protocol“
NIO
protocol=”org.apache.coyote.http11.Http11NioProtocol“
AIO
protocol=”org.apache.coyote.http11.Http11Nio2Protocol“
APR
protocol=”org.apache.coyote.http11.Http11AprProtocol“

2、BIO 与NIO有什么区别

分别演示在高并发场景下BIO与NIO的线程数的变化?
演示数据:

每秒提交数 BIO执行线程 NIO执行线程
预测 200 200 50
实验环境 200 48 37
生产环境 200 419 23

结论:

BIO 线程模型讲解
Tomcat - 图5

NIO 线程模型讲解
Tomcat - 图6

BIO 源码解读

  1. Http11Protocol Http BIO协议解析器
  2. JIoEndpoint
  3. Acceptor implements Runnable
  4. SocketProcessor implements Runnable
  5. Http11NioProtocol Http Nio协议解析器
  6. NioEndpoint
  7. Acceptor implements Runnable
  8. Poller implements Runnable
  9. SocketProcessor implements Runnable

四、Tomcat 处理一个Http请求的过程

image.png

假设来自客户的请求为:http://localhost:8080/test/index.jsp 请求被发送到本机端口8080

  1. 用户点击网页内容,请求被发送到本机端口 8080,被在那里监听的 Coyote HTTP/1.1 Connector 获得。
  2. Connector 把该请求交给它所在的 Service 的 Engine 来处理,并等待 Engine 的回应。
  3. Engine获得请求 localhost/test/index.jsp,匹配所有的虚拟主机 Host。
  4. Engine匹配到名为 localhost 的 Host(即使匹配不到也把请求交给该Host处理,因为该 Host 被定义为该Engine的默认主机),名为 localhost 的 Host 获得请求 /test/index.jsp,匹配它所拥有的所有的Context。Host 匹配到路径为/test的 Context(如果匹配不到就把该请求交给路径名为”” 的 Context 去处理)。
  5. path=“/test”的 Context 获得请求 /index.jsp,在它的 mapping table 中寻找出对应的 Servlet。Context 匹配到 URL PATTERN 为*.jsp 的 Servlet,对应于 JspServlet 类。
  6. 构造 HttpServletRequest 对象和 HttpServletResponse 对象,作为参数调用 JspServlet的doGet()doPost() 执行业务逻辑、数据存储等程序。
  7. Context 把执行完之后的 HttpServletResponse 对象返回给 Host。
  8. Host 把 HttpServletResponse 对象返回给 Engine。
  9. Engine 把 HttpServletResponse 对象返回 Connector。
  10. Connector 把 HttpServletResponse 对象返回给客户 Browser。

简单模拟 Tomcat

  1. package Server;
  2. import java.io.*;
  3. import java.net.ServerSocket;
  4. import java.net.Socket;
  5. import java.net.URLDecoder;
  6. import java.util.StringTokenizer;
  7. public class TomcatServer {
  8. private final static int PORT = 8080;
  9. public static void main(String[] args) {
  10. try {
  11. ServerSocket server = new ServerSocket(PORT);// 根据端口号启动一个serverSocket
  12. ServletHandler servletHandler = new ServletHandler(server);
  13. servletHandler.start();
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. private static class ServletHandler extends Thread {
  19. ServerSocket server = null;
  20. public ServletHandler(ServerSocket server) {
  21. this.server = server;
  22. }
  23. @Override
  24. public void run() {
  25. while (true) {
  26. try {
  27. Socket client = null;
  28. client = server.accept();// ServerSocket阻塞等待客户端请求数据
  29. if (client != null) {
  30. try {
  31. System.out.println("接收到一个客户端的请求");
  32. // 根据客户端的Socket对象获取输入流对象。
  33. // 封装字节流到字符流
  34. BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
  35. // GET /test.jpg /HTTP1.1
  36. // http请求由三部分组成,分别是:请求行、消息报头、请求正文。
  37. // 这里取的第一行数据就是请求行。http协议详解可以参考http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html说的很详细
  38. String line = reader.readLine();
  39. System.out.println("line: " + line);
  40. // 拆分http请求路径,取http需要请求的资源完整路径
  41. String resource = line.substring(line.indexOf('/'), line.lastIndexOf('/') - 5);
  42. System.out.println("the resource you request is: " + resource);
  43. resource = URLDecoder.decode(resource, "UTF-8");
  44. // 获取到这次请求的方法类型,比如get或post请求
  45. String method = new StringTokenizer(line).nextElement().toString();
  46. System.out.println("the request method you send is: " + method);
  47. // 继续循环读取浏览器客户端发出的一行一行的数据
  48. while ((line = reader.readLine()) != null) {
  49. if (line.equals("")) {// 当line等于空行的时候标志Header消息结束
  50. break;
  51. }
  52. System.out.println("the Http Header is : " + line);
  53. }
  54. // 如果是POST的请求,直接打印POST提交上来的数据
  55. if ("post".equals(method.toLowerCase())) {
  56. System.out.println("the post request body is: " + reader.readLine());
  57. } else if ("get".equals(method.toLowerCase())) {
  58. // 判断是get类型的http请求处理
  59. // 根据http请求的资源后缀名来确定返回数据
  60. // 比如下载一个图片文件,我这里直接给定一个图片路径来模拟下载的情况
  61. if (resource.endsWith(".jpg")) {
  62. transferFileHandle("d://1.jpg", client);
  63. closeSocket(client);
  64. continue;
  65. } else {
  66. // 直接返回一个网页数据
  67. // 其实就是将html的代码以字节流的形式写到IO中反馈给客户端浏览器。
  68. // 浏览器会根据http报文“Content-Type”来知道反馈给浏览器的数据是什么格式的,并进行什么样的处理
  69. PrintStream writer = new PrintStream(client.getOutputStream(), true);
  70. writer.println("HTTP/1.0 200 OK");// 返回应答消息,并结束应答
  71. writer.println("Content-Type:text/html;charset=utf-8");
  72. writer.println();
  73. // writer.println("Content-Length:" +
  74. // html.getBytes().length);// 返回内容字节数
  75. writer.println("<html><body>");
  76. writer.println("<a href='www.baidu.com'>百度</a>");
  77. writer.println(
  78. "<img src='https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png'></img>");
  79. writer.println("</html></body>");
  80. // writer.println("HTTP/1.0 404 Not
  81. // found");// 返回应答消息,并结束应答
  82. writer.println();// 根据 HTTP 协议, 空行将结束头信息
  83. writer.close();
  84. closeSocket(client);// 请求资源处理完毕,关闭socket链接
  85. continue;
  86. }
  87. }
  88. } catch (Exception e) {
  89. System.out.println("HTTP服务器错误:" + e.getLocalizedMessage());
  90. }
  91. }
  92. } catch (Exception e) {
  93. e.printStackTrace();
  94. }
  95. }
  96. }
  97. private void closeSocket(Socket socket) {
  98. try {
  99. socket.close();
  100. } catch (IOException ex) {
  101. ex.printStackTrace();
  102. }
  103. System.out.println(socket + "离开了HTTP服务器");
  104. }
  105. private void transferFileHandle(String path, Socket client) {
  106. File fileToSend = new File(path);
  107. if (fileToSend.exists() && !fileToSend.isDirectory()) {
  108. try {
  109. // 根据Socket获取输出流对象,将访问的资源数据写入到输出流中
  110. PrintStream writer = new PrintStream(client.getOutputStream());
  111. writer.println("HTTP/1.0 200 OK");// 返回应答消息,并结束应答
  112. writer.println("Content-Type:application/binary");
  113. writer.println("Content-Length:" + fileToSend.length());// 返回内容字节数
  114. writer.println();// 根据 HTTP 协议, 空行将结束头信息
  115. FileInputStream fis = new FileInputStream(fileToSend);
  116. byte[] buf = new byte[fis.available()];
  117. fis.read(buf);
  118. writer.write(buf);
  119. writer.close();
  120. fis.close();
  121. } catch (IOException e) {
  122. e.printStackTrace();
  123. }
  124. }
  125. }
  126. }
  127. }

拓展资料