springboot版本:2.2.2
Springboot啟動,SpringApplication.run方法中,createApplicationContext方法創建一個工廠:AnnotationConfigEmbeddedWebApplicationContext之后,調用applicationContext的refresh方法,方法中其父類EmbeddedWebApplicationContext的onRefresh方法在調用父類的onRefresh之后,再調用 createEmbeddedServletContainer,從Spring工廠中找到EmbeddedServletContainerFactory的實例,(注:其實這是個TomcatEmbeddedServletContainerFactory的注冊是在EmbeddedServletContainerAutoConfiguration中,而這個autoConfiguration是spring-boot-autoconfigure包中spring.factories中***.***.**EnableAutoConfiguration的一個值,之所以返回的是Tomcat的Factory而不是Jetty或者Undertow是因為后兩者需要導入需要的jar包)並調用factory的getEmbeddedServletContainer方法(傳入ServletContextInitializer作為參數),new 一個Tomcat,set一個new 的Connector,傳入Http11NioProtocol作為參數,說明springboot內嵌的tomcat用的nio,getService方法初始化一個StandardServer並為server添加一個StandardService,為service添加一個connector。getHost方法初始化一個StandardEngine,放到service中,又new 一個StandardHost放到engine中,prepareContext方法中,new 一個TomcatEmbeddedContext,放到host中。(注意這里的server-service-engine-host-context,以及后面說到的wrapper,在new的時候都會同時new一個standard****valve放到其pipeline屬性中,以便以后鏈式的處理request和response)。最后new 一個tomcatEmbeddedServletContainer返回,在構造方法中有initialize,其中調用this.tomcat.start(),啟tomcat,server.start()---service.start()--->protocolHandler.start()--->endpoint.start()。
NioEndPoint.start中,bindWithCleanup--->bind方法中,serverSock = ServerSocketChannel.open()是生成一個ServerSocketChannel;serverSock.socket().bind(addr,getAcceptCount())是把ServerSocketChannel綁定到端口上,bind之后startInternal中,初始化一個Poller和一個Acceptor兩個線程,其中Acceptor是管理TCP握手建立連接的,其run方法中endpoint.serverSocketAccept()其實是調用剛才的ServerSocketChannel.accept(),生成一個socketChannel;而endpoint.setSocketOptions方法先把socketChannel封裝一下,接着調用了poller.register,點進去看,再封裝成了PollerEvent,通過addEvent方法放到events中,而PollerEvent的run方法中有socket.getIOChannel().register方法,點進去看,其實就是把剛才生成的socketChannel注冊到poller的selector上。而poller線程調用poller的run方法,每次循環開始的時候都會把events中的PollerEvent取出,執行run方法。也就是實際上把socketChannel給注冊了,然后遍歷selector上的key,調用processSocket方法,封裝成一個實現了runnable的SocketProcessorBase放到線程池中,其run方法中有getHandler().process(socketWrapper, event)--->processor.process(wrapper, status)--->service(socketWrapper--->getAdapter().service(request, response)--->connector.getService().getContainer().getPipeline().getFirst().invoke(request, response)--->service的container是engine,pipeline的first是null,所以會調用默認的StandardEngineValve,取出request里面的host,調用host的pipeline鏈中 first.getNext()[next是StandardHostValve]的invoke,調用StandardContextValve的invoke,從request中取出Wrapper,調用StandardWrapperValve的invoke方法,從host中取出filter,形成filterChain,把servlet(DispatcherServlet)放進去,然后是filterChain.dofilter+dispatherServlet.dispatch,下面就是SpringMVC的東西了。
未解決:dispatherServlet是怎么被放到request中,然后取出來的?可能和service.start中的mapperListener.start有關?
對比tomcat和netty的nio框架的異同:
相同點:都是對Java nio的封裝
不同點:netty的兩個group都可以是多個線程,fatherGroup可以綁定多個端口。而tomcat對應fatherGroup的是Acceptor,單線程while循環處理握手,因為只綁定了一個端口,這個和netty差不太多,一個端口對應一個線程;而對應childGroup的是poller,也是單線程遍歷selector,也就是一個線程處理多個tcp連接的讀和寫,記得1.5.幾的springboot用的還是poller數組,多個線程輪流分配channel。不過select這時候的cpu占用並不是很高,而后面對運算要求高的springMVC里面用戶自己寫的邏輯的處理,是放在線程池中的,這樣看來,也是可行的。