栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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 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这两个有什么区别呢?后续再做分析

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1009163.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号