前文:
《Tomcat源码:启动类Bootstrap与Catalina的加载》
《Tomcat源码:容器的生命周期管理与事件监听》
《Tomcat源码:StandardServer与StandardService》
《Tomcat源码:Container接口》
《Tomcat源码:StandardEngine、StandardHost、StandardContext、StandardWrapper》
《Tomcat源码:Pipeline与Valve》
《Tomcat源码:连接器与Executor、Connector》
《Tomcat源码:ProtocolHandler与Endpoint》
《Tomcat源码:Acceptor与Poller、PollerEvent》
《Tomcat源码:SocketProcessor、ConnectionHandler与Http11Processor》
前言
在前文中,我们介绍了Processor如何接收来自EndPoint的Socket,读取字节流解析成 Tomcat Request 和 Response 对象,最后将请求传递给了Adapter做进一步的处理。本文我们就来介绍下一个请求是如何从连接器被转发到容器中并由相应的servlet处理的。
目录
前言
一、CoyoteAdapter
1、CoyoteAdapter
2、service
3、invoke
二、Valve#invoke
1、invoke
2、wrapper.allocate
3、loadServlet
4、initServlet
三、ApplicationFilterChain
1、createFilterChain
findFilterMaps
2、doFilter
internalDoFilter
一、CoyoteAdapter
1、CoyoteAdapter
CoyoteAdapter的创建是在Connector#initInternal中完成的,这里会new一个CoyoteAdapter对象并将当前的Connector 对象传入其中。
public class Connector extends LifecycleMBeanBase {
/**
* Coyote adapter.
*/
protected Adapter adapter = null;
protected void initInternal() throws LifecycleException {
super.initInternal();
// Initialize adapter
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
// ...
}
CoyoteAdapter类继承自 Adapter 接口,该接口定义了适配器的基本功能。
public class CoyoteAdapter implements Adapter {
/**
* Construct a new CoyoteProcessor associated with the specified connector.
*
* @param connector CoyoteConnector that owns this processor
*/
public CoyoteAdapter(Connector connector) {
super();
this.connector = connector;
}
}
2、service
然后我们来看service方法。
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) throws Exception {
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
if (request == null) {
// Create objects
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
// Set query string encoding
req.getParameters().setQueryStringCharset(connector.getURICharset());
}
// ...
try {
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
}
request.finishRequest();
response.finishResponse();
} catch (IOException e) {
} finally {
// ...
}
}
首先根据 org.apache.coyote.Request#getNote(ADAPTER_NOTES) 和 org.apache.coyote.Response#getNote(ADAPTER_NOTES) 来获取 org.apache.catalina.connector.Request 和 org.apache.catalina.connector.Response 对象,
如果获取不到 org.apache.catalina.connector.Request 对象,就创建 org.apache.catalina.connector.Request 和 org.apache.catalina.connector.Response 对象,并分别设置到 notes 数组的 notes[1] 元素里。
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
public final class Request {
/**
* Notes.
*/
private final Object notes[] = new Object[Constants.MAX_NOTES];
public Object getNote(int pos) {
return notes[pos];
}
}
public final class Response {
/**
* Notes.
*/
final Object notes[] = new Object[Constants.MAX_NOTES];
public Object getNote(int pos) {
return notes[pos];
}
}
Connector 的 createRequest 和 createResponse 也很简单。
/**
* Create (or allocate) and return a Request object suitable for specifying the contents of a Request to the
* responsible Container.
*
* @return a new Servlet request object
*/
public Request createRequest() {
Request request = new Request();
request.setConnector(this);
return (request);
}
/**
* Create (or allocate) and return a Response object suitable for receiving the contents of a Response from the
* responsible Container.
*
* @return a new Servlet response object
*/
public Response createResponse() {
Response response = new Response();
response.setConnector(this);
return (response);
}
然后就进入 try-catch 语句块。首先调用 postParseRequest 方法,这个方法是在 http 的 header 解析完之后,对 Request 和 Response 做一些设置的工作,里面包扩了uri参数解析,Host映射等重要步骤。
3、invoke
调用完 postParseRequest 后就进入关键代码 if (postParseRequest) 里,在这个 if 里,先调用一下 request.setAsyncSupported,然后调用
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
这里的connector 是在 CoyoteAdapter 初始化时传入的,根据我们前文中对容器组件结构的介绍,这里connector.getService() 返回的是 Connector 关联的 Service 属性,而后续的getContainer() 返回的是 Service 里的容器 Engine 属性,至于getPipeline().getFirst()则是获取Engine容器的Valve。(这块内容我们在前文《Tomcat源码:Pipeline与Valve》已做过详细介绍,这里就不赘述了)
public CoyoteAdapter(Connector connector) {
super();
this.connector = connector;
}
由于各层容器见的关系如下图所示,因此最后会调用StandardWrapperValve类的invoke方法。
// StandardEngineValve.java
public void invoke(Request request, Response response) throws IOException, ServletException {
// 获取一个 Host 对象,获取不到就直接返回
Host host = request.getHost();
if (host == null) {
if (!response.isError()) {
response.sendError(404);
}
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
host.getPipeline().getFirst().invoke(request, response);
}
// StandardHostValve.java
public void invoke(Request request, Response response) throws IOException, ServletException {
Context context = request.getContext();
if (context == null) {
if (!response.isError()) {
response.sendError(404);
}
return;
}
// 其余代码
context.getPipeline().getFirst().invoke(request, response);
}
// StandardContextValve.java
public void invoke(Request request, Response response) throws IOException, ServletException {
Wrapper wrapper = request.getWrapper();
if (wrapper == null || wrapper.isUnavailable()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// Acknowledge the request
try {
response.sendAcknowledgement(ContinueResponseTiming.IMMEDIATELY);
} catch (IOException ioe) {
container.getLogger().error(sm.getString("standardContextValve.acknowledgeException"), ioe);
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
wrapper.getPipeline().getFirst().invoke(request, response);
}
整个流程如下图所示,StandardEngineValve、StandardHostValve、StandardContextValve这三个 Valve 的 invoke 方法的核心逻辑就是调用子容器的 Pipeline 的 Valve 的invoke 方法,也就是 StandardEngineValve#invoke -> StandardHostValve#invoke -> StandardContextValve#invoke -> StandardWrapper#invoke 方法。
最后response.finishResponse()负责将数据返回给客户端。
request.finishRequest();
response.finishResponse();
二、Valve#invoke
1、invoke
紧接上文,invoke(request, response)最后会调用StandardWrapperValve类的invoke方法。这段比较长,下面我们来分别讲解下
final class StandardWrapperValve extends ValveBase {
public void invoke(Request request, Response response) throws IOException, ServletException {
boolean unavailable = false;
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
Context context = (Context) wrapper.getParent();
// Allocate a servlet instance to process this request
try {
if (!unavailable) {
servlet = wrapper.allocate();
}
} catch (Throwable e) {
// ...
}
// Create the filter chain for this request
ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
Container container = this.container;
try {
if ((servlet != null) && (filterChain != null)) {
// Swallow output if needed
if (context.getSwallowOutput()) {
try {
SystemLogHandler.startCapture();
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
filterChain.doFilter(request.getRequest(), response.getResponse());
}
} finally {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
context.getLogger().info(log);
}
}
} else {
if (request.isAsyncDispatching()) {
request.getAsyncContextInternal().doInternalDispatch();
} else {
filterChain.doFilter(request.getRequest(), response.getResponse());
}
}
}
} finally {
// ...
}
}
}
2、wrapper.allocate
首先是wrapper.allocate(),其实现类在StandardWrapper中,从注释中可以看出,这里会先根据singleThreadModel 属性判断是否为单线程模型,然后根据不同的情况选择调用loadServlet、initServlet来加载或是初始化servlet对象。
public class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper, NotificationEmitter {
/**
* Does this servlet implement the SingleThreadModel interface?
*
* @deprecated This will be removed in Tomcat 10.1 onwards.
*/
@Deprecated
protected volatile boolean singleThreadModel = false;
/**
* The (single) possibly uninitialized instance of this servlet.
*/
protected volatile Servlet instance = null;
public Servlet allocate() throws ServletException {
boolean newInstance = false;
// If not SingleThreadedModel, return the same instance every time
if (!singleThreadModel) {
// Load and initialize our instance if necessary
if (instance == null || !instanceInitialized) {
synchronized (this) {
if (instance == null) {
try {
instance = loadServlet();
newInstance = true;
if (!singleThreadModel) {
countAllocated.incrementAndGet();
}
} catch (Throwable e) {
// ...
}
}
if (!instanceInitialized) {
initServlet(instance);
}
}
}
if (singleThreadModel) {
if (newInstance) {
synchronized (instancePool) {
instancePool.push(instance);
nInstances++;
}
}
} else {
if (!newInstance) {
countAllocated.incrementAndGet();
}
return instance;
}
}
synchronized (instancePool) {
while (countAllocated.get() >= nInstances) {
// Allocate a new instance if possible, or else wait
if (nInstances < maxInstances) {
try {
instancePool.push(loadServlet());
nInstances++;
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("standardWrapper.allocate"), e);
}
} else {
try {
instancePool.wait();
} catch (InterruptedException e) {
// Ignore
}
}
}
countAllocated.incrementAndGet();
return instancePool.pop();
}
}
}
3、loadServlet
loadServlet这里的逻辑很简单,就是通过instanceManager.newInstance(servletClass)来创建
/**
* The fully qualified servlet class name for this servlet.
*/
protected String servletClass = null;
public synchronized Servlet loadServlet() throws ServletException {
// Nothing to do if we already have an instance or an instance pool
if (!singleThreadModel && (instance != null)) {
return instance;
}
Servlet servlet;
try {
long t1 = System.currentTimeMillis();
// Complain if no servlet class has been specified
if (servletClass == null) {
unavailable(null);
throw new ServletException(sm.getString("standardWrapper.notClass", getName()));
}
InstanceManager instanceManager = ((StandardContext) getParent()).getInstanceManager();
try {
servlet = (Servlet) instanceManager.newInstance(servletClass);
} catch (Throwable e) {
// ...
}
initServlet(servlet);
} finally {
// ...
}
return servlet;
}
getParent()).getInstanceManager()回到StandardContext容器中获取 instanceManager对象,这个对象是在StandardContext的startInternal方法中初始化的,默认的实现类为DefaultInstanceManager ,从其具体实现可以看到这里newInstance实际上就是调用servlet的构造方法创建了实例(clazz.getConstructor().newInstance())。
public class DefaultInstanceManager implements InstanceManager {
@Override
public Object newInstance(String className)
throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException,
ClassNotFoundException, IllegalArgumentException, NoSuchMethodException, SecurityException {
Class<?> clazz = loadClassMaybePrivileged(className, classLoader);
return newInstance(clazz.getConstructor().newInstance(), clazz);
}
}
4、initServlet
servlet实例创建好了之后就是调用initServlet来初始化了,内容也很简单,就是调用了servlet的初始化方法init(),我们之前有专门介绍过servlet,有兴趣的可以看下这篇文章《Tomcat:servlet与servlet容器》
private synchronized void initServlet(Servlet servlet) throws ServletException {
if (instanceInitialized && !singleThreadModel) {
return;
}
// Call the initialization method of this servlet
try {
if (Globals.IS_SECURITY_ENABLED) {
boolean success = false;
try {
Object[] args = new Object[] { facade };
SecurityUtil.doAsPrivilege("init", servlet, classType, args);
success = true;
} finally {
if (!success) {
SecurityUtil.remove(servlet);
}
}
} else {
servlet.init(facade);
}
instanceInitialized = true;
}catch (Throwable f) {
// ...
}
}
在获取到了servlet对象后,下一步会获取过滤器调用链,并使用doFilter方法来进行过滤。
三、ApplicationFilterChain
1、createFilterChain
createFilterChain从名字可以看出来是创建过滤链。首先创建ApplicationFilterChain对象,然后调用context.findFilterMaps()获取所有的过滤器,最后通过映射路径和servlet名来匹配能作用当前servlet上的过滤器,最后调用context.findFilterConfig来获取一个 ApplicationFilterConfig 对象,并调用filterChain.addFilter(filterConfig) 把这个对象加入到 filterChain 里。
public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
// If there is no servlet to execute, return null
if (servlet == null) {
return null;
}
// Create and initialize a filter chain object
ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
Request req = (Request) request;
if (Globals.IS_SECURITY_ENABLED) {
// Security: Do not recycle
filterChain = new ApplicationFilterChain();
} else {
filterChain = (ApplicationFilterChain) req.getFilterChain();
if (filterChain == null) {
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
}
}
} else {
// Request dispatcher in use
filterChain = new ApplicationFilterChain();
}
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// Acquire the filter mappings for this Context
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// If there are no filter mappings, we are done
if ((filterMaps == null) || (filterMaps.length == 0)) {
return filterChain;
}
// Acquire the information we will need to match filter mappings
DispatcherType dispatcher = (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
String requestPath = null;
Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null) {
requestPath = attribute.toString();
}
String servletName = wrapper.getName();
// Add the relevant path-mapped filters to this filter chain
for (FilterMap filterMap : filterMaps) {
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
if (!matchFiltersURL(filterMap, requestPath)) {
continue;
}
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context
.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// Add filters that match on servlet name second
for (FilterMap filterMap : filterMaps) {
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
if (!matchFiltersServlet(filterMap, servletName)) {
continue;
}
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context
.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}
filterChain.addFilter(filterConfig);
}
// Return the completed filter chain
return filterChain;
}
findFilterMaps
StandardContext中定义了filterMaps用来保存应用程序里的 web.xml 文件里配置的 Filter 的相关信息的,比如名字、作用路径等。因此拿到 FilterMap[] 数组后,会分别调用matchDispatcher、matchFiltersURL这两个 for 循环。
/**
* @return the set of filter mappings for this Context.
*/
@Override
public FilterMap[] findFilterMaps() {
return filterMaps.asArray();
}
在前文介绍StandardContext时我们提到过在web.xml被解析时会给context添加了一个监听器ContextConfig,这给监听器内部会调用方法为context容器查找出web.xml中所有的过滤器,并调用addFilterDef、addFilterMap将过滤器以不同的形式和内容注入到context中去。
StandardContext#startInternal中会调用filterStart方法,该方法内会将过滤器封装到filterConfigs中,供上文中的context.findFilterConfig方法调用。
public class ContextConfig implements LifecycleListener {
private void configureContext(WebXml webxml) {
for (FilterDef filter : webxml.getFilters().values()) {
if (filter.getAsyncSupported() == null) {
filter.setAsyncSupported("false");
}
context.addFilterDef(filter);
}
for (FilterMap filterMap : webxml.getFilterMappings()) {
context.addFilterMap(filterMap);
}
}
}
public class StandardContext extends ContainerBase implements Context, NotificationEmitter {
/**
* The set of filter definitions for this application, keyed by filter name.
*/
private HashMap<String, FilterDef> filterDefs = new HashMap<>();
/**
* The set of filter mappings for this application, in the order they were defined in the deployment descriptor with
* additional mappings added via the {@link ServletContext} possibly both before and after those defined in the
* deployment descriptor.
*/
private final ContextFilterMaps filterMaps = new ContextFilterMaps();
private HashMap<String, ApplicationFilterConfig> filterConfigs = new HashMap<>();
public void addFilterMap(FilterMap filterMap) {
validateFilterMap(filterMap);
// Add this filter mapping to our registered set
filterMaps.add(filterMap);
fireContainerEvent("addFilterMap", filterMap);
}
public FilterMap[] findFilterMaps() {
return filterMaps.asArray();
}
public boolean filterStart() {
// Instantiate and record a FilterConfig for each defined filter
boolean ok = true;
synchronized (filterConfigs) {
filterConfigs.clear();
for (Entry<String, FilterDef> entry : filterDefs.entrySet()) {
String name = entry.getKey();
if (getLogger().isDebugEnabled()) {
getLogger().debug(" Starting filter '" + name + "'");
}
try {
ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, entry.getValue());
filterConfigs.put(name, filterConfig);
} catch (Throwable t) {
t = ExceptionUtils.unwrapInvocationTargetException(t);
ExceptionUtils.handleThrowable(t);
getLogger().error(sm.getString("standardContext.filterStart", name), t);
ok = false;
}
}
}
return ok;
}
protected synchronized void startInternal() throws LifecycleException {
// ...
if (ok) {
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
}
public FilterConfig findFilterConfig(String name) {
return filterConfigs.get(name);
}
}
2、doFilter
doFilter会将请求一层层的经过上文中匹配到的过滤器链中的各个组件处理,可以看到这里其实是调用了internalDoFilter方法
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (Globals.IS_SECURITY_ENABLED) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws ServletException, IOException {
internalDoFilter(req, res);
return null;
}
});
} catch (PrivilegedActionException pe) {
...
}
} else {
internalDoFilter(request, response);
}
}
internalDoFilter
internalDoFilter 方法里分了两部分,if (pos < n) 和 if 后面的 try-catch 语句。
if (pos < n)先从 filter 是数组里获取下一个 ApplicationFilterConfig 对象。
ApplicationFilterConfig filterConfig = filters[pos++];
然后通过 filterConfig.getFilter() 获取一个 Filter 对象,并调用这个 Filter 的 doFilter 方法,并把 ApplicationFilterChain 对象传入 Filter#doFilter。
在 if (pos < n) 里的 n 是这个 ApplicationFilterChain 里包含的 Filter 的总数。每次调用 internalDoFilter 方法,pos 就加1,也就是获取下一个 ApplicationFilterConfig,直到 pos 等于 n 的时候才不执行 if 语句,为了达到这个效果,应用程序必须在自定义的在 Filter 里执行 FilterChain#doFilter 方法,才能再次进入 ApplicationFilterChain#internalDoFilter 方法,这样就完成了调用 ApplicationFilterChain 里所有的 Filter 的 doFilter 方法。
这种方式是责任链模式的一种体现。
internalDoFilter里的 try-catch 是在执行完所有的 Filter#doFilter 方法之后执行的。
try 语句块里先设置一下属性,然后执行
servlet.service(request, response);
通过调用 Servlet 对象的 service 方法来处理请求。到这里,tomcat 终于把请求交给了应用程序了,tomcat 整个接受请求、转发处理请求的流程就差不多完结了。
private void internalDoFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() &&
"false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if (Globals.IS_SECURITY_ENABLED) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal = ((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[] { req, res, this };
SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
// We fell off the end of the chain -- call the servlet instance
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal = ((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[] { req, res };
SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal);
} else {
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
}
本文中流程处理过程总结如下图,到这里我们整个tomcat请求传递与映射的逻辑就整理完了,总体来看整个tomcat使用连接器与容器两部分来分别处理请求的传递与处理,并使用了一些方法来增加了并发以及吞吐能力。