SpringBoot中内置tomcat启动和监听器开启流程(粗浅的一个流程分析,记录一下内置tomcat的创建和使用IO多路复用)

Spring Boot内置tomcat实例创建

Spring Boot 启动过程中调用ServletWebServerApplicationContext类的onRefresh方法

ServletWebServerApplicationContext
@Override
protected void onRefresh() {
		super.onRefresh();
		try {
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}

再继续调用createWebServer方法创建一个tomcat

private void createWebServer() {
		WebServer webServer = this.webServer;//第一次进入时this.webServer = null
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
			StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
			ServletWebServerFactory factory = getWebServerFactory();
			createWebServer.tag("factory", factory.getClass().toString());
			this.webServer = factory.getWebServer(getSelfInitializer());
			createWebServer.end();
			getBeanFactory().registerSingleton("webServerGracefulShutdown",
					new WebServerGracefulShutdownLifecycle(this.webServer));
			getBeanFactory().registerSingleton("webServerStartStop",
					new WebServerStartStopLifecycle(this, this.webServer));
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
		initPropertySources();
	}

继续看getWebServer方法

@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) {
		if (this.disableMBeanRegistry) {
			Registry.disableRegistry();
		}
		Tomcat tomcat = new Tomcat(); //创建一个tomcat
		File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		for (LifecycleListener listener : this.serverLifecycleListeners) {
			tomcat.getServer().addLifecycleListener(listener);
		}
		Connector connector = new Connector(this.protocol);
		connector.setThrowOnFailure(true);
		tomcat.getService().addConnector(connector);
		customizeConnector(connector);
		tomcat.setConnector(connector);
		tomcat.getHost().setAutoDeploy(false);
		configureEngine(tomcat.getEngine());
		for (Connector additionalConnector : this.additionalTomcatConnectors) {
			tomcat.getService().addConnector(additionalConnector);
		}
		prepareContext(tomcat.getHost(), initializers);
		return getTomcatWebServer(tomcat);
	}

Tomcat tomcat = new Tomcat(); 创建tomcat

tomcat类的包路径 org.apache.catalina.startup.Tomcat

public Tomcat() {
        ExceptionUtils.preload();
    }

tomcat的默认构造方法只执行了一个preload方法进行预加载处理

由于该类在错误处理中被广泛使用,因此谨慎的做法是预先加载它以避免加载该类的任何失败,从而掩盖错误处理期间的真正问题。
public static void preload() {
        // NO-OP
}

在preload方法中没有做任何事情 源代码注释中说可以做一些简单的预加载

继续看tomcat的后续创建流程

File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir(“tomcat”); 创建根路径 后续在创建文件或者其他的文件会保存在这个临时目录中

在这里插入图片描述

tomcat.setBaseDir(baseDir.getAbsolutePath());

将上一步创建的路径设置到创建tomcat的根路径下,tomcat在前面的创建过程并没有指定根路径

指定连接协议 Connector connector = new Connector(this.protocol);

public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";
private String protocol = DEFAULT_PROTOCOL;

tomcat.getService().addConnector(connector); 将连接器添加到tomcat中

getTomcatWebServer(tomcat);

包装tomcat 生成一个TomcatWebServer对象 - 调用TomcatWebServer的构造方法

public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
		Assert.notNull(tomcat, "Tomcat Server must not be null");
		this.tomcat = tomcat;
		this.autoStart = autoStart;
		this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
		initialize();
	}
private void initialize() throws WebServerException {
		logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
		synchronized (this.monitor) {
			try {
				...
				// Start the server to trigger initialization listeners
				this.tomcat.start(); //启动tomcat 开始监听端口
				...
			}
			catch (Exception ex) {
				stopSilently();
				destroySilently();
				throw new WebServerException("Unable to start embedded Tomcat", ex);
			}
		}
	}

tomcat实际启动监听器的流程

