2. Фаза запуска Tomcat
- daemon.start(), анализ фазы запуска tomcat
# 1.Bootstrap的start()方法
public void start()
throws Exception {
if( catalinaDaemon==null ) init();
//实际上是执行了catalina的start()方法
Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
method.invoke(catalinaDaemon, (Object [])null);
}
# 1.Catalina的start()方法
public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
long t1 = System.nanoTime();
// Start the new server
try {
//启动Server(重点)
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
}
// Register shutdown hook 钩子
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
// If JULI is being used, disable JULI's shutdown hook since
// shutdown hooks run in parallel and log messages may be lost
// if JULI's hook completes before the CatalinaShutdownHook()
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
false);
}
}
if (await) {
await();
stop();
}
}
- getServer().start(), метод запускает Сервер, анализ исходного кода
# 1.LifecycleBase的start()方法
@Override
public final synchronized void start() throws LifecycleException {
if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
}
return;
}
if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
//启动子类的StandardServer(重点)
startInternal();
if (state.equals(LifecycleState.FAILED)) {
// This is a 'controlled' failure. The component put itself into the
// FAILED state so call stop() to complete the clean-up.
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
// This is an 'uncontrolled' failure so put the component into the
// FAILED state and throw an exception.
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
}
}
# 2.standardServer的startInternal()方法
@Override
protected void startInternal() throws LifecycleException {
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
globalNamingResources.start();
// Start our defined Services
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
//启动services(重点)
services[i].start();
}
}
}
Метод инициализации start() сервера сначала вызовет метод start() родительского класса LifecycleBase, а затем вызовет метод startInternal() подкласса Server. Режим вызова: запуск родительского класса ---> запуск подклассаInternal, в следующих службах, коннекторе, инициализация движка - тот же режим.
- services[i].start(), запуск служб, анализ исходного кода
# 1.standardService的startInternal()方法
@Override
protected void startInternal() throws LifecycleException {
if(log.isInfoEnabled())
log.info(sm.getString("standardService.start.name", this.name));
setState(LifecycleState.STARTING);
//启动engine(重点)
// Start our defined Container first
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
//启动mapperListener
mapperListener.start();
// Start our defined Connectors second
synchronized (connectorsLock) {
for (Connector connector: connectors) {
try {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
//启动connector(重点)
connector.start();
}
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
}
}
Из анализа запуска сервисов можно получить автозапуск сервисов, который запустит движок и коннектор. (исполнитель, мапперслушатель)
- коннектор.start(), запуск коннектора, анализ исходного кода
# 1.Connector的startInternal()方法
@Override
protected void startInternal() throws LifecycleException {
// Validate settings before starting
if (getPort() < 0) {
throw new LifecycleException(sm.getString(
"coyoteConnector.invalidPort", Integer.valueOf(getPort())));
}
setState(LifecycleState.STARTING);
try {
//启动protocolHandler(重点)
protocolHandler.start();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
}
}
# 2.AbstractProtocol下protocolHandler的启动start()方法
@Override
public void start() throws Exception {
if (getLog().isInfoEnabled()) {
getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
}
//启动endpoint
endpoint.start();
// Start async timeout thread
asyncTimeout = new AsyncTimeout();
Thread timeoutThread = new Thread(asyncTimeout, getNameInternal() + "-AsyncTimeout");
int priority = endpoint.getThreadPriority();
if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
priority = Thread.NORM_PRIORITY;
}
timeoutThread.setPriority(priority);
timeoutThread.setDaemon(true);
timeoutThread.start();
}
# 3.AbstractEndpoint下endpoint的启动start()方法
public final void start() throws Exception {
if (bindState == BindState.UNBOUND) {
bind();
bindState = BindState.BOUND_ON_START;
}
startInternal();
}
Из анализа запуска соединителя можно получить информацию о запуске соединителя, и будут запущены обработчик протокола и конечная точка.
- engine.start(), запуск двигателя, анализ исходного кода
# 1.StandardEngine的startInternal()方法
@Override
protected synchronized void startInternal() throws LifecycleException {
// Log our server identification information
if(log.isInfoEnabled())
log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());
// Standard container startup
super.startInternal();
}
# 2.ContainerBase的startInternal()方法
@Override
protected synchronized void startInternal() throws LifecycleException {
// Start our subordinate components, if any
logger = null;
//日志处理
getLogger();
//有集群启动集群
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start();
}
//域处理
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
//启动所有子容器 StandardHost, StandardContext, StandardWrapper
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
MultiThrowable multiThrowable = null;
//开启线程启动子容器
for (Future<Void> result : results) {
try {
//阻塞,让子容器启动完成再执行下面的代码
result.get();
} catch (Throwable e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
if (multiThrowable == null) {
multiThrowable = new MultiThrowable();
}
multiThrowable.add(e);
}
}
if (multiThrowable != null) {
throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
multiThrowable.getThrowable());
}
//启用Pipeline管道
// Start the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
//设置生命周期的状态starting,激发监听器listener:HostConfig,通过这个去启动Host(重要)
setState(LifecycleState.STARTING);
//开启线程
threadStart();
}
# 3.StandardHost的startInternal()方法
@Override
protected synchronized void startInternal() throws LifecycleException {
//设置错误报告
// Set error report valve
String errorValve = getErrorReportValveClass();
if ((errorValve != null) && (!errorValve.equals(""))) {
try {
boolean found = false;
Valve[] valves = getPipeline().getValves();
for (Valve valve : valves) {
if (errorValve.equals(valve.getClass().getName())) {
found = true;
break;
}
}
if(!found) {
Valve valve =
(Valve) Class.forName(errorValve).getConstructor().newInstance();
getPipeline().addValve(valve);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString(
"standardHost.invalidErrorReportValveClass",
errorValve), t);
}
}
//再次调用父类ContainerBase
super.startInternal();
}
# 4.LifecycleBase.setState(LifecycleState.STARTING); 改变生命周期的状态,激发监听器listener:HostConfig。
protected synchronized void setState(LifecycleState state) throws LifecycleException {
setStateInternal(state, null, true);
}
private synchronized void setStateInternal(LifecycleState state,
Object data, boolean check) throws LifecycleException {
this.state = state;
String lifecycleEvent = state.getLifecycleEvent();
if (lifecycleEvent != null) {
fireLifecycleEvent(lifecycleEvent, data);
}
}
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
//这个listener就是hostConfig(重点)
listener.lifecycleEvent(event);
}
}
# 5.HostConfig的start()
public void lifecycleEvent(LifecycleEvent event) {
// Process the event that has occurred
if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
check();
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(Lifecycle.START_EVENT)) {
//执行hostConfig的start()方法
start();
} else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
stop();
}
}
public void start() {
...
//部署webapps
if (host.getDeployOnStartup())
deployApps();
}
# 6.部署webapps
protected void deployApps() {
File appBase = host.getAppBaseFile();
File configBase = host.getConfigBaseFile();
String[] filteredAppPaths = filterAppPaths(appBase.list());
//部署xml文件配置的server.xml <host><context>...
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list());
//部署war包
// Deploy WARs
deployWARs(appBase, filteredAppPaths);
//部署文件夹
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths);
}
# 7.部署文件夹deployDirectories()
protected void deployDirectories(File appBase, String[] files) {
//发布文件夹
if (files == null)
return;
//使用future和线程池的技术
ExecutorService es = host.getStartStopExecutor();
List<Future<?>> results = new ArrayList<>();
for (int i = 0; i < files.length; i++) {
if (files[i].equalsIgnoreCase("META-INF"))
continue;
if (files[i].equalsIgnoreCase("WEB-INF"))
continue;
File dir = new File(appBase, files[i]);
if (dir.isDirectory()) {
ContextName cn = new ContextName(files[i], false);
if (isServiced(cn.getName()) || deploymentExists(cn.getName()))
continue;
//submit(Runnable),接收一个Runnable,会返回一个Future
//new DeployDirectory(重点)
results.add(es.submit(new DeployDirectory(this, cn, dir)));
}
}
//如果任务结束执行则返回null(会等待线程执行完)
for (Future<?> result : results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString(
"hostConfig.deployDir.threaded.error"), e);
}
}
}
//实现runnable接口
private static class DeployDirectory implements Runnable {
private HostConfig config;
private ContextName cn;
private File dir;
public DeployDirectory(HostConfig config, ContextName cn, File dir) {
this.config = config;
this.cn = cn;
this.dir = dir;
}
@Override
public void run() {
//部署文件夹
config.deployDirectory(cn, dir);
}
}
# 8.部署文件夹deployDirectory()
protected void deployDirectory(ContextName cn, File dir) {
long startTime = 0;
// Deploy the application in this directory
if( log.isInfoEnabled() ) {
startTime = System.currentTimeMillis();
log.info(sm.getString("hostConfig.deployDir",
dir.getAbsolutePath()));
}
//拿到context
Context context = null;
File xml = new File(dir, Constants.ApplicationContextXml);
File xmlCopy = new File(host.getConfigBaseFile(), cn.getBaseName() + ".xml");
DeployedApplication deployedApp;
boolean copyThisXml = isCopyXML();
boolean deployThisXML = isDeployThisXML(dir, cn);
try {
if (deployThisXML && xml.exists()) {
synchronized (digesterLock) {
try {
//解析context节点
context = (Context) digester.parse(xml);
}
}
} else if (!deployThisXML && xml.exists()) {
context = new FailedContext();
} else {
context = (Context) Class.forName(contextClass).getConstructor().newInstance();
}
//实例化 ContextConfig,作为 LifecycleListener 添加到 Context 容器中
//这和 StandardHost 的套路一样,都是使用 XXXConfig
Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
context.addLifecycleListener(listener);
context.setName(cn.getName());
context.setPath(cn.getPath());
context.setWebappVersion(cn.getVersion());
context.setDocBase(cn.getBaseName());
//把context添加到子节点,然后启动context(重点)
host.addChild(context);
}
}
Из процесса запуска стандартного движка видно, что он в основном предназначен для запуска стандартного хоста. Затем с помощью технологии прослушивателя запустите HostConfig, а затем проанализируйте веб-приложения и запустите контекст.
DeployDirectory HostConfig в основном выполняет несколько задач: 1. Используйте дайджест или создайте экземпляр StandardContext путем отражения 2. Создайте экземпляр ContextConfig и зарегистрируйте прослушиватели событий для контейнера Context.Как и в случае с StandardHost, запустите и остановите контейнер с помощью XXXConfig. 3. Добавить текущий экземпляр Context в качестве подконтейнера в контейнер Host.Логика добавления подконтейнеров реализована в ContainerBase.Если текущий статус Container STARTING_PREP и startChildren true, то подконтейнер также будет запущен
- Анализ метода startInternal() класса StandardContext
# 1.StandardContext的startInternal()方法
@Override
protected synchronized void startInternal() throws LifecycleException {
//StandardContext启动
if(log.isDebugEnabled())
log.debug("Starting " + getBaseName());
//发布这个状态,广播出去,让其他监听
// Send j2ee.state.starting notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.starting",
this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
setConfigured(false);
boolean ok = true;
//启动命名空间资源
// Currently this is effectively a NO-OP but needs to be called to
// ensure the NamingResources follows the correct lifecycle
if (namingResources != null) {
namingResources.start();
}
//创建工作目录work
// Post work directory
postWorkDirectory();
//加载资源
// Add missing components as necessary
if (getResources() == null) { // (1) Required by Loader
if (log.isDebugEnabled())
log.debug("Configuring default Resources");
try {
setResources(new StandardRoot(this));
} catch (IllegalArgumentException e) {
log.error(sm.getString("standardContext.resourcesInit"), e);
ok = false;
}
}
if (ok) {
resourcesStart();
}
//Webapp加载器
if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
//初始化一个cookie
// An explicit cookie processor hasn't been specified; use the default
if (cookieProcessor == null) {
cookieProcessor = new Rfc6265CookieProcessor();
}
//字符集的映射
// Initialize character set mapper
getCharsetMapper();
//依赖关系处理
// Validate required extensions
boolean dependencyCheck = true;
try {
dependencyCheck = ExtensionValidator.validateApplication
(getResources(), this);
} catch (IOException ioe) {
log.error(sm.getString("standardContext.extensionValidationError"), ioe);
dependencyCheck = false;
}
if (!dependencyCheck) {
// do not make application available if dependency check fails
ok = false;
}
//用户命名属性,获取环境变量
// Reading the "catalina.useNaming" environment variable
String useNamingProperty = System.getProperty("catalina.useNaming");
if ((useNamingProperty != null)
&& (useNamingProperty.equals("false"))) {
useNaming = false;
}
if (ok && isUseNaming()) {
if (getNamingContextListener() == null) {
NamingContextListener ncl = new NamingContextListener();
ncl.setName(getNamingContextName());
ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
addLifecycleListener(ncl);
setNamingContextListener(ncl);
}
}
// Standard container startup
if (log.isDebugEnabled())
log.debug("Processing standard container startup");
// Binding thread
ClassLoader oldCCL = bindThread();
//发出一个生命周期事件,触发监听器:ContextConfig(重点)
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
//启动wrapper子节点(重点)
// Start our child containers, if not already started
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
child.start();
}
}
}
# 2.ContextConfig的configureStart()方法
protected synchronized void configureStart() {
//解析web.xml(重点)
//解析servlet,filter,listener
webConfig();
}
protected void webConfig() {
//创建web.xml解析器
WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
context.getXmlValidation(), context.getXmlBlockExternal());
Set<WebXml> defaults = new HashSet<>();
defaults.add(getDefaultWebXmlFragment(webXmlParser));
WebXml webXml = createWebXml();
//解析web.xml
InputSource contextWebXml = getContextWebXmlSource();
if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
ok = false;
}
ServletContext sContext = context.getServletContext();
// Ordering is important here
// Step 1. Identify all the JARs packaged with the application and those
// provided by the container. If any of the application JARs have a
// web-fragment.xml it will be parsed at this point. web-fragment.xml
// files are ignored for container provided JARs.
Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
// Step 2. Order the fragments.
Set<WebXml> orderedFragments = null;
orderedFragments =
WebXml.orderWebFragments(webXml, fragments, sContext);
// Step 3. Look for ServletContainerInitializer implementations
if (ok) {
processServletContainerInitializers();
}
if (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
// Steps 4 & 5.
processClasses(webXml, orderedFragments);
}
if (!webXml.isMetadataComplete()) {
// Step 6. Merge web-fragment.xml files into the main web.xml
// file.
if (ok) {
ok = webXml.merge(orderedFragments);
}
// Step 7. Apply global defaults
// Have to merge defaults before JSP conversion since defaults
// provide JSP servlet definition.
webXml.merge(defaults);
// Step 8. Convert explicitly mentioned jsps to servlets
if (ok) {
convertJsps(webXml);
}
// Step 9. Apply merged web.xml to Context
if (ok) {
//配置context
configureContext(webXml);
}
} else {
webXml.merge(defaults);
convertJsps(webXml);
configureContext(webXml);
}
if (context.getLogEffectiveWebXml()) {
log.info("web.xml:\n" + webXml.toXml());
}
// Always need to look for static resources
// Step 10. Look for static resources packaged in JARs
if (ok) {
// Spec does not define an order.
// Use ordered JARs followed by remaining JARs
Set<WebXml> resourceJars = new LinkedHashSet<>();
for (WebXml fragment : orderedFragments) {
resourceJars.add(fragment);
}
for (WebXml fragment : fragments.values()) {
if (!resourceJars.contains(fragment)) {
resourceJars.add(fragment);
}
}
processResourceJARs(resourceJars);
// See also StandardContext.resourcesStart() for
// WEB-INF/classes/META-INF/resources configuration
}
// Step 11. Apply the ServletContainerInitializer config to the
// context
if (ok) {
for (Map.Entry<ServletContainerInitializer,
Set<Class<?>>> entry :
initializerClassMap.entrySet()) {
if (entry.getValue().isEmpty()) {
context.addServletContainerInitializer(
entry.getKey(), null);
} else {
context.addServletContainerInitializer(
entry.getKey(), entry.getValue());
}
}
}
// 指定 ServletContext 的相关参数
mergeParameters();
// 调用 ServletContainerInitializer#onStartup()
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
initializers.entrySet()) {
try {
entry.getKey().onStartup(entry.getValue(),getServletContext());
} catch (ServletException e) {
log.error(sm.getString("standardContext.sciFail"), e);
ok = false;
break;
}
}
//初始化 Filter
if (ok) {
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
//处理 Wrapper 容器(如果Servlet的loadOnStartup >= 0,便会在这一阶段完成 Servlet 的加载)
if (ok) {
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
}
Как и другие контейнеры, StandardContext также переопределяет метод startInternal. Поскольку он включает в себя процесс запуска веб-приложения, требуется много подготовительной работы, например использование WebResourceRoot для загрузки файлов ресурсов, использование Loader для загрузки классов, использование JarScanner для сканирования пакетов jar и т. д. Поэтому логика запуска StandardContext более сложная, вот следующие важные шаги:
- Создайте рабочий каталог, такой как $CATALINA_HOME\work\Catalina\localhost\examples; создайте экземпляр ContextServlet, приложение получит режим отображения ApplicationContext
- Создайте экземпляр WebResourceRoot, классом реализации по умолчанию является StandardRoot, который используется для чтения файловых ресурсов веб-приложения.
- Создание экземпляра объекта Loader.Loader — это инкапсуляция ClassLoader в tomcat для поддержки горячей загрузки классов во время выполнения.
- Вызовите событие CONFIGURE_START_EVENT, ContextConfig обработает это событие, основной целью является чтение связанного с сервлетом прослушивателя, сервлета, фильтра и т. д. из веб-приложения.
- Создайте диспетчер сеансов, используя StandardManager по умолчанию.
- Вызовите listenerStart, создайте экземпляры различных прослушивателей, связанных с сервлетом, и вызовите
ServletContextListener
- Фильтр обработки
- загрузить сервлет
- Инициировать событие CONFIGURE_START_EVENT, активировать прослушиватель ContextConfig
ContextConfig — это LifycycleListener, который играет очень важную роль в процессе запуска Context. StandardContext выдаст событие CONFIGURE_START_EVENT, а ContextConfig обработает это событие. Основная цель — прочитать информацию о конфигурации сервлета, такую как фильтр, сервлет, прослушиватель и т. д., через конфигурацию аннотаций web.xml или Servlet3.0. основная логика находится в ContextConfig#, реализованном в методе webConfig(). Важные шаги, выполняемые ContextConfig:
- Он анализирует web.xml через WebXmlParser.Если есть файл web.xml, Servlet, Filter и Listener, определенные в файле, будут зарегистрированы в экземпляре WebXml.
- Если файл web.xml отсутствует, tomcat сначала просканирует файлы классов в каталоге WEB-INF/classes, затем просканирует пакеты jar в каталоге WEB-INF/lib и проанализирует байт-код, чтобы прочитать аннотацию, связанную с сервлетом. классы конфигурации Не жалуйтесь на аннотацию serlvet3.0, обработка аннотации сервлета довольно тяжеловесна. Tomcat не будет загружать класс в jvm заранее, а получит некоторую информацию о соответствующем классе путем разбора файла байт-кода, такую как аннотации, реализованные интерфейсы и т.д.
- Шаг 9. Добавьте оболочку подконтейнера в контекст
# 1.context添加wrapper子节点
private void configureContext(WebXml webxml) {
// 设置 Filter 定义
for (FilterDef filter : webxml.getFilters().values()) {
if (filter.getAsyncSupported() == null) {
filter.setAsyncSupported("false");
}
context.addFilterDef(filter);
}
// 设置 FilterMapping,即 Filter 的 URL 映射
for (FilterMap filterMap : webxml.getFilterMappings()) {
context.addFilterMap(filterMap);
}
// 往 Context 中添加子容器 Wrapper,即 Servlet
for (ServletDef servlet : webxml.getServlets().values()) {
Wrapper wrapper = context.createWrapper();
// 省略若干代码。。。
wrapper.setOverridable(servlet.isOverridable());
context.addChild(wrapper);
}
// ......
}
- Запустите контейнер StandardWrapper.
# 1.StandardWrapper的startInternal()方法
@Override
protected synchronized void startInternal() throws LifecycleException {
// 发出 j2ee.state.starting 事件通知
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.starting",
this.getObjectName(),
sequenceNumber++);
broadcaster.sendNotification(notification);
}
// ConainerBase 的启动逻辑
super.startInternal();
setAvailable(0L);
// 发出 j2ee.state.running 事件通知
if (this.getObjectName() != null) {
Notification notification =
new Notification("j2ee.state.running", this.getObjectName(), sequenceNumber++);
broadcaster.sendNotification(notification);
}
}
# 2.StandardWrapper的load()方法
@Override
public synchronized void load() throws ServletException {
// 实例化 Servlet,并且调用 init 方法完成初始化
instance = loadServlet();
if (!instanceInitialized) {
initServlet(instance);
}
if (isJspServlet) {
// 处理 jsp Servlet
StringBuilder oname = new StringBuilder(getDomain());
oname.append(":type=JspMonitor");
oname.append(getWebModuleKeyProperties());
oname.append(",name=");
oname.append(getName());
oname.append(getJ2EEKeyProperties());
try {
jspMonitorON = new ObjectName(oname.toString());
Registry.getRegistry(null, null)
.registerComponent(instance, jspMonitorON, null);
} catch( Exception ex ) {
log.info("Error registering JSP monitoring with jmx " +
instance);
}
}
}
StandardWrapper не имеет подконтейнеров, а логика запуска относительно проста и понятна, он переписывает метод startInternal, в основном, для завершения уведомления о событии jmx и последовательной отправки стартовых и запущенных событий в jmx.
Как видно из предыдущего анализа контейнера Context, после того, как Context завершит инициализацию Filter, если loadOnStartup >= 0, будет вызван метод load для загрузки контейнера Wrapper. StandardWrapper использует InstanceManager для создания экземпляра сервлета и вызывает метод init сервлета для его инициализации.Входящий ServletConfig является объектом StandardWrapperFacade.
Суммировать: Tomcat реализует интерфейс javax.servlet.ServletContext, который создается при запуске Context. Контейнер контекста считывает конфигурацию аннотаций сервлета 3.0 через web.xml или сканирует байт-код класса, тем самым загружая компоненты сервлета, такие как прослушиватель, сервлет, фильтр и т. д., определенные веб-приложением, но объект не создается немедленно. После того, как все они загружены, создайте экземпляры Listener, Filter и Servlet по очереди и вызовите их методы инициализации, такие как ServletContextListener#contextInitialized(), Flter#init() и т. д.
На этом этап запуска tomcat завершен. Используя модель цепочки ответственности, начните шаг за шагом. Последовательность запуска компонента: Сервер-->Сервис-->Движок-->Хост-->Контекст-->Оболочка
- Блок-схема инициализации и запуска Tomcat:
3. Этап обработки веб-запросов Tomcat
- Общая блок-схема веб-запроса
Как видно из общей архитектуры tomcat, tomcat используется соединителем для получения запроса пользователя, а затем передается контейнеру для обработки. Отслеживая процесс запуска коннектора, последовательно запускается обработчик протокола, затем запускается конечная точка, а затем запускается акцептор (используется для получения запросов).
- Глядя на основную структуру NioEndpoint, можно увидеть, что этот класс имеет три важных внутренних класса: Acceptor, Poller, SocketProcessor.
# 1.NioEndpoint的startInternal()方法
@Override
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getProcessorCache());
eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getEventCache());
nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
socketProperties.getBufferPool());
//创建工作者线程池
// Create worker collection
if ( getExecutor() == null ) {
createExecutor();
}
initializeConnectionLatch();
//启动poller线程,用来轮询检查新的请求
// Start poller threads
pollers = new Poller[getPollerThreadCount()];
for (int i=0; i<pollers.length; i++) {
pollers[i] = new Poller();
Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
}
//启动Acceptor线程,用来接收用户请求
startAcceptorThreads();
}
}
# 2.启动Acceptor线程
protected final void startAcceptorThreads() {
//获取Acceptor线程数(默认是1)
int count = getAcceptorThreadCount();
//创建Acceptor数组
acceptors = new Acceptor[count];
for (int i = 0; i < count; i++) {
//创建Acceptor对象,Acceptor继承Runnable
acceptors[i] = createAcceptor();
String threadName = getName() + "-Acceptor-" + i;
acceptors[i].setThreadName(threadName);
//创建Thread对象
Thread t = new Thread(acceptors[i], threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
//启动线程
t.start();
}
}
Из приведенного выше кода видно, что NioEndpoint запустит поток Acceptor при выполнении метода startInternal(). Acceptor наследует Runnable, а затем использует Thread для запуска потока Acceptor.
- Анализ метода акцептора run()
# 1.Acceptor 用来接收请求
protected class Acceptor extends AbstractEndpoint.Acceptor {
@Override
public void run() {
int errorDelay = 0;
System.out.println("Acceptor 接收者开始执行");
// Loop until we receive a shutdown command
while (running) {
// Loop if endpoint is paused
while (paused && running) {
state = AcceptorState.PAUSED;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// Ignore
}
}
if (!running) {
break;
}
state = AcceptorState.RUNNING;
try {
//if we have reached max connections, wait
countUpOrAwaitConnection();
SocketChannel socket = null;
try {
//接收请求,拿到socket
socket = serverSock.accept();
} catch (IOException ioe) {
// We didn't get a socket
countDownConnection();
if (running) {
// Introduce delay if necessary
errorDelay = handleExceptionWithDelay(errorDelay);
// re-throw
throw ioe;
} else {
break;
}
}
// Successful accept, reset the error delay
errorDelay = 0;
// Configure the socket
if (running && !paused) {
//设置socket的一些属性
if (!setSocketOptions(socket)) {
closeSocket(socket);
}
} else {
closeSocket(socket);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("endpoint.accept.fail"), t);
}
}
state = AcceptorState.ENDED;
}
}
# 2.设置socket的一些属性
protected boolean setSocketOptions(SocketChannel socket) {
// Process the connection
try {
//disable blocking, APR style, we are gonna be polling it
socket.configureBlocking(false);
Socket sock = socket.socket();
socketProperties.setProperties(sock);
//SocketChannel转化成nioChannel
NioChannel channel = nioChannels.pop();
if (channel == null) {
SocketBufferHandler bufhandler = new SocketBufferHandler(
socketProperties.getAppReadBufSize(),
socketProperties.getAppWriteBufSize(),
socketProperties.getDirectBuffer());
if (isSSLEnabled()) { //SSL, https
channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
} else {
//http1.1
channel = new NioChannel(socket, bufhandler);
}
} else {
channel.setIOChannel(socket);
channel.reset();
}
//获取poller对象,注册channel(重要)
getPoller0().register(channel);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
try {
log.error("",t);
} catch (Throwable tt) {
ExceptionUtils.handleThrowable(tt);
}
// Tell to close the socket
return false;
}
return true;
}
# 3.poller对象,注册channel。Poller.register()方法
public void register(final NioChannel socket) {
socket.setPoller(this);
NioSocketWrapper ka = new NioSocketWrapper(socket, NioEndpoint.this);
socket.setSocketWrapper(ka);
ka.setPoller(this);
ka.setReadTimeout(getSocketProperties().getSoTimeout());
ka.setWriteTimeout(getSocketProperties().getSoTimeout());
ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
ka.setSecure(isSSLEnabled());
ka.setReadTimeout(getConnectionTimeout());
ka.setWriteTimeout(getConnectionTimeout());
PollerEvent r = eventCache.pop();
//生成poller, socket加入到even queue
ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
else r.reset(socket,ka,OP_REGISTER);
addEvent(r);
}
Глядя на метод запуска акцептора, вы можете видеть, что объект сокета получен. (Указывает, что нижний слой tomcat взаимодействует через сокеты) Затем, сгенерировав poller. Потоки опроса в основном используются для опроса подключенных сокетов с меньшими ресурсами для поддержания соединений и передачи в рабочие потоки, когда данные доступны.
- Поллер опрашивает, чтобы проверить, есть ли новые запросы
# 1.Poller的run()方法
@Override
public void run() {
// Loop until destroy() is called
while (true) {
boolean hasEvents = false;
try {
if (!close) {
//该方法遍历了eventqueue中所有的pollorEvent
//然后依次调用pollorEvent的run方法
//将socket注册到selector中
hasEvents = events();
if (wakeupCounter.getAndSet(-1) > 0) {
keyCount = selector.selectNow();
} else {
keyCount = selector.select(selectorTimeout);
}
wakeupCounter.set(0);
}
if (close) {
events();
timeout(0, false);
try {
selector.close();
} catch (IOException ioe) {
log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
}
break;
}
} catch (Throwable x) {
ExceptionUtils.handleThrowable(x);
log.error("",x);
continue;
}
//either we timed out or we woke up, process events first
if ( keyCount == 0 ) hasEvents = (hasEvents | events());
Iterator<SelectionKey> iterator =
keyCount > 0 ? selector.selectedKeys().iterator() : null;
// Walk through the collection of ready keys and dispatch
// any active event.
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
if (attachment == null) {
iterator.remove();
} else {
iterator.remove();
//处理(重点)
processKey(sk, attachment);
}
}//while
//process timeouts
timeout(keyCount,hasEvents);
}//while
getStopLatch().countDown();
}
# 2.Poller,处理processKey
protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {
try {
if ( close ) {
cancelledKey(sk);
} else if ( sk.isValid() && attachment != null ) {
if (sk.isReadable() || sk.isWritable() ) {
if ( attachment.getSendfileData() != null ) {
processSendfile(sk,attachment, false);
} else {
unreg(sk, attachment, sk.readyOps());
boolean closeSocket = false;
// Read goes before write
//读事件
if (sk.isReadable()) {
//处理socket(创建worker,重点)
if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {
closeSocket = true;
}
}
//写事件
if (!closeSocket && sk.isWritable()) {
if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {
closeSocket = true;
}
}
if (closeSocket) {
cancelledKey(sk);
}
}
}
} else {
//invalid key
cancelledKey(sk);
}
} catch ( CancelledKeyException ckx ) {
cancelledKey(sk);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("",t);
}
}
Потоки опроса в основном используются для опроса подключенных сокетов с меньшими ресурсами для поддержания соединений и передачи в рабочие потоки, когда данные доступны. Отслеживая метод запуска опросчика, можно увидеть, что будет создан SocketProcessor (воркер) для дальнейшей обработки сокета.
- SocketProcessor (рабочий) обрабатывает процесс сокета.
# 1.AbstractEndpoint的processSocket()方法
//处理socket
public boolean processSocket(SocketWrapperBase<S> socketWrapper,
SocketEvent event, boolean dispatch) {
try {
if (socketWrapper == null) {
return false;
}
//创建SocketProcessor处理器(worker)
SocketProcessorBase<S> sc = processorCache.pop();
if (sc == null) {
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
//执行
Executor executor = getExecutor();
if (dispatch && executor != null) {
executor.execute(sc);
} else {
sc.run();
}
} catch (RejectedExecutionException ree) {
getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
return false;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This means we got an OOM or similar creating a thread, or that
// the pool and its queue are full
getLog().error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}
- Анализ doRun() SocketProcessor.
# 1.SocketProcessor的doRun()方法
protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
public SocketProcessor(SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
super(socketWrapper, event);
}
@Override
protected void doRun() {
NioChannel socket = socketWrapper.getSocket();
SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
try {
int handshake = -1;
if (handshake == 0) {
SocketState state = SocketState.OPEN;
// Process the request from this socket
if (event == null) {
//处理socket(重点)
state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
} else {
state = getHandler().process(socketWrapper, event);
}
if (state == SocketState.CLOSED) {
close(socket, key);
}
}
}
}
}
# 2.AbstractProtocol的ConnectionHandler的process()方法。(只贴出重点关注代码)
@Override
public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
//拿到socket对象
S socket = wrapper.getSocket();
Processor processor = connections.get(socket);
if (processor == null) {
//创建Processor(重点)
processor = getProtocol().createProcessor();
register(processor);
}
//通过processor执行wrapper(重点)
state = processor.process(wrapper, status);
// Make sure socket/processor is removed from the list of current
// connections
connections.remove(socket);
release(processor);
return SocketState.CLOSED;
}
# 3.AbstractHttp11Protocol.createProcessor()方法。 创建Processor。
protected Processor createProcessor() {
//构建Http11Processor
Http11Processor processor = new Http11Processor(getMaxHttpHeaderSize(),
getAllowHostHeaderMismatch(), getRejectIllegalHeaderName(), getEndpoint(),
getMaxTrailerSize(), allowedTrailerHeaders, getMaxExtensionSize(),
getMaxSwallowSize(), httpUpgradeProtocols, getSendReasonPhrase(),
relaxedPathChars, relaxedQueryChars);
//设置adapter适配器(重点)
processor.setAdapter(getAdapter());
//默认的keepAlive情况下,每个socket处理的最多的 请求次数
processor.setMaxKeepAliveRequests(getMaxKeepAliveRequests());
//开启keepAlive的Timeout
processor.setConnectionUploadTimeout(getConnectionUploadTimeout());
//http当遇到文件上传时 默认超时时间(300*1000)
processor.setDisableUploadTimeout(getDisableUploadTimeout());
//当http请求的body size超过这个值时,通过gzip进行压缩
processor.setCompressionMinSize(getCompressionMinSize());
//http请求是否开启compression处理,gzip压缩
processor.setCompression(getCompression());
processor.setNoCompressionUserAgents(getNoCompressionUserAgents());
//http body里面的内容是“text/html,text/xml,text/plain”
//才会进行压缩处理
processor.setCompressibleMimeTypes(getCompressibleMimeTypes());
processor.setRestrictedUserAgents(getRestrictedUserAgents());
//最大的post处理尺寸的大小 4*1000
processor.setMaxSavePostSize(getMaxSavePostSize());
processor.setServer(getServer());
processor.setServerRemoveAppProvidedValues(getServerRemoveAppProvidedValues());
return processor;
}
# 4.通过processor执行wrapper(AbstractProcessorLight.process()方法)
@Override
public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
throws IOException {
SocketState state = SocketState.CLOSED;
Iterator<DispatchType> dispatches = null;
do {
if (dispatches != null) {
DispatchType nextDispatch = dispatches.next();
state = dispatch(nextDispatch.getSocketStatus());
} else if (status == SocketEvent.DISCONNECT) {
// Do nothing here, just wait for it to get recycled
} else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) {
state = dispatch(status);
if (state == SocketState.OPEN) {
//执行service方法,处理socket(重点)
state = service(socketWrapper);
}
} else if (status == SocketEvent.OPEN_WRITE) {
// Extra write event likely after async, ignore
state = SocketState.LONG;
} else if (status == SocketEvent.OPEN_READ){
state = service(socketWrapper);
} else {
state = SocketState.CLOSED;
}
if (dispatches == null || !dispatches.hasNext()) {
// Only returns non-null iterator if there are
// dispatches to process.
dispatches = getIteratorAndClearDispatches();
}
} while (state == SocketState.ASYNC_END ||
dispatches != null && state != SocketState.CLOSED);
return state;
}
# 5.Http11Processor的service()方法(只保留重点代码)
@Override
public SocketState service(SocketWrapperBase<?> socketWrapper){
//通过adapter处理请求(重点)
getAdapter().service(request, response);
}
С помощью приведенного выше анализа кода SocketProcessor обрабатывает объект сокета и, наконец, обрабатывает его, вызывая метод getAdapter().service(request, response).
- Далее введите анализ метода getAdapter().service(request, response).
# 1.CoyoteAdapter的service()方法
@Override
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
throws Exception { //接收到所有的请求
//转换request和response
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
if (request == null) {
//通过connector创建request和response
request = connector.createRequest();
request.setCoyoteRequest(req);
response = connector.createResponse();
response.setCoyoteResponse(res);
//link将request和response连接起来
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
//设置URI的编码
req.getParameters().setQueryStringCharset(connector.getURICharset());
}
if (connector.getXpoweredBy()) {
response.addHeader("X-Powered-By", POWERED_BY);
}
boolean async = false;
boolean postParseSuccess = false;
req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
try {
// Parse and set Catalina and configuration specific
// request parameters
//在map里面解析业务请求(重要)
postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
//设置异步支持(getContainer()拿到的是engine)
//check valves if we support async
request.setAsyncSupported(
connector.getService().getContainer().getPipeline().isAsyncSupported());
// Calling the container
//执行pipeline管道(invoke)standardEngineValve.invoke (重点)
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);
}
if (request.isAsync()) {
} else {
//请求和响应完成
request.finishRequest();
response.finishResponse();
}
}
}
# 2.standardEngineValve.invoke()方法
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
//获取StandardHost对象
// 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());
}
//执行standardHostValue.invoke()方法(重点)
// Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}
# 3.standardHostValue.invoke()方法
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
//获取standardContext
Context context = request.getContext();
if (context == null) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
sm.getString("standardHost.noContext"));
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(context.getPipeline().isAsyncSupported());
}
boolean asyncAtStart = request.isAsync();
boolean asyncDispatching = request.isAsyncDispatching();
try {
context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) {
return;
}
try {
if (!asyncAtStart || asyncDispatching) {
//执行standardContextValue.invoke()的方法(重点)
context.getPipeline().getFirst().invoke(request, response);
}
}
}
}
# 4.standardContextValue.invoke()方法
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
//获取StandardWrapper
// Select the Wrapper to be used for this Request
Wrapper wrapper = request.getWrapper();
if (wrapper == null || wrapper.isUnavailable()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
}
//执行standardWrapperValue.invoke()的方法(重点)
wrapper.getPipeline().getFirst().invoke(request, response);
}
# 5.standardWrapperValue.invoke()方法
//最终找到servlet处理
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Initialize local variables we may need
boolean unavailable = false;
Throwable throwable = null;
// This should be a Request attribute...
long t1=System.currentTimeMillis();
//增加请求次数,CAS
requestCount.incrementAndGet();
StandardWrapper wrapper = (StandardWrapper) getContainer();
//获取servlet对象
Servlet servlet = null;
Context context = (Context) wrapper.getParent();
// Check for the application being marked unavailable
if (!context.getState().isAvailable()) {
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardContext.isUnavailable"));
unavailable = true;
}
// Check for the servlet being marked unavailable
if (!unavailable && wrapper.isUnavailable()) {
container.getLogger().info(sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
unavailable = true;
}
//Servlet默认的是在第一次请求的时候实例化
// Allocate a servlet instance to process this request
try {
if (!unavailable) {
//获取不到,再分配加载一个
servlet = wrapper.allocate();
}
} catch (UnavailableException e) {
container.getLogger().error(
sm.getString("standardWrapper.allocateException",
wrapper.getName()), e);
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
} catch (ServletException e) {
container.getLogger().error(sm.getString("standardWrapper.allocateException",
wrapper.getName()), StandardWrapper.getRootCause(e));
throwable = e;
exception(request, response, e);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.allocateException",
wrapper.getName()), e);
throwable = e;
exception(request, response, e);
servlet = null;
}
//获取path
MessageBytes requestPathMB = request.getRequestPathMB();
DispatcherType dispatcherType = DispatcherType.REQUEST;
if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
requestPathMB);
// Create the filter chain for this request
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
//执行相应的filter过滤器链
// Call the filter chain for this request
// NOTE: This also calls the servlet's service() method
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());
}
}
}
} catch (ClientAbortException | CloseNowException e) {
if (container.getLogger().isDebugEnabled()) {
container.getLogger().debug(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
}
throwable = e;
exception(request, response, e);
} catch (IOException e) {
container.getLogger().error(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
throwable = e;
exception(request, response, e);
} catch (UnavailableException e) {
container.getLogger().error(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
// throwable = e;
// exception(request, response, e);
wrapper.unavailable(e);
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE)) {
response.setDateHeader("Retry-After", available);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
sm.getString("standardWrapper.isUnavailable",
wrapper.getName()));
} else if (available == Long.MAX_VALUE) {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
sm.getString("standardWrapper.notFound",
wrapper.getName()));
}
// Do not save exception in 'throwable', because we
// do not want to do exception(request, response, e) processing
} catch (ServletException e) {
Throwable rootCause = StandardWrapper.getRootCause(e);
if (!(rootCause instanceof ClientAbortException)) {
container.getLogger().error(sm.getString(
"standardWrapper.serviceExceptionRoot",
wrapper.getName(), context.getName(), e.getMessage()),
rootCause);
}
throwable = e;
exception(request, response, e);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString(
"standardWrapper.serviceException", wrapper.getName(),
context.getName()), e);
throwable = e;
exception(request, response, e);
}
// Release the filter chain (if any) for this request
if (filterChain != null) {
filterChain.release();
}
// Deallocate the allocated servlet instance
try {
if (servlet != null) {
wrapper.deallocate(servlet);
}
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.deallocateException",
wrapper.getName()), e);
if (throwable == null) {
throwable = e;
exception(request, response, e);
}
}
// If this servlet has been marked permanently unavailable,
// unload it and release this instance
try {
if ((servlet != null) &&
(wrapper.getAvailable() == Long.MAX_VALUE)) {
wrapper.unload();
}
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
container.getLogger().error(sm.getString("standardWrapper.unloadException",
wrapper.getName()), e);
if (throwable == null) {
throwable = e;
exception(request, response, e);
}
}
}
Анализируя метод service() CoyoteAdapter, мы можем знать, что он вызывает метод шаг за шагом. StandardEngineValue-->StandardHostValue-->StandardContextValue-->метод вызова StandardWrapperValue.
- Блок-схема веб-запроса:
- Приложение: исходный код tomcat (с комментариями)GitHub.com/Компьютер мистера Ли/Том С…
- диаграмма классов кота