作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
学习必须往深处挖,挖的越深,基础越扎实!
阶段1、深入多线程
阶段2、深入多线程设计模式
阶段3、深入juc源码解析
码哥源码部分
码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】
码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】
码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】
码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】
打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】
我们接着上一篇文章的容器处理来讲,当postParseRequest方法返回true时,则由容器继续处理,在service方法中有 connector.getService().getContainer().getPipeline().getFirst().invoke(request, response) 这一行:
- Connector调用getService()返回StandardService;
- StandardService调用getContainer返回StandardEngine;
- StandardEngine调用getPipeline返回与其关联的StandardPipeline;
Engine处理请求
我们在前面的文章中讲过 StandardEngine 的构造函数为自己的Pipeline添加了基本阀 StandardEngineValve ,代码如下:
public StandardEngine() {
super();
pipeline.setBasic( new StandardEngineValve());
try {
setJvmRoute(System.getProperty("jvmRoute"));
} catch(Exception ex) {
log.warn(sm.getString("standardEngine.jvmRouteFail"));
}
}
接下来我们看看StandardEngineValve的invoke()方法。该方法主要是选择合适的Host,然后调用Host中pipeline的第一个Valve的invoke()方法。
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Host to be used for this Request
Host host = request.getHost();
if (host == null) {
response.sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
// Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}
该方法很简单,校验该Engline 容器是否含有Host容器,如果不存在,返回400错误,否则继续执行 host.getPipeline().getFirst().invoke(request, response),可以看到 Host 容器先获取自己的管道,再获取第一个阀门,我们再看看该阀门的 invoke 方法。
Host处理请求
分析Host的时候,我们从Host的构造函数入手,该方法主要是设置基础阀门。
public StandardHost() {
super();
pipeline.setBasic(new StandardHostValve());
}
StandardPipeline调用getFirst得到第一个阀去处理请求,由于基本阀是最后一个,所以最后会由基本阀去处理请求。
StandardHost的Pipeline里面一定有 ErrorReportValve 与 StandardHostValve两个Valve,ErrorReportValve主要是检测 Http 请求过程中是否出现过什么异常, 有异常的话, 直接拼装 html 页面, 输出到客户端。
我们看看ErrorReportValve的invoke方法:
public void invoke(Request request, Response response)
throws IOException, ServletException {
// Perform the request
// 1. 先将 请求转发给下一个 Valve
getNext().invoke(request, response);
// 2. 这里的 isCommitted 表明, 请求是正常处理结束
if (response.isCommitted()) {
return;
}
// 3. 判断请求过程中是否有异常发生
Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
if (request.isAsyncStarted() && ((response.getStatus() < 400 &&
throwable == null) || request.isAsyncDispatching())) {
return;
}
if (throwable != null) {
// The response is an error
response.setError();
// Reset the response (if possible)
try {
// 4. 重置 response 里面的数据(此时 Response 里面可能有些数据)
response.reset();
} catch (IllegalStateException e) {
// Ignore
}
// 5. 这就是我们常看到的 500 错误码
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
response.setSuspended(false);
try {
// 6. 这里就是将 异常的堆栈信息组合成 html 页面, 输出到前台
report(request, response, throwable);
} catch (Throwable tt) {
ExceptionUtils.handleThrowable(tt);
}
if (request.isAsyncStarted()) {
// 7. 若是异步请求的话, 设置对应的 complete (对应的是 异步 Servlet)
request.getAsyncContext().complete();
}
}
该方法首先执行了下个阀门的 invoke 方法。然后根据返回的Request 属性设置一些错误信息。那么下个阀门是谁呢?其实就是基础阀门了:StandardHostValve,该阀门的 invoke 的方法是如何实现的呢?
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Context to be used for this Request
Context context = request.getContext();
if (context == null) {
response.sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
sm.getString("standardHost.noContext"));
return;
}
// Bind the context CL to the current thread
if( context.getLoader() != null ) {
// Not started - it should check for availability first
// This should eventually move to Engine, it's generic.
if (Globals.IS_SECURITY_ENABLED) {
PrivilegedAction<Void> pa = new PrivilegedSetTccl(
context.getLoader().getClassLoader());
AccessController.doPrivileged(pa);
} else {
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
}
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(context.getPipeline().isAsyncSupported());
}
// Don't fire listeners during async processing
// If a request init listener throws an exception, the request is
// aborted
boolean asyncAtStart = request.isAsync();
// An async error page may dispatch to another resource. This flag helps
// ensure an infinite error handling loop is not entered
boolea

本文接着上一篇文章的容器处理继续讲解,详细介绍了Tomcat中Engine、Host、Context和Wrapper处理请求的流程。如Engine选择合适的Host,Host处理时检测异常等。还罗列了Wrapper处理请求的几个关键点,包括分配Servlet实例、创建过滤器链等,并对部分关键点进行了源码分析。
最低0.47元/天 解锁文章
621

被折叠的 条评论
为什么被折叠?



