(六)SpringBoot源码解析----springboot 与 tomcat

spring版本 5.x
jdk版本 1.8
spring boot 2.x

1.概述

   本文将介绍下 springboot 中,tomcat启动时做了哪些事。

  1. 简述 tomcat结构, 主要介绍下connector 及 container
  2. connector 源码分析

    当浏览器向服务端发起请求时,实际是 tomcat 接收到了请求,并处理了请求,所以如果我们想要完全了解浏览器的一条请求是怎样被处理的,也需要先了解下 tomcat。

2. tomcat结构简述

先盗一张官网的图
在这里插入图片描述
tomcat 的结构主要分为以上六个部分, 其中 connector负责处理与客户端的连接。所以分析 connector是首要任务,connect 会把接收到的请求交给其它组件处理。

3. connector 源码分析

   springboot中, tomcat是在 ServletWebServerApplicationContext的finishRefresh()方法中启动

protected void finishRefresh() {
		super.finishRefresh();
		//启动 tomcat
		WebServer webServer = startWebServer();
		if (webServer != null) {
			publishEvent(new ServletWebServerInitializedEvent(webServer, this));
		}
	}

继续深入代码,发现是由StandardService的 addConnector()

  public void addConnector(Connector connector) {
        synchronized (connectorsLock) {
            //忽略代码...
            if (getState().isAvailable()) {
                try {
                	//启动 connector
                    connector.start();
                } catch (LifecycleException e) {
                    log.error(sm.getString(
                            "standardService.connector.startFailed",
                            connector), e);
                }
            }
            // Report this property change to interested listeners
            support.firePropertyChange("connector", null, connector);
        }
    }

接续深入Connector的 start()方法, Connectors是LifecycleBase子类,所以最后调用的是Connector的startInternal()方法。

protected void startInternal() throws LifecycleException {

        // Validate settings before starting
        if (getPort() < 0) {
            throw new LifecycleException(sm.getString(
                    "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
        }

        setState(LifecycleState.STARTING);

        try {
            protocolHandler.start();
        } catch (Exception e) {
            throw new LifecycleException(
                    sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
        }
    }

继续跟进代码,最后进入的是NioEndpoint的成员类 Poller的 run 方法。 是的, Poller是个 Runnable, 你才的没错

  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);
                        }
                        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("",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 attachment = (NioSocketWrapper)sk.attachment();
                    // Attachment may be null if another thread has called
                    // cancelledKey()
                    if (attachment == null) {
                        iterator.remove();
                    } else {
                        iterator.remove();
                        processKey(sk, attachment);
                    }
                }//while

                //process timeouts
                timeout(keyCount,hasEvents);
            }//while

            getStopLatch().countDown();
        }

上面贴了一大堆代码,也没怎么分析,不过不重要,我们只是想找到 connector的 启动到底是启动个啥。现在我们看到了:其实是启动了一个 selector , selector 是 NIO 的一个组件 (对于 NIO 感兴趣,可以看看这篇NIO简介)。selector负责监听网络事件,然后通过下面的方法处理事件:

 processKey(sk, attachment);

进入 Poller 的 processKey方法,关键代码为:

if (sk.isReadable()) {
    if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {
        closeSocket = true;
    }
}
if (!closeSocket && sk.isWritable()) {
    if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {
        closeSocket = true;
    }
}

其中 attachment 是NioSocketWrapper类型,接下来进入processSocket()方法,一路跟踪下去,最后执行 NioEndpoint的成员类SocketProcessor的doRun()方法。
至此已经完成了对于网络时间的监听,doRun()方法是对事件的真正处理了。其中会有一些关于 HTTP 的操作,最后会转给 spring 处理,关于 spring 的部分会在下一篇文章中分析。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值