赞
踩
前文:
《Tomcat源码:启动类Bootstrap与Catalina的加载》
写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。
在前文中我们介绍了tomcat启动类的加载,在Catalina初始化时加载了server.xml,并调用了getServer().init()方法加载server接口的实现类standserver。另外还介绍了以standserver为代表的容器组件共同继承的用于管理生命周期的抽象类LifecycleBase。
本文我们接着前文的内容继续介绍standserver与standservice。
目录
1.2、LifecycleMBeanBase#initInternal
1.1、StandardService#initInternal
2.1、StandardService#startInternal
2.2、 MapperListener#startInternal
Catalina类的主要作用就是根据server.xml的配置来初始化Tomcat运行所需要的组件,比如 Server,Service 等等,然后调用成员变量Server类对象的init和start方法,来启动 tomcat。
一个 Server 类的实例就代表了一个 Tomcat 的容器,一个Tomcat 进程只会有一个 Server 实例。Server 是一个接口,它的实现类是 StandardServer
StandardServer的init方法由父类LifecycleBase实现,可以看到LifecycleBase使用模板模式将初始化的具体操作留给了每个容器自己实现。
- @Override
- public final synchronized void init() throws LifecycleException {
- // 非NEW状态,不允许调用init()方法
- if (!state.equals(LifecycleState.NEW)) {
- invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
- }
-
- try {
- // 初始化逻辑之前,先将状态变更为`INITIALIZING`
- setStateInternal(LifecycleState.INITIALIZING, null, false);
- // 初始化,该方法为一个abstract方法,需要组件自行实现
- initInternal();
- // 初始化完成之后,状态变更为`INITIALIZED`
- setStateInternal(LifecycleState.INITIALIZED, null, false);
- } catch (Throwable t) {
- // 初始化的过程中,可能会有异常抛出,这时需要捕获异常,并将状态变更为`FAILED`
- ExceptionUtils.handleThrowable(t);
- setStateInternal(LifecycleState.FAILED, null, false);
- throw new LifecycleException(
- sm.getString("lifecycleBase.initFail",toString()), t);
- }
- }
先看第一步,调用父类中的initInternal,这个方法的实现我们可以在父类LifecycleMBeanBase中找到
- // StandardServer.java
- protected void initInternal() throws LifecycleException {
- // 调用父类LifecycleMBeanBase中的实现
- super.initInternal();
-
- // 其余代码
- }
该方法其实就是将当前容器注册到MBeanServer中,相关的知识可以看我之前的文章《Java8之JMX与MBean》,不了解的可以将它当作Spring中的IOC,可以管控容器中的方法调用与生命周期,这里就是将StandServer进行注册。
- // LifecycleMBeanBase.java
- private ObjectName oname = null;
- protected MBeanServer mserver = null;
-
- protected void initInternal() throws LifecycleException {
- if (oname == null) {
- mserver = Registry.getRegistry(null, null).getMBeanServer();
- oname = register(this, getObjectNameKeyProperties());
- }
- }
-
- protected final ObjectName register(Object obj,
- String objectNameKeyProperties) {
- StringBuilder name = new StringBuilder(getDomain());
- name.append(':');
- name.append(objectNameKeyProperties);
-
- ObjectName on = null;
-
- try {
- on = new ObjectName(name.toString());
- Registry.getRegistry(null, null).registerComponent(obj, on, null);
- } catch (Exception e) {
- log.warn(sm.getString("lifecycleMBeanBase.registerFail", obj, name), e);
- }
- return on;
- }
-
- // StandardServer.java
- protected String getObjectNameKeyProperties() {
- return "type=Server";
- }
再回到StandServer#initInternal中,调用完父类的initInternal方法后初始化 onameMBeanFactory和onameStringCache这属性,内容其实就是注册StringCache对象和 MBeanFactory对象到MBeanServer,再然后就是调用 globalNamingResources 对象的 init 方法。
- protected void initInternal() throws LifecycleException {
- // 调用父类LifecycleMBeanBase中的实现
- super.initInternal();
-
- // 注册StringCache对象和 MBeanFactory对象到MBeanServer
- onameStringCache = register(new StringCache(), "type=StringCache");
- MBeanFactory factory = new MBeanFactory();
- factory.setContainer(this);
- onameMBeanFactory = register(factory, "type=MBeanFactory");
-
- globalNamingResources.init();
-
- // 其余代码
- }
globalNamingResources为全局命名资源,对应server.xml中的<GlobalNamingResources>,用来定义一些外部访问资源,在StandServer构造方法中创建。
- private NamingResourcesImpl globalNamingResources = null;
- public StandardServer() {
- super();
- globalNamingResources = new NamingResourcesImpl();
- globalNamingResources.setContainer(this);
- if (isUseNaming()) {
- namingContextListener = new NamingContextListener();
- addLifecycleListener(namingContextListener);
- } else {
- namingContextListener = null;
- }
- }
再往下这段是将catalina的parentClassLoader(这个属性在Bootstrap#init方法里通过反射调用Catalina的setParentClassLoader将sharedClassLoader传进去,也就是说这个 parentClassLoader就是sharedClassLoader指向的对象,也就是一个URLClassLoader对象)里能加载的jar文件的目录,都添加到ExtensionValidator 这个集合中。
- protected void initInternal() throws LifecycleException {
- // 其余代码
- // 添加可以加载jar包的路径
- if (getCatalina() != null) {
- ClassLoader cl = getCatalina().getParentClassLoader();
- 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")) {
- ExtensionValidator.addSystemResource(f);
- }
- } catch (URISyntaxException | IOException e) {
- }
- }
- }
- }
- cl = cl.getParent();
- }
- }
- // 其余代码
- }
最后就是循环创建所有的service对象。Server里的service是在server.xml里定义的,在 Catalina解析server.xml的时候初始化,并注入到Server对象里。
显然 StandardServer#initInternal() 方法最重要的就是这段,调用 Service#init 方法来创建Service这个Tomcat的核心组件之一。
- protected void initInternal() throws LifecycleException {
- // 其余代码
- // 初始化service
- for (Service service : services) {
- service.init();
- }
- }
介绍完了server组件的初始化方法init,接着来看启动方法,该流程是在catlina中调用getServer().start()触发的。
和init方法一样,server的start方法也是由父类LifecycleBase中实现,也同样使用模板模式将具体实现留给了子类Standserver。
- public final synchronized void start() throws LifecycleException {
- // `STARTING_PREP`、`STARTING`和`STARTED时,将忽略start()逻辑
- if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
- LifecycleState.STARTED.equals(state)) {
- return;
- }
- // `NEW`状态时,执行init()方法
- if (state.equals(LifecycleState.NEW)) {
- init();
- }
- // `FAILED`状态时,执行stop()方法
- else if (state.equals(LifecycleState.FAILED)) {
- stop();
- }
- // 不是`INITIALIZED`和`STOPPED`时,则说明是非法的操作
- else if (!state.equals(LifecycleState.INITIALIZED) &&
- !state.equals(LifecycleState.STOPPED)) {
- invalidTransition(Lifecycle.BEFORE_START_EVENT);
- }
- try {
- // start前的状态设置
- setStateInternal(LifecycleState.STARTING_PREP, null, false);
- // start逻辑,抽象方法,由组件自行实现
- startInternal();
- // start过程中,可能因为某些原因失败,这时需要stop操作
- if (state.equals(LifecycleState.FAILED)) {
- stop();
- } else if (!state.equals(LifecycleState.STARTING)) {
- invalidTransition(Lifecycle.AFTER_START_EVENT);
- } else {
- // 设置状态为STARTED
- setStateInternal(LifecycleState.STARTED, null, false);
- }
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- setStateInternal(LifecycleState.FAILED, null, false);
- throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
- }
- }
方法的第一行代码先触发 CONFIGURE_START_EVENT 事件,以便执行 StandardServer 的 LifecycleListener 监听器,然后调用 setState 方法设置成 LifecycleBase 的 state 属性为 LifecycleState.STARTING。
接着就 globalNamingResources.start(),跟 initInternal 方法其实是类似的。
- protected void startInternal() throws LifecycleException {
- fireLifecycleEvent(CONFIGURE_START_EVENT, null);
- setState(LifecycleState.STARTING);
- globalNamingResources.start();
- // 启动service
- synchronized (servicesLock) {
- for (Service service : services) {
- service.start();
- }
- }
- }
本节介绍了Server组件,Server组件是tomcat的核心组件之一,它是通过调用init和start方法来启动tomcat的,而Server的init方法和start方法则是调用Service的init和start方法来启动 Service(tomcat的另一个核心组件)。
可以看出,一个 Tomcat 进程只有一个 Server 实例,一个 Server 实例可以包含多个 Service 对象。
Service 是Tomcat的核心组件之一,大概可以分为4个部分,service属性、executor属性、connector属性、engine属性。
- <!--
- 每个Service元素只能有一个Engine元素.元素处理在同一个<Service>中所有<Connector>元素接收到的客户请求
- -->
-
- <Service name="Catalina">
- <!-- 1. 属性说明
- name:Service的名称
- -->
-
- <!--2. 一个或多个excecutors -->
- <!--
- <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
- maxThreads="150" minSpareThreads="4"/>
- -->
-
- <!--
- 3.Connector元素:
- 由Connector接口定义.<Connector>元素代表与客户程序实际交互的组件,它负责接收客户请求,以及向客户返回响应结果.
- -->
- <Connector port="80" maxHttpHeaderSize="8192"
- maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
- enableLookups="false" redirectPort="8443" acceptCount="100"
- connectionTimeout="20000" disableUploadTimeout="true" />
- <!-- 属性说明
- port:服务器连接器的端口号,该连接器将在指定端口侦听来自客户端的请求。
- enableLookups:如果为true,则可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名;
- 若为false则不进行DNS查询,而是返回其ip地址。
- redirectPort:服务器正在处理http请求时收到了一个SSL传输请求后重定向的端口号。
- acceptCount:当所有可以使用的处理请求的线程都被用光时,可以放到处理队列中的请求数,超过这个数的请求将不予处理,而返回Connection refused错误。
- connectionTimeout:等待超时的时间数(以毫秒为单位)。
- maxThreads:设定在监听端口的线程的最大数目,这个值也决定了服务器可以同时响应客户请求的最大数目.默认值为200。
- protocol:必须设定为AJP/1.3协议。
- address:如果服务器有两个以上IP地址,该属性可以设定端口监听的IP地址,默认情况下,端口会监听服务器上所有IP地址。
- minProcessors:服务器启动时创建的处理请求的线程数,每个请求由一个线程负责。
- maxProcessors:最多可以创建的处理请求的线程数。
- minSpareThreads:最小备用线程 。
- maxSpareThreads:最大备用线程。
- debug:日志等级。
- disableUploadTimeout:禁用上传超时,主要用于大数据上传时。
- -->
-
-
- <Connector port="8009" enableLookups="false" redirectPort="8443" protocol="AJP/1.3" />
- <!-- 负责和其他HTTP服务器建立连接。在把Tomcat与其他HTTP服务器集成时就需要用到这个连接器。 -->
-
- <!--
- 4. Engine
- -->
- <Engine name="Catalina" defaultHost="localhost">
-
- </Engine>
- </Service>
上文中分析了 Server 类的 init 和 start 方法,其中最核心的内容就是调用了 StandardServer 类的的 Service 类型的成员的 init 和 start 方法。Service 的实现类是StandardService。StandardService和StandardServer一样也是继承自 LifecycleMBeanBase。
开头和StandServer一样调用了父类的initInternal方法,然后分别调用了四个成员变量engine、executor、mapperListener、connector的init方法。
- protected void initInternal() throws LifecycleException {
- super.initInternal();
- if (engine != null) {
- engine.init();
- }
- for (Executor executor : findExecutors()) {
- if (executor instanceof JmxEnabled) {
- ((JmxEnabled) executor).setDomain(getDomain());
- }
- executor.init();
- }
- mapperListener.init();
- synchronized (connectorsLock) {
- for (Connector connector : connectors) {
- try {
- connector.init();
- } catch (Exception e) {
- String message = sm.getString("standardService.connector.initFailed", connector);
- log.error(message, e);
-
- if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
- throw new LifecycleException(message);
- }
- }
- }
- }
- }
先看看这几个成员变量的定义
- private Engine engine = null;
- protected final ArrayList<Executor> executors = new ArrayList<>();
- protected final MapperListener mapperListener = new MapperListener(this);
- protected Connector connectors[] = new Connector[0];
除了mapperListener,都是解析server.xml文件时根据配置文件的配置初始化的。其中engine 和connector也是Tomcat核心组件之二,会分别单独解析,这里就先略过。
executor的实现类是,StandardThreadExecutor,同样继承自 LifecycleMBeanBase,它的作用跟线程池类似,这部分内容我们会在介绍connect时一并介绍。
- public Executor[] findExecutors() {
- synchronized (executors) {
- return executors.toArray(new Executor[0]);
- }
- }
mapperListener的作用是在start的时候将容器类对象注册到Mapper对象中,这里我们先来看下他的构造方法。
- // StandardService.java
- protected final MapperListener mapperListener = new MapperListener(this);
-
- // MapperListener.java
- private final Mapper mapper;
- private final Service service;
- public MapperListener(Service service) {
- this.service = service;
- this.mapper = service.getMapper();
- }
service.getMapper() 返回的是StandardService对象的mapper成员变量,该变量用来处理 Http 请求。Tomcat使用Mapper来处理一个Request到Host、Context 的映射关系,从而决定使用哪个 Service 来处理请求。
MapperListener不过没有重载 initInternal 方法,因此init只是单纯的注册了下MBean,没有别的操作。
- protected void startInternal() throws LifecycleException {
- setState(LifecycleState.STARTING);
- if (engine != null) {
- synchronized (engine) {
- engine.start();
- }
- }
- synchronized (executors) {
- for (Executor executor : executors) {
- executor.start();
- }
- }
- mapperListener.start();
- synchronized (connectorsLock) {
- for (Connector connector : connectors) {
- try {
- if (connector.getState() != LifecycleState.FAILED) {
- connector.start();
- }
- } catch (Exception e) {
- log.error(sm.getString("standardService.connector.startFailed", connector), e);
- }
- }
- }
- }
startInternal 跟 initInternal 方法一样,也是依次调用。executor 的 start 方法初始化了内部的线程池,具体内容我们后续介绍,这里先看下mapperListener的启动。
- engine.start();
- executor.start();
- mapperListener.start();
- connector.start();
- public void startInternal() throws LifecycleException {
- setState(LifecycleState.STARTING);
- Engine engine = service.getContainer();
- if (engine == null) {
- return;
- }
- findDefaultHost();
- addListeners(engine);
- Container[] conHosts = engine.findChildren();
- for (Container conHost : conHosts) {
- Host host = (Host) conHost;
- if (!LifecycleState.NEW.equals(host.getState())) {
- registerHost(host);
- }
- }
- }
findDefaultHost()其实就是找出 defaultHost ,并存储到mapper中。这个 defaultHost 是 server.xml 的 <Engine> 标签的属性,一般都是 "localHost"。
for循环中的代码块总结下就是取出Engine中的子Container(即host)然后找出一个名字跟 defaultHost 指定的名字相同的 Host 对象。
- private void findDefaultHost() {
- Engine engine = service.getContainer();
- // 找出defaultHost
- String defaultHost = engine.getDefaultHost();
- boolean found = false;
- if (defaultHost != null && defaultHost.length() > 0) {
- Container[] containers = engine.findChildren();
- for (Container container : containers) {
- Host host = (Host) container;
- if (defaultHost.equalsIgnoreCase(host.getName())) {
- found = true;
- break;
- }
- String[] aliases = host.findAliases();
- for (String alias : aliases) {
- if (defaultHost.equalsIgnoreCase(alias)) {
- found = true;
- break;
- }
- }
- }
- }
- if (found) {
- mapper.setDefaultHostName(defaultHost);
- } else {
- log.error(sm.getString("mapperListener.unknownDefaultHost", defaultHost, service));
- }
- }
addListeners就是将MapperListener这个监听器添加到 Engine 及其子容器中
- private void addListeners(Container container) {
- container.addContainerListener(this);
- container.addLifecycleListener(this);
- for (Container child : container.findChildren()) {
- addListeners(child);
- }
- }
最后是for循环中的代码,这段的作用就是,调用 registerHost方法来注册 Engine 的字容器 Host。
- Container[] conHosts = engine.findChildren();
- for (Container conHost : conHosts) {
- Host host = (Host) conHost;
- if (!LifecycleState.NEW.equals(host.getState())) {
- // Registering the host will register the context and wrappers
- registerHost(host);
- }
- }
registerHost方法先调用mapper.addHost将Host加入的Mapper类的的成员变量,然后调用 registerContext方法注册Host的子容器Context。
- private void registerHost(Host host) {
- String[] aliases = host.findAliases();
- mapper.addHost(host.getName(), aliases, host);
- for (Container container : host.findChildren()) {
- if (container.getState().isAvailable()) {
- registerContext((Context) container);
- }
- }
- findDefaultHost();
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("mapperListener.registerHost", host.getName(), domain, service));
- }
- }
registerContext又继续将context下面的每个wrapper与context都添加到mapper
- private void registerContext(Context context) {
-
- String contextPath = context.getPath();
- if ("/".equals(contextPath)) {
- contextPath = "";
- }
- Host host = (Host) context.getParent();
-
- WebResourceRoot resources = context.getResources();
- String[] welcomeFiles = context.findWelcomeFiles();
- List<WrapperMappingInfo> wrappers = new ArrayList<>();
-
- for (Container container : context.findChildren()) {
- prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);
-
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("mapperListener.registerWrapper", container.getName(), contextPath, service));
- }
- }
-
- mapper.addContextVersion(host.getName(), host, contextPath, context.getWebappVersion(), context, welcomeFiles,
- resources, wrappers);
-
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("mapperListener.registerContext", contextPath, service));
- }
- }
prepareWrapperMappingInfo用于准备注册到mapper下的wrapper,这儿mapper对于wrapper的支持是wrapper的包装对象WrapperMappingInfo。而一个context可能有多个wrapper,所以WrapperMappingInfo是一个list。
简单来说就是将映射url、wrapper名字和资源只读标记等信息组合成对象添加到wrappers中。
- private void prepareWrapperMappingInfo(Context context, Wrapper wrapper, List<WrapperMappingInfo> wrappers) {
- String wrapperName = wrapper.getName();
- boolean resourceOnly = context.isResourceOnlyServlet(wrapperName);
- String[] mappings = wrapper.findMappings();
- for (String mapping : mappings) {
- boolean jspWildCard = (wrapperName.equals("jsp") && mapping.endsWith("/*"));
- wrappers.add(new WrapperMappingInfo(mapping, wrapper, jspWildCard, resourceOnly));
- }
- }
上面的分析中提到了 Engine、Host、Context、Wrapper 和 Connector。除了 Connector,其余的都是 Container 接口的实现类。它们的父类是 ContainerBase,ContainerBase 继承自 LifecycleMBeanBase,因此 Container 也有生命周期方法。
每一个 Container 都可能有子Container,其中,Engine的子Container是Host,Host的子 Container是Context,Context 的子Container是Wrapper,这里说的父子关系,不是指类继承关系,而是说一个 Container 内部有一个 Map,这个 Map 保存了其他类型的 Container。
Container是能够执行客户端请求并且给出响应的对象,Tomcat便是用这些 Container 对象逐层地处理请求的。
参考资料:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。