/** * Main method and entry point when starting Tomcat via the provided * scripts. * * @param args Command line arguments to be processed */ publicstaticvoidmain(String args[]){ // 创建一个 Bootstrap 对象,调用它的 init 方法初始化 synchronized (daemonLock) { if (daemon == null) { // Don't set daemon until init() has completed Bootstrap bootstrap = new Bootstrap(); try { bootstrap.init(); } catch (Throwable t) { handleThrowable(t); t.printStackTrace(); return; } daemon = bootstrap; } else { // When running as a service the call to stop will be on a new // thread so make sure the correct class loader is used to // prevent a range of class not found exceptions. Thread.currentThread().setContextClassLoader(daemon.catalinaLoader); } }
/** * Start a new server instance. * Catalina 的加载过程 */ publicvoidload(){ // 已加载则退出 if (loaded) { return; } loaded = true;
long t1 = System.nanoTime();
// 该方法已弃用 initDirs();
// Before digester - it may be needed // 设置额外的系统变量 initNaming();
// Parse main server.xml // 解析server.xml,Tomcat中的组件都是在这里完成的初始化,例如Server、Service等 parseServerXml(true); Server s = getServer(); if (s == null) { return; }
<!-- 3.GlobalNamingResources 相关 --> <GlobalNamingResources> <Resourcename="UserDatabase"auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources>
/** * @return the global naming resources. */ public NamingResourcesImpl getGlobalNamingResources();
/** * Set the global naming resources. * * @param globalNamingResources The new global naming resources */ publicvoidsetGlobalNamingResources (NamingResourcesImpl globalNamingResources);
/** * @return the global naming resources context. */ public javax.naming.Context getGlobalNamingContext();
/** * @return the port number we listen to for shutdown commands. * * @see #getPortOffset() * @see #getPortWithOffset() */ publicintgetPort();
/** * Set the port number we listen to for shutdown commands. * 该服务器等待关闭命令的TCP / IP端口号。设置为-1禁用关闭端口。 * @param port The new port number * * @see #setPortOffset(int) */ publicvoidsetPort(int port);
/** * Get the number that offsets the port used for shutdown commands. * For example, if port is 8005, and portOffset is 1000, * the server listens at 9005. * 获取用于关闭命令的端口偏移量。例如,如果端口为8005,而端口偏移量为1000,则服务器在9005处监听。 * @return the port offset */ publicintgetPortOffset();
/** * Set the number that offsets the server port used for shutdown commands. * For example, if port is 8005, and you set portOffset to 1000, * connector listens at 9005. * * @param portOffset sets the port offset */ publicvoidsetPortOffset(int portOffset);
/** * Get the actual port on which server is listening for the shutdown commands. * If you do not set port offset, port is returned. If you set * port offset, port offset + port is returned. * 获取服务器监听关机命令的实际端口。如果不设置端口偏移量,则返回端口。如果设置端口偏移量,则返回端口偏移量+端口。 * @return the port with offset */ publicintgetPortWithOffset();
/** * @return the address on which we listen to for shutdown commands. */ public String getAddress();
/** * Set the address on which we listen to for shutdown commands. * 该服务器等待关闭命令的TCP / IP地址。如果未指定地址,localhost则使用。 * @param address The new address */ publicvoidsetAddress(String address);
/** * @return the shutdown command string we are waiting for. */ public String getShutdown();
/** * Set the shutdown command we are waiting for. * 设置shutdown指令,这个指令用来关闭Server * @param shutdown The new shutdown command */ publicvoidsetShutdown(String shutdown);
/** * @return the parent class loader for this component. If not set, return * {@link #getCatalina()} {@link Catalina#getParentClassLoader()}. If * catalina has not been set, return the system class loader. */ public ClassLoader getParentClassLoader();
/** * Set the parent class loader for this server. * * @param parent The new parent class loader */ publicvoidsetParentClassLoader(ClassLoader parent);
/** * @return the outer Catalina startup/shutdown component if present. */ public Catalina getCatalina();
/** * Set the outer Catalina startup/shutdown component if present. * * @param catalina the outer Catalina component */ publicvoidsetCatalina(Catalina catalina);
/** * @return the configured base (instance) directory. Note that home and base * may be the same (and are by default). If this is not set the value * returned by {@link #getCatalinaHome()} will be used. */ public File getCatalinaBase();
/** * Set the configured base (instance) directory. Note that home and base * may be the same (and are by default). * * @param catalinaBase the configured base directory */ publicvoidsetCatalinaBase(File catalinaBase);
/** * @return the configured home (binary) directory. Note that home and base * may be the same (and are by default). */ public File getCatalinaHome();
/** * Set the configured home (binary) directory. Note that home and base * may be the same (and are by default). * * @param catalinaHome the configured home directory */ publicvoidsetCatalinaHome(File catalinaHome);
/** * Get the utility thread count. * 获取此service中用于各种实用程序任务(包括重复执行的线程)的线程数 * @return the thread count */ publicintgetUtilityThreads();
/** * Set the utility thread count. * @param utilityThreads the new thread count */ publicvoidsetUtilityThreads(int utilityThreads);
/** * Add a new Service to the set of defined Services. * * @param service The Service to be added */ publicvoidaddService(Service service);
/** * Wait until a proper shutdown command is received, then return. */ publicvoidawait();
/** * Find the specified Service * * @param name Name of the Service to be returned * @return the specified Service, or <code>null</code> if none exists. */ public Service findService(String name);
/** * @return the set of Services defined within this Server. */ public Service[] findServices();
/** * Remove the specified Service from the set associated from this * Server. * * @param service The Service to be removed */ publicvoidremoveService(Service service);
/** * @return the token necessary for operations on the associated JNDI naming * context. */ public Object getNamingToken();
/** * @return the utility executor managed by the Service. */ public ScheduledExecutorService getUtilityExecutor();
/** * Wait until a proper shutdown command is received, then return. * This keeps the main thread alive - the thread pool listening for http * connections is daemon threads. */ @Override publicvoidawait(){ // Negative values - don't wait on port - tomcat is embedded or we just don't like ports if (getPortWithOffset() == -2) { // undocumented yet - for embedding apps that are around, alive. return; } if (getPortWithOffset() == -1) { try { awaitThread = Thread.currentThread(); // 每10s检测一次 while(!stopAwait) { try { Thread.sleep( 10000 ); } catch( InterruptedException ex ) { // continue and check the flag } } } finally { awaitThread = null; } return; }
// Set up a server socket to wait on try { // 创建一个Socket连接,端口号使用计算过偏移量之后的端口,address没有配置则使用localhost,用来接收shutdown命令 awaitSocket = new ServerSocket(getPortWithOffset(), 1, InetAddress.getByName(address)); } catch (IOException e) { log.error(sm.getString("standardServer.awaitSocket.fail", address, String.valueOf(getPortWithOffset()), String.valueOf(getPort()), String.valueOf(getPortOffset())), e); return; }
try { awaitThread = Thread.currentThread();
// Loop waiting for a connection and a valid command while (!stopAwait) { ServerSocket serverSocket = awaitSocket; if (serverSocket == null) { break; }
// Wait for the next connection Socket socket = null; StringBuilder command = new StringBuilder(); try { InputStream stream; long acceptStartTime = System.currentTimeMillis(); try { socket = serverSocket.accept(); socket.setSoTimeout(10 * 1000); // Ten seconds stream = socket.getInputStream(); } catch (SocketTimeoutException ste) { // This should never happen but bug 56684 suggests that // it does. log.warn(sm.getString("standardServer.accept.timeout", Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste); continue; } catch (AccessControlException ace) { log.warn(sm.getString("standardServer.accept.security"), ace); continue; } catch (IOException e) { if (stopAwait) { // Wait was aborted with socket.close() break; } log.error(sm.getString("standardServer.accept.error"), e); break; }
// Read a set of characters from the socket int expected = 1024; // Cut off to avoid DoS attack while (expected < shutdown.length()) { if (random == null) { random = new Random(); } expected += (random.nextInt() % 1024); } while (expected > 0) { int ch = -1; try { ch = stream.read(); } catch (IOException e) { log.warn(sm.getString("standardServer.accept.readError"), e); ch = -1; } // Control character or EOF (-1) terminates loop if (ch < 32 || ch == 127) { break; } // 读取Socket接收到的数据 command.append((char) ch); expected--; } } finally { // Close the socket now that we are done with it try { if (socket != null) { socket.close(); } } catch (IOException e) { // Ignore } }
// Match against our command string // 判断接收到的命令是否与事先指定的shutdown命令吻合 boolean match = command.toString().equals(shutdown); if (match) { log.info(sm.getString("standardServer.shutdownViaPort")); // 命令正确跳出循环 break; } else { log.warn(sm.getString("standardServer.invalidShutdownCommand", command.toString())); } } } finally { ServerSocket serverSocket = awaitSocket; awaitThread = null; awaitSocket = null;
// Close the server socket and return if (serverSocket != null) { try { // 关闭socket连接 serverSocket.close(); } catch (IOException e) { // Ignore } } } }
/** * Add a new Service to the set of defined Services. * * @param service The Service to be added */ @Override publicvoidaddService(Service service){ service.setServer(this); synchronized (servicesLock) { // 数组扩容,然后将新的Service添加到末尾 Service results[] = new Service[services.length + 1]; System.arraycopy(services, 0, results, 0, services.length); results[services.length] = service; services = results;
/** * Sub-classes implement this method to perform any instance initialisation * required. * 该抽象方法的作用是为了注册Bean * * @throws LifecycleException If the initialisation fails */ protectedabstractvoidinitInternal()throws LifecycleException; /** * Sub-classes must ensure that the state is changed to * {@link LifecycleState#STARTING} during the execution of this method. * Changing state will trigger the {@link Lifecycle#START_EVENT} event. * * If a component fails to start it may either throw a * {@link LifecycleException} which will cause it's parent to fail to start * or it can place itself in the error state in which case {@link #stop()} * will be called on the failed component but the parent component will * continue to start normally. * * @throws LifecycleException Start error occurred */ protectedabstractvoidstartInternal()throws LifecycleException;
/** * Sub-classes must ensure that the state is changed to * {@link LifecycleState#STOPPING} during the execution of this method. * Changing state will trigger the {@link Lifecycle#STOP_EVENT} event. * * @throws LifecycleException Stop error occurred */ protectedabstractvoidstopInternal()throws LifecycleException;
/** * Sub-classes implement this method to perform any instance destruction * required. * 该抽象方法的作用是为了卸载Bean * @throws LifecycleException If the destruction fails */ protectedabstractvoiddestroyInternal()throws LifecycleException;
/** * Invoke a pre-startup initialization. This is used to allow connectors * to bind to restricted ports under Unix operating environments. * 初始化Server,完成一些属性的初始化和注册,并将所有的Service初始化 */ @Override protectedvoidinitInternal()throws LifecycleException {
// Register global String cache // Note although the cache is global, if there are multiple Servers // present in the JVM (may happen when embedding) then the same cache // will be registered under multiple names onameStringCache = register(new StringCache(), "type=StringCache");
// Register the MBeanFactory MBeanFactory factory = new MBeanFactory(); factory.setContainer(this); onameMBeanFactory = register(factory, "type=MBeanFactory");
// Register the naming resources globalNamingResources.init();
// Populate the extension validator with JARs from common and shared // class loaders if (getCatalina() != null) { ClassLoader cl = getCatalina().getParentClassLoader(); // Walk the class loader hierarchy. Stop at the system class loader. // This will add the shared (if present) and common class loaders while (cl != null && cl != ClassLoader.getSystemClassLoader()) { if (cl instanceof URLClassLoader) { URL[] urls = ((URLClassLoader) cl).getURLs(); for (URL url : urls) { if (url.getProtocol().equals("file")) { try { File f = new File (url.toURI()); if (f.isFile() && f.getName().endsWith(".jar")) { // 获取jar包的manifest文件信息,manifest文件定义了jar包的基本信息,包含创建人、版本、启动类等信息 // 这个信息会在之后启动Context组件时进行验证,见StandardContext.startInternal()方法 ExtensionValidator.addSystemResource(f); } } catch (URISyntaxException | IOException e) { // Ignore } } } } cl = cl.getParent(); } } // 初始化所有的Service // Initialize our defined Services for (Service service : services) { service.init(); } }
/** * Start nested components ({@link Service}s) and implement the requirements * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * 启动嵌套在当前Server下的Service * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ @Override protectedvoidstartInternal()throws LifecycleException {
/** * Stop nested components ({@link Service}s) and implement the requirements * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. * * @exception LifecycleException if this component detects a fatal error * that needs to be reported */ @Override protectedvoidstopInternal()throws LifecycleException {
setState(LifecycleState.STOPPING);
if (monitorFuture != null) { monitorFuture.cancel(true); monitorFuture = null; } if (periodicLifecycleEventFuture != null) { periodicLifecycleEventFuture.cancel(false); periodicLifecycleEventFuture = null; }
fireLifecycleEvent(CONFIGURE_STOP_EVENT, null);
// Stop our defined Services for (Service service : services) { service.stop(); }
try { // start前的状态设置 setStateInternal(LifecycleState.STARTING_PREP, null, false); // start逻辑,抽象方法,由组件自行实现 startInternal(); // start过程中,可能因为某些原因失败,这时需要stop操作 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(); } elseif (!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 { // 设置状态为STARTED 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. handleSubClassException(t, "lifecycleBase.startFail", toString()); } }
if (log.isDebugEnabled()) { Exception e = new LifecycleException(); log.debug(sm.getString("lifecycleBase.alreadyStopped", toString()), e); } elseif (log.isInfoEnabled()) { log.info(sm.getString("lifecycleBase.alreadyStopped", toString())); }
return; }
// `NEW`状态时,直接将状态变更为`STOPPED` if (state.equals(LifecycleState.NEW)) { state = LifecycleState.STOPPED; return; }
// stop()的执行,必须要是`STARTED`和`FAILED` if (!state.equals(LifecycleState.STARTED) && !state.equals(LifecycleState.FAILED)) { invalidTransition(Lifecycle.BEFORE_STOP_EVENT); }
try { // `FAILED`时,直接触发BEFORE_STOP_EVENT事件 if (state.equals(LifecycleState.FAILED)) { // Don't transition to STOPPING_PREP as that would briefly mark the // component as available but do ensure the BEFORE_STOP_EVENT is // fired fireLifecycleEvent(BEFORE_STOP_EVENT, null); } else { // 否则,设置状态为STOPPING_PREP setStateInternal(LifecycleState.STOPPING_PREP, null, false); }
// stop逻辑,抽象方法,组件自行实现 stopInternal();
// Shouldn't be necessary but acts as a check that sub-classes are // doing what they are supposed to. // 不是`STOPPING`和`FAILED`时,则说明是非法的操作 if (!state.equals(LifecycleState.STOPPING) && !state.equals(LifecycleState.FAILED)) { invalidTransition(Lifecycle.AFTER_STOP_EVENT); }
@Override publicfinalsynchronizedvoiddestroy()throws LifecycleException { // `FAILED`状态时,直接触发stop()逻辑 if (LifecycleState.FAILED.equals(state)) { try { // Triggers clean-up stop(); } catch (LifecycleException e) { // Just log. Still want to destroy. log.error(sm.getString("lifecycleBase.destroyStopFail", toString()), e); } }
// `DESTROYING`和`DESTROYED`时,忽略destroy的执行 if (LifecycleState.DESTROYING.equals(state) || LifecycleState.DESTROYED.equals(state)) { if (log.isDebugEnabled()) { Exception e = new LifecycleException(); log.debug(sm.getString("lifecycleBase.alreadyDestroyed", toString()), e); } elseif (log.isInfoEnabled() && !(thisinstanceof Lifecycle.SingleUse)) { // Rather than have every component that might need to call // destroy() check for SingleUse, don't log an info message if // multiple calls are made to destroy() log.info(sm.getString("lifecycleBase.alreadyDestroyed", toString())); }
可以清晰的看到 Service 中包含的子容器,包括:Engine 和 Connector。下面来根据 StandardService 的继承关系来分析源码,先来看 Service 接口部分的方法:
公共属性部分:
1 2 3 4 5 6 7 8 9 10 11
/** * @return the name of this Service. */ public String getName();
/** * Set the name of this Service. * 设置Service Name * @param name The new service name */ publicvoidsetName(String name);
Engine 部分:
1 2 3 4 5 6 7 8 9 10 11 12 13
/** * @return the <code>Engine</code> that handles requests for all * <code>Connectors</code> associated with this Service. */ public Engine getContainer();
/** * Set the <code>Engine</code> that handles requests for all * <code>Connectors</code> associated with this Service. * 设置Engine * @param engine The new Engine */ publicvoidsetContainer(Engine engine);
/** * @return the <code>Server</code> with which we are associated (if any). */ public Server getServer();
/** * Set the <code>Server</code> with which we are associated (if any). * * @param server The server that owns this Service */ publicvoidsetServer(Server server);
/** * @return the parent class loader for this component. If not set, return * {@link #getServer()} {@link Server#getParentClassLoader()}. If no server * has been set, return the system class loader. */ public ClassLoader getParentClassLoader();
/** * Set the parent class loader for this service. * * @param parent The new parent class loader */ publicvoidsetParentClassLoader(ClassLoader parent);
/** * @return the domain under which this container will be / has been * registered. */ public String getDomain();
/** * Add a new Connector to the set of defined Connectors, and associate it * with this Service's Container. * * @param connector The Connector to be added */ publicvoidaddConnector(Connector connector);
/** * Find and return the set of Connectors associated with this Service. * * @return the set of associated Connectors */ public Connector[] findConnectors();
/** * Remove the specified Connector from the set associated from this * Service. The removed Connector will also be disassociated from our * Container. * * @param connector The Connector to be removed */ publicvoidremoveConnector(Connector connector);
/** * Invoke a pre-startup initialization. This is used to allow connectors * to bind to restricted ports under Unix operating environments. */ @Override protectedvoidinitInternal()throws LifecycleException {
super.initInternal();
// 初始化Engine if (engine != null) { engine.init(); }
// Initialize any Executors // 初始化Executors for (Executor executor : findExecutors()) { if (executor instanceof JmxEnabled) { ((JmxEnabled) executor).setDomain(getDomain()); } executor.init(); }
// Initialize our defined Connectors // 初始化Connector synchronized (connectorsLock) { for (Connector connector : connectors) { connector.init(); } } }
/** * Start nested components ({@link Executor}s, {@link Connector}s and * {@link Container}s) and implement the requirements of * {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ @Override protectedvoidstartInternal()throws LifecycleException {
// Start our defined Container first // 启动Engine if (engine != null) { synchronized (engine) { engine.start(); } } // 启动Executors synchronized (executors) { for (Executor executor: executors) { executor.start(); } }
// 启动mapperListener mapperListener.start();
// Start our defined Connectors second // 启动Connector synchronized (connectorsLock) { for (Connector connector: connectors) { // If it has already failed, don't try and start it if (connector.getState() != LifecycleState.FAILED) { connector.start(); } } } }
/** * Stop nested components ({@link Executor}s, {@link Connector}s and * {@link Container}s) and implement the requirements of * {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. * * @exception LifecycleException if this component detects a fatal error * that needs to be reported */ @Override protectedvoidstopInternal()throws LifecycleException {
synchronized (connectorsLock) { // Initiate a graceful stop for each connector // This will only work if the bindOnInit==false which is not the // default. for (Connector connector: connectors) { connector.getProtocolHandler().closeServerSocketGraceful(); }
// Wait for the graceful shutdown to complete long waitMillis = gracefulStopAwaitMillis; if (waitMillis > 0) { for (Connector connector: connectors) { waitMillis = connector.getProtocolHandler().awaitConnectionsClose(waitMillis); } }
// Pause the connectors for (Connector connector: connectors) { connector.pause(); } }
// Stop our defined Container once the Connectors are all paused if (engine != null) { synchronized (engine) { engine.stop(); } }
// Now stop the connectors synchronized (connectorsLock) { for (Connector connector: connectors) { if (!LifecycleState.STARTED.equals( connector.getState())) { // Connectors only need stopping if they are currently // started. They may have failed to start or may have been // stopped (e.g. via a JMX call) continue; } connector.stop(); } }
// If the Server failed to start, the mapperListener won't have been // started if (mapperListener.getState() != LifecycleState.INITIALIZED) { mapperListener.stop(); }
if (protocolHandler == null) { thrownew LifecycleException( sm.getString("coyoteConnector.protocolHandlerInstantiationFailed")); }
// 初始化 adapter adapter = new CoyoteAdapter(this); protocolHandler.setAdapter(adapter); // 交给protocolHandler if (service != null) { protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor()); }
// 设置parseBody的方法,默认为POST if (null == parseBodyMethodsSet) { setParseBodyMethods(getParseBodyMethods()); }
// 校验 if (protocolHandler.isAprRequired() && !AprStatus.isInstanceCreated()) { thrownew LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener", getProtocolHandlerClassName())); } if (protocolHandler.isAprRequired() && !AprStatus.isAprAvailable()) { thrownew LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary", getProtocolHandlerClassName())); } if (AprStatus.isAprAvailable() && AprStatus.getUseOpenSSL() && protocolHandler instanceof AbstractHttp11JsseProtocol) { AbstractHttp11JsseProtocol<?> jsseProtocolHandler = (AbstractHttp11JsseProtocol<?>) protocolHandler; if (jsseProtocolHandler.isSSLEnabled() && jsseProtocolHandler.getSslImplementationName() == null) { // OpenSSL is compatible with the JSSE configuration, so use it if APR is available jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName()); } }
endpoint/** * Endpoint that provides low-level network I/O - must be matched to the * ProtocolHandler implementation (ProtocolHandler using NIO, requires NIO * Endpoint etc.). */ privatefinal AbstractEndpoint<S,?> endpoint;
// Separated out to make it easier for folks that extend NioEndpoint to // implement custom [server]sockets protectedvoidinitServerSocket()throws Exception { if (!getUseInheritedChannel()) { serverSock = ServerSocketChannel.open(); // 打开ServerSocket通道 socketProperties.setProperties(serverSock.socket()); InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset()); serverSock.socket().bind(addr,getAcceptCount()); // 绑定到指定服务地址和端口,这样你才可以通过这个访问服务(处理请求) } else { // Retrieve the channel provided by the OS Channel ic = System.inheritedChannel(); if (ic instanceof ServerSocketChannel) { serverSock = (ServerSocketChannel) ic; } if (serverSock == null) { thrownew IllegalArgumentException(sm.getString("endpoint.init.bind.inherited")); } } serverSock.configureBlocking(true); //mimic APR behavior }
/** * Begin processing requests via this Connector. * * @exception LifecycleException if a fatal startup error occurs */ @Override protectedvoidstartInternal()throws LifecycleException {
// Validate settings before starting if (getPortWithOffset() < 0) { thrownew LifecycleException(sm.getString( "coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset()))); }