run方法运行
public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(VariousSpringBootApplication.class);
        //关闭banner
        springApplication.setBannerMode(Banner.Mode.OFF);
        //跟踪应用程序的启动顺序
        springApplication.setApplicationStartup(new BufferingApplicationStartup(2048));
        //启动springboot
        springApplication.run(args);
    }

	public ConfigurableApplicationContext run(String... args) {
			...
			refreshContext(context);
			...
	}

	protected void refresh(ConfigurableApplicationContext applicationContext) {
		applicationContext.refresh();
	}

	public final void refresh() throws BeansException, IllegalStateException {
		...
			super.refresh();
		...
	}

	@Override
	public void refresh() throws BeansException, IllegalStateException {
			...
				finishRefresh();
			...
		}
	}

	protected void finishRefresh() {
		...
		getLifecycleProcessor().onRefresh();
		...
	}

	@Override
	public void onRefresh() {
		startBeans(true);
		this.running = true;
	}

	private void startBeans(boolean autoStartupOnly) {
		...
		if (!phases.isEmpty()) {
			phases.values().forEach(LifecycleGroup::start);
		}
		...
	}
	
	public void start() {
		...
		for (LifecycleGroupMember member : this.members) {						
			doStart(this.lifecycleBeans, member.name, this.autoStartupOnly);
		}
	}

	.....

	class - Connector
	protected void startInternal() throws LifecycleException {
        ...
            protocolHandler.start(); //启动监听器核心方法
        ...
    }

	class - Connector
	public void start() throws Exception {
        ...
        endpoint.start();
        ...
    }

	class - AbstractEndpoint <- NioEndpoint(具体实现类在tomcat创建过程中被创建)
	public final void start() throws Exception {
        ...
        startInternal();
        ...
    }

	@Override
    public void startInternal() throws Exception {

        if (!running) {
            running = true;
            paused = false;

            if (socketProperties.getProcessorCache() != 0) {
                processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getProcessorCache());
            }
            if (socketProperties.getEventCache() != 0) {
                eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getEventCache());
            }
            if (socketProperties.getBufferPool() != 0) {
                nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getBufferPool());
            }

            // Create worker collection
            if (getExecutor() == null) {
                createExecutor();
            }

            initializeConnectionLatch();

            // Start poller thread
            poller = new Poller();//监听器 - 核心点(Selector)
            Thread pollerThread = new Thread(poller, getName() + "-Poller");
            pollerThread.setPriority(threadPriority);//设置线程优先级
            pollerThread.setDaemon(true);//设置为守护进程
            pollerThread.start();//运行程序

            startAcceptorThread();//启动一个监听处理的程序(当监听器监听到一个请求会将请求发送到这里)
        }
    }
    
	protected void startAcceptorThread() {
        acceptor = new Acceptor<>(this);//具体执行程序
        String threadName = getName() + "-Acceptor";
        acceptor.setThreadName(threadName);
        Thread t = new Thread(acceptor, threadName);
        t.setPriority(getAcceptorThreadPriority());
        t.setDaemon(getDaemon());
        t.start();
    }

在Spring Boot的启动流程中将tomcat作为一个内置的容器启动 相比于之前的ssm架构不需要在配置web.xml tomcat的属性配置都可以在application.xml中进行配置

Poller部分方法分析

Poller位于NioEndpoint中的一个公共内部类

	//构造方法
	public Poller() throws IOException {
        this.selector = Selector.open();
    }
	//Poller的构造方法最终获取的是一个WindowsSelectorImpl(selector具体执行原理不做分析) - windows环境
	public AbstractSelector openSelector() throws IOException {
        return new WindowsSelectorImpl(this);
    }

	//核心方法
	public void run() {
            // Loop until destroy() is called
            while (true) {
                boolean hasEvents = false;
                try {
                    if (!close) {
                        hasEvents = events();
                        if (wakeupCounter.getAndSet(-1) > 0) {
                            // If we are here, means we have other stuff to do
                            // Do a non blocking select
                            keyCount = selector.selectNow();
                        } else {
                            keyCount = selector.select(selectorTimeout); //调用的是WindwosSelectorImpl放啊发的select
                        }
                        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(sm.getString("endpoint.nio.selectorLoopError"), 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 socketWrapper = (NioSocketWrapper) sk.attachment();
                    // Attachment may be null if another thread has called
                    // cancelledKey()
                    if (socketWrapper == null) {
                        iterator.remove();
                    } else {
                        iterator.remove();
                        processKey(sk, socketWrapper);
                    }
                }

                // Process timeouts
                timeout(keyCount,hasEvents);
            }

            getStopLatch().countDown();
        }

	protected int doSelect(long var1) throws IOException {
       ...
       this.subSelector.poll();
       ...
    }

	 private int poll() throws IOException {
            return this.poll0(WindowsSelectorImpl.this.pollWrapper.pollArrayAddress, Math.min(WindowsSelectorImpl.this.totalChannels, 1024), this.readFds, this.writeFds, this.exceptFds, WindowsSelectorImpl.this.timeout);
        }
           
    //最终调用的native方法(有空再分析-需要编译openJdk) 
    private native int poll0(long var1, int var3, int[] var4, int[] var5, int[] var6, long var7);  

后续流程暂不做分析

题外话 netty使用的NioEventLoop tomcat使用的是NioEndpoint这两个有什么区别呢?后续再做分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值