【192期】面试官问:说说 Tomcat 组成与工作原理?
点击上方“Java精选”,选择“设为星标”
别问别人为什么,多问自己凭什么!
下方有惊喜,留言必回,有问必答!
每天 08:15 更新文章,每天进步一点点...
所有容器都是继承自 ContainerBase,基类中封装了容器中的重复工作,负责启动容器相关的组 件 Loader、Logger、Manager、Cluster、Pipeline,启动子容器(线程池并发启动子容器,通过 线程池 submit 多个线程,调用后返回 Future 对象,线程内部启动子容器,接着调用 Future 对象 的 get 方法来等待执行结果)。
List<Future<Void>> results = new ArrayList<Future<Void>>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
boolean fail = false;
for (Future<Void> result :results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
fail = true;
}
}
Server.xml 配置 Host 元素,指定 appBase 属性,默认$catalina.base/webapps/ Server.xml 配置 Context 元素,指定 docBase,元素,指定 web 应用的路径 自定义配置:在$catalina.base/EngineName/HostName/XXX.xml 配置 Context 元素
扫描 appbase 路径下的所有文件夹和 war 包,解析各个应用的 META-INF/context.xml,并 创建 StandardContext,并将 Context 加入到 Host 的子容器中。 解析$catalina.base/EngineName/HostName/下的所有 Context 配置,找到相应 web 应 用的位置,解析各个应用的 META-INF/context.xml,并创建 StandardContext,并将 Context 加入到 Host 的子容器中。
HostConfig 并没有实际解析 Context.xml,而是在 ContextConfig 中进行的。 HostConfig 中会定期检查 watched 资源文件(context.xml 配置文件)
先解析全局的配置 config/context.xml 然后解析 Host 的默认配置 EngineName/HostName/context.xml.default 最后解析应用的 META-INF/context.xml
先解析全局的配置 config/web.xml 然后解析 Host 的默认配置 EngineName/HostName/web.xml.default 接着解析应用的 MEB-INF/web.xml 扫描应用 WEB-INF/lib/下的 jar 文件,解析其中的 META-INF/web-fragment.xml 最后合并 xml 封装成 WebXml,并设置 Context
扫描 web 应用和 jar 中的注解(Filter、Listener、Servlet)就是上述步骤中进行的。 容器的定期执行:backgroundProcess,由 ContainerBase 来实现的,并且只有在顶层容器 中才会开启线程。(backgroundProcessorDelay=10 标志位来控制)
请求到达 server 端,server 根据 url 映射到相应的 Servlet 判断 Servlet 实例是否存在,不存在则加载和实例化 Servlet 并调用 init 方法 Server 分别创建 Request 和 Response 对象,调用 Servlet 实例的 service 方法(service 方法 内部会根据 http 请求方法类型调用相应的 doXXX 方法) doXXX 方法内为业务逻辑实现,从 Request 对象获取请求参数,处理完毕之后将结果通过 response 对象返回给调用方 当 Server 不再需要 Servlet 时(一般当 Server 关闭时),Server 调用 Servlet 的 destroy() 方 法。
根据 server.xml 配置的指定的 connector 以及端口监听 http、或者 ajp 请求 请求到来时建立连接,解析请求参数,创建 Request 和 Response 对象,调用顶层容器 pipeline 的 invoke 方法 容器之间层层调用,最终调用业务 servlet 的 service 方法 Connector 将 response 流中的数据写到 socket 中
各层容器对应的 basic valve 分别是 StandardEngineValve、StandardHostValve、 StandardContextValve、StandardWrapperValve。JSP引擎
编译阶段:servlet 容器编译 servlet 源文 件,生成 servlet 类 初始化阶段:加载与 JSP 对应的 servlet 类, 创建其实例,并调用它的初始化方法 执行阶段:调用与 JSP 对应的 servlet 实例的 服务方法 销毁阶段:调用与 JSP 对应的 servlet 实例的 销毁方法,然后销毁 servlet 实例
代码片段:<% 代码片段 %> JSP声明:<%! declaration; [ declaration; ]+ ... %> JSP表达式:<%= 表达式 %> JSP注释:<%-- 注释 --%> JSP指令: <%@ directive attribute=“value” %> JSP行为: <jsp:action_name attribute=“value” /> HTML元素:html/head/body/div/p/… JSP隐式对象:request、response、out、session、application、config、 pageContext、page、Exception
代码片段:包含任意量的 Java 语句、变量、方法或表达式; JSP 声明:一个声明语句可以声明一个或多个变量、方法,供后面的 Java 代码使用; JSP 表达式:输出 Java 表达式的值,String 形式; JSP 注释:为代码作注释以及将某段代码注释掉 JSP 指令:用来设置与整个 JSP 页面相关的属性, <%@ page ... %>定义页面的依赖属性,比如 language、contentType、errorPage、 isErrorPage、import、isThreadSafe、session 等等 <%@ include ... %>包含其他的 JSP 文件、HTML 文件或文本文件,是该 JSP 文件的一部分,会 被同时编译执行 <%@ taglib ... %>引入标签库的定义,可以是自定义标签 JSP 行为:jsp:include、jsp:useBean、jsp:setProperty、jsp:getProperty、jsp:forward
代码片段:在_jspService()方法内直接输出 JSP 声明: 在 servlet 类中进行输出 JSP 表达式:在_jspService()方法内直接输出 JSP 注释:直接忽略,不输出 JSP 指令:根据不同指令进行区分,include:对引入的文件进行解析;page 相关的属性会做为 JSP 的属性,影响的是解析和请求处理时的行为 JSP 行为:不同的行为有不同的处理方式,jsp:useBean 为例,会从 pageContext 根据 scope 的 类别获取 bean 对象,如果没有会创建 bean,同时存到相应 scope 的 pageContext 中 HTML:在_jspService()方法内直接输出 JSP 隐式对象:在_jspService()方法会进行声明,只能在方法中使用;
JIO:用java.io编写的TCP模块,阻塞IO NIO:用java.nio编写的TCP模块,非阻塞IO,(IO多路复用) APR:全称Apache Portable Runtime,使用JNI的方式来进行读取文件以及进行网络传输
Support Polling:是否支持基于IO多路复用的socket事件轮询 Polling Size:轮询的最大连接数 Wait for next Request:在等待下一个请求时,处理线程是否释放,BIO是没有释放的,所以在keep-alive=true的情况下处理的并发连接数有限 Read Request Headers:由于request header数据较少,可以由容器提前解析完毕,不需要阻塞 Read Request Body:读取request body的数据是应用业务逻辑的事情,同时Servlet的限制,是需要阻塞读取的 Write Response:跟读取request body的逻辑类似,同样需要阻塞写
Begin:新的请求连接接入调用,可进行与Request和Response相关的对象初始化操作,并保存response对象,用于后续写入数据 Read:请求连接有数据可读时调用 End:当数据可用时,如果读取到文件结束或者response被关闭时则被调用 Error:在连接上发生异常时调用,数据读取异常、连接断开、处理异常、socket超时
Read:在post请求有数据,但在begin事件中没有处理,则会调用read,如果read没有读取数据,在会触发Error回调,关闭socket End:当socket超时,并且response被关闭时也会调用;server被关闭时调用 Error:除了socket超时不会关闭socket,其他都会关闭socket End和Error时间触发时应关闭当前comet会话,即调用CometEvent的close方法 Note:在事件触发时要做好线程安全的操作
首先,Servlet 接收到请求之后,request数据解析; 接着,调用业务接口的某些方法,以完成业务处理; 最后,根据处理的结果提交响应,Servlet 线程结束
客户端发送一个请求 Servlet容器分配一个线程来处理容器中的一个servlet servlet调用request.startAsync(),保存AsyncContext, 然后返回 任何方式存在的容器线程都将退出,但是response仍然保持开放 业务线程使用保存的AsyncContext来完成响应(线程池) 客户端收到响应
onStartAsync:Request调用startAsync方法时触发 onComplete:syncContext调用complete方法时触发 onError:处理请求的过程出现异常时触发 onTimeout:socket超时触发
作者:VectorJin
https://juejin.cn/post/6844903473482317837
3000+ 道面试题在线刷,最新、最全 Java 面试题!
【184期】4 种方案,解决使用 Maven 时构建项目过慢问题,保证速度杠杠的!
【185期】RocketMQ 主机磁盘空间有限,如何无限期延长消息存储?
【186期】面试官问:OAuth2 vs JWT,到底怎么选择?
【187期】Java 中的 BigDecimal 运算,如何解决精度丢失问题?
【189期】利用 Fastjson 注入 Spring MVC 内存马,太秀了~!
最近有很多人问,有没有读者&异性交流群,你懂的!想知道如何加入。加入方式很简单,有兴趣的同学,只需要点击下方卡片,回复“加群”,即可免费加入交流群!