一.Web容器和Servlet规范
1.Web容器 = HTTP服务器 + Servlet容器
- 在早期浏览新闻等静态页面的Web应用中,HTTP服务器(Apache,Nginx)向浏览器返回静态HTML,浏览器解析HTML并显示页面
- 互联网的发展需要与页面交互并获得动态结果,即需要一些扩展机制让HTTP服务器调用服务端程序
- Sun公司推出的Servlet技术可以简单理解为运行在服务端的Java小程序,但Servlet没有main方法,不能独立运行,所以必须把它部署到Servlet容器中,由容器实例化并调用
- Tomcat就是一个Servlet容器(Container组件),同时为了方便使用,它也具有HTTP服务器(Connector组件)功能,也称为Web容器
2.Servlet规范 = Servlet接口 + Servlet容器
- HTTP服务器接收到浏览器发送的HTTP格式请求后,会调用服务端程序(Java类)处理
- 不同的请求需要调用不同的Java类处理,这部分逻辑如果写在HTTP服务器里会和业务逻辑耦合在一起
- 面向接口编程可以解决耦合问题,我们为处理请求的业务类定义统一的Servlet接口
- 同时使用Servlet容器加载和管理这些业务类,并负责将请求转发给处理它的业务类
- 所以Servlet接口是Servlet容器与具体业务类之间的接口
- 程序员实现新的业务功能,只需要实现一个Servlet并注册到Tomcat(Servlet容器)即可,其他的工作都交由Tomcat完成
2.1 Servlet接口
public interface Servlet {
void init(ServletConfig config) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;
String getServletInfo();
void destroy();
}
- 具体业务类在 service 方法中实现处理逻辑
- service方法的参数ServletRequest和ServletResponse封装请求响应信息,本质上是封装通信协议
- 通过HttpServletRequest和HttpServletResponse封装HTTP协议的请求和响应,通过HttpServletRequest可以获取请求路径、Cookie、HTTP头、请求参数等信息
- Servlet容器在加载Servlet类时会调用 init 方法初始化资源,在卸载时会调用 destroy 方法释放资源。SpringMVC中的DispatcherServlet就是在 init 方法中创建了自己的Spring容器
- ServletConfig类用来封装Servlet的初始化参数,这些参数配置在 web.xml 中,在程序通过 getServletConfig 方法获取
2.2 Servlet容器
工作流程
- HTTP服务器创建ServletRequest对象封装客户请求信息,发送ServletRequest对象到Servlet容器
- Servlet容器收到请求后,根据URL和Servlet的映射关系,找到(或创建)这个Servlet
- 调用这个Servlet的service方法处理请求,并把ServletResponse对象返回给HTTP服务器
- HTTP服务器把响应发回客户端
Web应用
- Servlet通过Web应用程序的方式部署到Servlet容器中
- Web应用程序有固定的目录结构,目录中分别放置了Servlet的类文件、配置文件及静态资源,Servlet容器通过读取配置文件就能找到并加载Servlet
- Servlet规范定义ServletContext接口对应一个Web应用,Servlet容器启动时会加载Web应用并创建对应的ServletContext对象
| - MyWebApp
| - WEB-INF/web.xml -- 配置文件,用来配置Servlet等
| - WEB-INF/lib/ -- 存放Web应用所需各种JAR包
| - WEB-INF/classes/ -- 存放你的应用类,比如Servlet类
| - META-INF/ -- 目录存放工程的一些信息
扩展机制(Filter&Listener)
- Servlet规范标准化了Socket网络通信、HTTP协议解析和业务类调用的过程,程序员只需要专注业务逻辑的实现
- 同时为满足业务的个性化需求,Servlet提供了Filter和Listener两种扩展机制
- Filter允许对请求和响应进行统一的定制化处理,比如根据请求的频率限制访问或根据国家地区的不同修改响应内容
- Listener可以监听Servlet容器内部发生的各种事件(如Web应用的启动和停止、用户请求到达等),Servlet容器负责在事件发生时调用监听器的方法。Spring通过监听ServletContext启动事件,创建并初始化全局的Spring容器
二.Tomcat目录结构和日志
1.Tomcat目录结构
| - bin -- Tomcat脚本文件
| - conf
| - logging.properties -- Tomcat日志配置文件
| - server.xml -- Tomcat Server配置文件
| - web.xml -- Servlet配置文件,Tomcat默认实现部分配置
| - org.apache.catalina.servlets.DefaultServlet
| - org.apache.jasper.servlet.JspServlet
| - lib -- 公共类库
| - logs -- Tomcat运行中产生的日志文件
| - webapps -- 部署到Tomcat的Web应用
| - work -- Tomcat运行时产生的编译文件
- conf目录下的其他文件
- catalina.policy: Tomcat安全策略文件
- catalina.properties: Tomcat Catalina行为控制配置文件
- GlobalNamingResources: 全局JNDI资源
- context.xml: 全局Context配置文件
- tomcat-users.xml: Tomcat用户配置文件
2.Tomcat日志
- Tomcat日志分为
- 运行日志记录运行过程中的信息,包括异常和错误信息
- 访问日志记录访问的时间、IP地址和访问路径等信息
- Tomcat日志文件包括
- catalina.{date}.txt: 记录Tomcat启动过程信息
- localhost.{date}.txt: 记录Web应用初始化过程中遇到的未处理的异常
- localhost_access_log.{date}.txt: 记录访问Tomcat的请求日志,包括请求的IP地址、时间、路径、请求协议和状态码等
三.Tomcat I/O 模型
1.I/O 模型
- I/O 指在计算机内存与外部设备(网络或硬盘)之间拷贝数据的过程
- 用户线程通过CPU向外部设备发出读指令后,数据从外部设备拷贝到内存需要一段时间,这段时间内用户线程可以
- 让出CPU资源,CPU执行其他任务 - 用户线程阻塞
- 让CPU不停查询数据是否拷贝完成 - 用户线程非阻塞
- 网络 I/O 通信涉及调用这个 I/O 操作的用户线程和操作系统内核
- 一个进程的地址空间分为用户空间和内核空间,用户线程不能直接访问内核空间
- 当用户线程发起 I/O 操作后,网络数据读取操作经历以下两步
- 数据准备阶段: 用户线程等待内核将数据从网卡拷贝到内核空间
- 数据拷贝阶段: 内核将数据从内核空间拷贝到用户空间(应用程序的缓冲区)
- 同步指内核将数据拷贝到用户空间后需要唤醒用户线程处理数据,异步指用户线程发起read后只需等待最终数据处理的结果即可
2.Java I/O 模型
- 同步阻塞 I/O
- 用户线程发起read调用后即阻塞,让出CPU
- 内核在网卡数据到达后把数据从网卡拷贝到内核空间,再从内核空间拷贝到用户空间,并唤醒用户线程
- 同步非阻塞 I/O
- 用户线程反复发起read调用,直到数据到达内核空间
- 用户线程在等待数据从内核空间拷贝到用户空间这段时间里还是阻塞的
- 内核把数据拷贝到用户空间后唤醒用户线程
- I/O多路复用
- 用户线程读取操作分为select和read两步
- select调用询问内核数据是否准备好,一次select调用可以向内核查询多个数据通道(Channel)的状态,故称多路复用
- select查询到内核数据就绪后,用户线程再发起read调用,内核从内核空间拷贝数据到用户空间这段时间,用户线程还是阻塞的
- 异步 I/O
- 用户线程发起read调用的同时注册一个回调函数并立即返回
- 内核拷贝数据到用户空间后,调用回调函数完成数据处理后通知用户线程
3.Tomcat I/O 模型
I/O模型
- BIO(JioEndpoint): 同步阻塞式 I/O,适合连接数目小且固定架构, Tomcat8.5后移除
- NIO(NioEndpoint): 同步非阻塞式 I/O,基于多路复用,适合连接数多且连接比较短的架构(聊天服务器、服务器间通讯)
- AIO(Nio2Endpoint): 异步非阻塞式 I/O,适用于连接数较多且连接时间较长的应用
- APR(AprEndpoint): Apache Portal Runtime(Apache可移植运行库),通过JNI调用APR本地库实现非阻塞 I/O
Tomcat I/O (Connector)选型
- 多数情况使用默认的NIO
- Web应用使用TLS加密并对性能要求极高时,使用APR
- Windows平台,且HTTP请求的数据量较大,使用NIO2(Windows从操作系统层面实现了真正意义上的异步I/O)
- Linux中Java NIO和NIO2底层都是通过epoll实现,NIO更简单高效,建议使用
谁谓河广?一苇航之。谁谓宋远?企予望之。