启动流程分析
Pre
Tomcat - Tomcat 8.5.55 启动过程源码分析阶段一_init实例化Bootstrap
我们分析了 init 的主要功能,实例化Bootstrap , 调用init 通过反射调用
Catalina#setParentClassLoader ,后面调用的load 和 start方法 均为 反射调用的Catalina对象的load和start 方法。
load 加载初始化
总体预览
源码解析
load()
我们梳理关键脉络
// Digester对象 用于解析 server.xml Digester digester = createStartDigester(); // tomcat的配置文件 server.xml file = configFile(); // 解析 xml 重点关注返回的对象 root digester.parse(inputSource);
进到这个parse方法
/** * Parse the content of the specified input source using this Digester. * Returns the root element from the object stack (if any). * * @param input Input source containing the XML data to be parsed * @return the root object * @exception IOException if an input/output error occurs * @exception SAXException if a parsing exception occurs */ public Object parse(InputSource input) throws IOException, SAXException { configure(); getXMLReader().parse(input); return root; }
那我们看下我们source目录下的精简后的server.xml
结合tomcat总体的架构图,套娃结构 , server-service-----connector/container-----engine-----host-----context-----wrapper
刚才debug出来的root对象是不是很好理解了呢?
我们看下LifeCycle接口的继承关系, tomcat类的命名其实是很规范的 StandardXXXX
那继续看下 root对象的 及 配置文件的关系
Server初始化
继续往下跟
// Servier初始化 getServer().init();
调用的是 LifeCycle的init接口 , 跟进去可以看到是进入到了 LifeCycleBase这个抽象类的init方法
在init方法中 ,调用
// 初始化的关键方法 (抽象方法 交由子类实现 设计模式中的模板模式) initInternal();
可以看到这个方法是抽象方法 , 具体的实现由继承了LifeCycleBase这个抽象的子类实现具体的业务逻辑。
这里使用了模板模式 . 我们看下LifeCycleBase抽行类的具体实现
很规范的实现 ,每个组件实例化 都要从LifeCycleBase中走一遍 ,自行实现 init方法。
既然这里是Server, 那到StandardServer # initInternal 中看下
最重要的代码 实例化Service
for (Service service : services) { service.init(); }
这里可以看出来,Service 支持配置多个,不过通常情况下,不建议这么做。 想配置多个,干嘛不多起个tomcat呢?
Service初始化
同样的模板模式 , 还是会调用到 StandardService # initInternal ,精简后的核心代码如下
@Override protected void initInternal() throws LifecycleException { super.initInternal(); if (engine != null) { engine.init(); } // Initialize our defined Connectors synchronized (connectorsLock) { for (Connector connector : connectors) { connector.init(); } } }
可以看到 StandardService # initInternal 中 主要干了两件事儿 engine.init 和 connector.init
Engine初始化
同样的老一套,模板模式, 跟进到 StandardEngine # initInternal
@Override protected void initInternal() throws LifecycleException { getRealm(); super.initInternal(); }
可以看到调用的是父类的 initInternal , 父类 ContainerBase
@Override protected void initInternal() throws LifecycleException { BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>(); startStopExecutor = new ThreadPoolExecutor( getStartStopThreadsInternal(), getStartStopThreadsInternal(), 10, TimeUnit.SECONDS, startStopQueue, new StartStopThreadFactory(getName() + "-startStop-")); startStopExecutor.allowCoreThreadTimeOut(true); super.initInternal(); }
干了件啥事儿? 无非就是实例化了一个连接池startStopExecutor , 这个线程池的主要作用是在后面的start方法中使用。Engine可以配置多个Host,这个连接池的作用主要是为了并行初始化Host
Connector 初始化
继续 StandardService # initInternal , 接着 Connector 实例化, for循环嘛 ,一看就是支持配置多个Connector
for (Connector connector : connectors) { connector.init(); }
Connector# initInternal
核心代码
@Override protected void initInternal() throws LifecycleException { super.initInternal(); // Initialize adapter 并绑定 protocolHandler adapter = new CoyoteAdapter(this); protocolHandler.setAdapter(adapter); // protocolHandler初始化,主要是 内部EndPoint的初始化 protocolHandler.init(); }
CoyoteAdapter 对应tomcat架构图,是不是就是 Http 和 Servlet 之间用来做转换的那个Adapter ?
protocolHandler.setAdapter(adapter) 绑定
继续看
// protocolHandler初始化,主要是 内部EndPoint的初始化 protocolHandler.init();
进入 AbstractHttp11Protocol # init
@Override public void init() throws Exception { for (UpgradeProtocol upgradeProtocol : upgradeProtocols) { configureUpgradeProtocol(upgradeProtocol); } super.init(); }
关注 super.init(); 调用抽象父类AbstractProtocol ,精简后的代码如下
@Override public void init() throws Exception { String endpointName = getName(); endpoint.setName(endpointName.substring(1, endpointName.length()-1)); endpoint.setDomain(domain); // 通信端点的初始化 endpoint.init(); }
核心: endpoint.init();
调用 AbstractEndpoint # init
public void init() throws Exception { if (bindOnInit) { bind(); bindState = BindState.BOUND_ON_INIT; } }
重点是bind 方法
NioEndpoint ,我们这里的版本是tomcat8+, 默认的是NIO
精简后的核心代码如下:
@Override public void bind() throws Exception { if (!getUseInheritedChannel()) { // 获取NIO 通道 serverSock = ServerSocketChannel.open(); socketProperties.setProperties(serverSock.socket()); InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort())); // 绑定端口,但尚未使用accept获取客户端连接 serverSock.socket().bind(addr,getAcceptCount()); } }
至此,load方法完毕
小结
通过一层层的分析,tomcat套娃式的设计,使用模板模式,一层层的初始化 ,是不是有了更深刻的理解了呢?
oad完了,接下来我们来看下start阶段都干了啥事儿,继续下一篇 ~