版本:spring-boot-1.5.17.RELEASE.jar,jetty-servlet-9.4.41.v20210516.jar
springboot启动刷新上下文也是走的AbstractApplicationContext细节不再多说,我们直接进入正题,在onRefresh这个环节是springboot接入内嵌web容器的环节。在创建上下文时系统会判断当前是否是web环境,如果是则创建AnnotationConfigEmbeddedWebApplicationContext默认的web上下文。该上下文中的onRefresh环节进行创建内嵌的容器
新版本spring-boot-2.2.1.RELEASE : org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
@Override
protected void onRefresh() {
super.onRefresh();
try {
createEmbeddedServletContainer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start embedded container",
ex);
}
}
创建内嵌的Servlet容器
- 获取内嵌的容器工厂EmbeddedServletContainerFactory,假定为jetty环境即JettyEmbeddedServletContainerFactory
- 使用工厂方法获取内嵌的servlet容器,入参为_ServletContextInitializer上下文初始化对象列表_
- getEmbeddedServletContainer:创建web上下文JettyEmbeddedWebAppContext与EmbeddedWebApplicationContext.getSelfInitializer()中的ServletContextInitializer实现绑定
- addHandlerWrappers:如果开启了压缩则创建createGzipHandler句柄包装JettyEmbeddedWebAppContext(也是句柄类型),如果存在serverHeader则创建ServerHeaderHandler句柄包装JettyEmbeddedWebAppContext(也是句柄类型)。默认不压缩也不存在header。即直接返回JettyEmbeddedWebAppContext。
- Server.setHandler中将hander句柄添加至管理的beans中(updateBean)
- addHandlerWrappers返回的句柄与org.eclipse.jetty.server.Server绑定
- 根据server与端口创建JettyEmbeddedServletContainer。构造器中执行初始化initialize。启动Server.start
- Server.start通过父类遍历管理的beans并启动start。包含:JettyEmbeddedWebAppContext.start。
- 回调父类WebAppContext启动方法doStart。父类ContextHandler无参构造器中创建ServletContext上下文实现Context(ContextHandler内部类)
获取spring-web自身的ServletContextInitializer列表
org.springframework.boot.web.servlet.ServletContextInitializer
- EmbeddedWebApplicationContext.createEmbeddedServletContainer方法获取ServletContextInitializer接口实现列表:getSelfInitializer
- EmbeddedWebApplicationContext.selfInitialize中创建ServletContextInitializerBeans
- 根据类型ServletContextInitializer从工厂中获取对象列表遍历
- 如果是ServletRegistrationBean类型以Servlet类型为key添加至initializers
- 如果是FilterRegistrationBean类型,获取对象的Filter属性并以Filter类型添加至initializers
- 如果是DelegatingFilterProxyRegistrationBean类型同Filter相同,source需要通过代理的getTargetBeanName获取
- 如果是ServletListenerRegistrationBean类型,获取对象的listener属性并以EventListener类型添加至initializers
- 其他按照ServletContextInitializer类型添加至initializers
- addAdaptableBeans:根据类型(Servlet、Filter、EventListener)从工厂中获取bean,将bean封装为对应的RegistrationBean并添加至initializers(后面会遍历列表注册bean至ServletContext上下文)
- 排序initializers:sortedList
springboot自动注册Servlet至ServletContext
回调spring-web自身ServletContextInitializer.onStartup
EmbeddedWebApplicationContext.selfInitialize遍历ServletContextInitializer回调onStartup方法入参为ServletContext,与Servlet对接
- 创建ServletContextInitializerBeans:构造器中根据类型(ServletContextInitializer)从工厂中获取ServletContextInitializer列表
- 遍历ServletContextInitializerBeans集合,调用集合元素ServletContextInitializer实现类的onStartup方法,即前面所提到的sorted列表:sortedList,常见的列表(ServletContextInitializer的ServletRegistrationBean实现类型):characterEncodingFilter、hiddenHttpMethodFilter、httpPutFormContentFilter、springSecurityFilterChain、dispatcherServlet、requestContextListener、SessionRepositoryFilter(共享session的Filter)
@Override
public Iterator<ServletContextInitializer> iterator() {
//sortedList排序后的ServletRegistrationBean列表
return this.sortedList.iterator();
}
- springmvc的dispatchServlet由DispatcherServletAutoConfiguration配置自动注册,其余由应用本身配置注册或其他自动配置注册
- 均为ServletRegistrationBean类型。ServletRegistrationBean类的onStartup方法将绑定的servlet注册至ServletContext上下文并配置
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
Assert.notNull(this.servlet, "Servlet must not be null");
String name = getServletName();
...
logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings);
//注册至ServletContext上下文
Dynamic added = servletContext.addServlet(name, this.servlet);
if (added == null) {
logger.info("Servlet " + name + " was not registered "
+ "(possibly already registered?)");
return;
}
//配置servlet,urlMapping、setLoadOnStartup、setMultipartConfig
configure(added);
}
Jetty内嵌容器工厂获取容器
- 获取容器JettyEmbeddedServletContainerFactory.getEmbeddedServletContainer
- 创建应用上下文JettyEmbeddedWebAppContext(ServletContextHandler类型),在后面的handle链调用中回调:Server调用handle链的根节点(即第一个handle)
- 父类ServletContextHandler执行relinkHandlers,doSetHandler将handler关联至当前handler的_handler属性,并更新注册至当前容器的_beans中,下面均相同逻辑不赘述
- 创建SessionHandler并关联至_sessionHandler,doSetHandler,org.eclipse.jetty.server.session.SessionHandler(ManagedObject)
- 创建SecurityHandler并关联至_securityHandler,doSetHandler
- 创建GzipHandler并关联至_gzipHandler,doSetHandler
- 创建ServletHandler并关联至_servletHandler,doSetHandler,org.eclipse.jetty.servlet.ServletHandler(ManagedObject)
- 获取端口号port默认为0:org.springframework.boot.context.embedded.AbstractConfigurableEmbeddedServletContainer#getPort
- 根据地址与端口创建SocketAddress实例InetSocketAddress
- 如果Server对象存在ThreadPool类型入参的构造器则使用jetty9的工厂方法创建Server(org.eclipse.jetty.server.Server);否则使用jetty8(8使用set方法注入线程池);入参为工厂线程池属性。如果连接池为空,构造器中会指定QueuedThreadPool为默认的线程池,最大200线程,最小线程数为8,空闲超时时间60000毫秒(1分钟),线程池名称为qtp前缀+hashcode,线程名称为qtp前缀+线程id
- 根据InetSocketAddress与Server创建Connector,jetty8使用org.eclipse.jetty.server.nio.SelectChannelConnector,jetty9使用org.eclipse.jetty.server.ServerConnector
- Server绑定connector
- 配置WebAppContext上下文configureWebAppContext
- web上下文设置contextPath,如果有则设置,默认为根"/"
- 如果指定则添加默认servlet:addDefaultServlet
- 如果需要注册JspServlet则添加,并添加JasperInitializer bean
- 添加字符集
- 设置ServletContextInitializerConfiguration入参为Initializer列表
- 配置session:configureSession;上下文获取SessionHandler句柄,如果存在超时则设置超时时间,默认为-1;如果指定persistSession需要持久化session则创建默认缓存DefaultSessionCache,为缓存设置文件存储FileSessionDataStore实例。将session缓存绑定至SessionHandler句柄
- 为Server设置handler句柄
- 如果启用了压缩,使用GzipHandler包装JettyEmbeddedWebAppContext(原始handler)
- 如果header包含text,使用ServerHeaderHandler包装handler
- _handler更新后更新bean:updateBean,添加至当前Server的_beans中在Server容器start启动时同时启动_beans列表中的管理bean
- 如果启用ssl,配置ssl并使用ssl的Connector覆盖原Server的Connector
- 获取JettyServerCustomizer列表,为server绑定自定义配置
- 如果启用useForwardHeaders,为server绑定ForwardHeadersCustomizer自定义配置
- 创建JettyEmbeddedServletContainer实例,入参为Server。如果端口大于0则设置自动启动容器为true。
创建容器JettyEmbeddedServletContainer
- 初始化
- 启动server:org.eclipse.jetty.server.Server
- 调用父类启动方法AbstractLifeCycle.start
- 调用子类Server重写的doStart方法
- 配置ErrorHandler
- 调用父类doStart,遍历ContainerLifecycleLifecycle的_beans,如果bean是managed类型并且没启动调用start方法启动,如果是auto类型,running状态则移除该bean,否则置为managed类型并启动bean
- 遍历Server的connectors列表,启动connector,假定为jetty9的ServerConnector
for (Connector connector : _connectors)
{
try
{
connector.start();
}
catch(Throwable e)
{
mex.add(e);
}
}
服务器连接器ServerConnector
创建ServerConnector
- 根据工厂acceptors数量、selector数量创建连接器
- 调用父类构造器AbstractConnector
- 将server添加至ContainerLifecycle中的beans,类型为UNMANAGED
- 将_executor添加至ContainerLifecycle中的beans,类型为:如果已经running则为UNMANAGED,否则为AUTO类型,如果不是Lifecycle类型则类型为POJO
- _scheduler注册至beans同_executor逻辑
- _byteBufferPool(默认为ArrayByteBufferPool类型)注册至beans同_executor逻辑
- 如果acceptors处理数量指定数量小于0,取1与(4与核心数量除以8之间的最小值)两者中的最大值
- 根据acceptor数量创建线程数组_acceptors
- 创建新的Selector管理器ServerConnectorManager,根据executor、scheduler、selectors数量
- 添加_manager至beans,类型为Managed
- 为Connector设置host、port
启动ServerConnector
- 调用父类start方法回调子类doStart方法
- AbstractNetworkConnector父类回调子类open方法,调用父类doStart方法
- 如果_acceptChannel为空创建,打开ServerSocketChannel通道绑定地址端口,设置_acceptChannel阻塞为true
- 添加_acceptChannel至beans,类型为managed
- 继续调用父类AbstractConnector doStart方法
- 继续调用父类ContainerLifecycle doStart方法,即调用新注册进来bean的start方法,如果存在auto类型并且running状态且不是unmanaged类型,是InheritedListener类型则移除bean,否则置为unmanaged类型。否则调用start方法
- 根据_acceptors长度创建CountDownLatch监听_stopping
- 根据_acceptors长度创建Acceptor,并将Acceptor注册至bean(POJO类型),获取线程池执行Acceptor
执行Acceptor
- 如果不是处于accepting并且是running状态,_setAccepting等待通知
- 回调ServerConnector连接器accept方法
@Override
public void accept(int acceptorID) throws IOException
{
ServerSocketChannel serverChannel = _acceptChannel;
if (serverChannel != null && serverChannel.isOpen())
{
SocketChannel channel = serverChannel.accept();
accepted(channel);
}
}
- accept打开channel并设置为非阻塞类型
- 设置socket的SocketOptions.TCP_NODELAY属性打开
- SelectorManager选择管理器ServerConnectorManager监听服务器通道accept(channel)
//SelectorManager监听通道
public void accept(SelectableChannel channel, Object attachment) {
ManagedSelector selector = this.chooseSelector();
Objects.requireNonNull(selector);
//将ServerSocketChannel封装为Accept提交至updates,等待消费
selector.submit(new Accept(selector, channel, attachment));
}
//SelectorManager容器管理初始化
protected SelectorManager(Executor executor, Scheduler scheduler, int selectors) {
this._selectorIndex = new AtomicInteger();
this._acceptListeners = new ArrayList();
this._connectTimeout = 15000L;
if (selectors <= 0) {
//选择器默认数量
//如果线程池是SizedThreadPool类型:为cpu/2与线程池最大线程数/16之间的最小值
//否则cpu/2
selectors = defaultSelectors(executor);
}
this.executor = executor;
this.scheduler = scheduler;
this._selectors = new ManagedSelector[selectors];
//选择索引算法为index递增对长度取余即轮询方法
this._selectorIndexUpdate = (index) -> {
return (index + 1) % this._selectors.length;
};
}
//容器管理的bean会在初始化管理器之后,启动管理选择器ManagedSelector,也是通过容器启动
protected void doStart() throws Exception {
this._lease = ThreadPoolBudget.leaseFrom(this.getExecutor(), this, this._selectors.length);
for(int i = 0; i < this._selectors.length; ++i) {
ManagedSelector selector = this.newSelector(i);
this._selectors[i] = selector;
this.addBean(selector);
}
super.doStart();
}
- 选择ManagedSelector容器管理的Selector,提交Accept至_updates(此时attachment为null),如果选择器_selecting为ture则选择当前选择器并唤醒选择器Selector的select阻塞
//ManagedSelector提交监听的Accept对象至队列ArrayDeque
public void submit(ManagedSelector.SelectorUpdate update) {
...
Selector selector = null;
synchronized(this) {
this._updates.offer(update);
//processUpdates处理之后将_selecting置为true开始唤醒selector的select阻塞
if (this._selecting) {
selector = this._selector;
this._selecting = false;
}
}
if (selector != null) {
...
//唤醒selector select阻塞
selector.wakeup();
}
}
//容器初始化时启动bean
protected void doStart() throws Exception {
super.doStart();
//调用选择管理器newSelector方法创建打开选择器Selector
_selector = _selectorManager.newSelector();
// The producer used by the strategies will never
// be idle (either produces a task or blocks).
// The normal strategy obtains the produced task, schedules
// a new thread to produce more, runs the task and then exits.
//策略:EatWhatYouKill,通常为从不idle空闲,要么生产task,要么阻塞
_selectorManager.execute(_strategy::produce);
// Set started only if we really are started
Start start = new Start();
submit(start);
start._started.await();
}
- ServerConnectorManager容器管理启动时,初始化创建ManagedSelector,ManagedSelector构造器中调用_selectorManager打开Selector,容器管理的doStart中执行EatWhatYouKill策略的produce生产方法,即SelectorProducer
选择器生产者SelectorProducer
EatWhatYouKill不断的调用SelectorProducer执行生产方法返回的任务
processSelected
- 获取已存在的SelectionKey(channel注册至selector上时返回的SelectionKey,attachment为SocketChannelEndPoint)
- 如果SelectionKey.attachment对象是ManagedSelector.Selectable类型返回task
- 回调attachment的onSelected方法,即SocketChannelEndPoint.onSelected,调用父类的ChannelEndPoint.onSelected
- 根据SelectionKey的操作类型返回读(_runFillable)、写(_runCompleteWrite)或读写(_runCompleteWriteFillable)双工的入任务
- 执行Callback回调任务,例如:ReadCallback回调succeeded,调用连接的onFillable方法,假定为HttpConnection,连接读取数据
- 如果是isConnectable则processConnect
- 如果是EndPoint则关闭EndPoint
processUpdates
将_updates队列中的SelectorUpdate数据封装的channel注册至指定的selector,并将当前channel与SelectionKey、Selector封装为相应的EndPoint对象(例如:SocketChannelEndPoint),将EndPoint绑定至SelectionKey的attachment
- 处理updates队列
- 将updates队列转移至updateable队列
- 遍历_updateable调用Start的update方法,设置ManagedSelector.this.started为true
- Start._started执行countdown
- 如果_updates不为空则唤醒selector(同Accept类型消息处理逻辑)
- 处理updates队列(Accept类型消息)
- 调用Accept的update方法,将Accept绑定的channel注册至当前选择的Selector,返回的SelectionKey的attachment设置为后面创建的EndPoint
- 调用_selectorManager执行当前Accept,即提交任务至线程池
- 创建EndPoint:选择管理器_selectorManager将当前服务器channel及SelectionKey封装为相应的EndPoint(例如:Selectable、isConnectable、EndPoint类型)绑定至SelectionKey
- 创建Connection:例如:HttpConnection
- 打开SocketChannelEndPoint.onOpen
- 将endPoint添加至连接器的_endPoints
- 打开连接器onOpened
- 将ReadCallback注册至endPoint的_fillInterest
- 遍历监听器_acceptListeners回调onAccept方法
- 清空_updateable
- 如果_updates为空则设置_selecting标识为true,表示正在选择,并唤醒selector,即通知selector的select方法阻塞消费
updateKeys
- 如果SelectionKey的attachment是Selectable类型,则更新SelectionKey
select
- 执行生产者的select选择方法,执行Selector的select方法
- 阻塞在selector的select方法中等待数据到达
- 如果存在数据则将selectedKeys绑定至_keys
- 将_keys列表的迭代器赋值至_cursor游标等待processSelected消费游标中的数据
- 如果selector已关闭返回false不再继续消费
HttpConnection连接
- org.eclipse.jetty.server.HttpConnection#onFillable读取数据
- org.eclipse.jetty.server.HttpConnection#fillRequestBuffer填充request的堆外内存buffer
- org.eclipse.jetty.server.HttpConnection#parseRequestBuffer解析requestBuffer,解析器:HttpParser
- org.eclipse.jetty.server.HttpChannel#HttpChannel通道构建时,同时创建org.eclipse.jetty.server.Request,org.eclipse.jetty.server.Response
- org.eclipse.jetty.server.HttpChannelOverHttp->org.eclipse.jetty.server.HttpChannel#handle通道事件channel event,如果不能handle事件继续读取,如果可以则执行handle
- HttpChannelOverHttp处理事件:handle(),调用父类HttpChannel.handle
- 根据Action类型处理事件,例如:org.eclipse.jetty.server.HttpChannelState.Action#DISPATCH事件转发请求,org.eclipse.jetty.server.HttpChannelState.Action#READ_PRODUCE事件读取Channel数据至org.eclipse.jetty.server.Request等
- 遍历监听器列表回调监听器accept方法入参为Request对象
- org.eclipse.jetty.server.HttpChannel#getServer获取Server,org.eclipse.jetty.server.Server#handle处理当前通道,假定为异步请求事件
- org.eclipse.jetty.server.Server#handleAsync处理HttpChannel通道
- org.eclipse.jetty.server.Server#handleAsync解析event(org.eclipse.jetty.server.Request#getHttpChannelState-》org.eclipse.jetty.server.HttpChannelState#getAsyncContextEvent)获取HttpServletRequest、HttpServletResponse、HttpChannel通道的Request、target资源路径,调用org.eclipse.jetty.server.handler.HandlerWrapper#handle,回调handler:org.eclipse.jetty.server.Handler#handle。同步org.eclipse.jetty.server.Server#handle入参是jetty封装的Request/Response,异步org.eclipse.jetty.server.Server#handleAsync入参是实现了Servlet HttpServletRequest/HttpServletResponse
- org.eclipse.jetty.server.AsyncContextEvent#AsyncContextEvent(在请求异步起动时创建org.eclipse.jetty.server.Request#startAsync()),javax.servlet.AsyncEvent#getSuppliedRequest获取的请求就是org.eclipse.jetty.server.Request或者启动AsyncContextEvent时传入的ServletRequest请求
public void handleAsync(HttpChannel channel) throws IOException, ServletException
{
final HttpChannelState state = channel.getRequest().getHttpChannelState();
final AsyncContextEvent event = state.getAsyncContextEvent();
final Request baseRequest=channel.getRequest();
final String path=event.getPath();
if (path!=null)
{
// this is a dispatch with a path
ServletContext context=event.getServletContext();
String query=baseRequest.getQueryString(); baseRequest.setURIPathQuery(URIUtil.addEncodedPaths(context==null?null:URIUtil.encodePath(context.getContextPath()), path));
HttpURI uri = baseRequest.getHttpURI();
baseRequest.setPathInfo(uri.getDecodedPath());
if (uri.getQuery()!=null)
baseRequest.mergeQueryParameters(query,uri.getQuery(), true); //we have to assume dispatch path and query are UTF8
}
final String target=baseRequest.getPathInfo();
final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest();
final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
...
handle(target, baseRequest, request, response);
...
}
handle处理
- handle链例如:JettyEmbeddedWebAppContext->ServletContextHandler->SessionHandler->SecurityHandler->ServletHandler
- 调用handle链,第一个handle是前面讲到的:JettyEmbeddedWebAppContext,Server容器启动时回调handle的WebAppContext.doStart方法
- 调用ServletContextHandler.doStart方法->父类ContextHandler调用startContext方法-》父类org.eclipse.jetty.server.handler.AbstractHandlerContainer#getChildHandlerByClass获取子节点(ScopedHandler类型)
- org.eclipse.jetty.servlet.ServletHandler由ContainerLifecycle管理启动doStart,doStart中执行ServletHandle初始化方法
- ServletHandler.doStart执行
- 更新org.eclipse.jetty.servlet.FilterMapping:org.eclipse.jetty.servlet.ServletHandler#updateNameMappings,更新filterNameMapping、servletNameMapping
- 更新updateMappings,如果contextHandler不为空并且已经启动,则执行initialize初始化方法
- 执行org.eclipse.jetty.servlet.FilterHolder.start方法
- . 执行FilterHolder.initialize方法
- 遍历org.eclipse.jetty.servlet.ServletHolder列表->执行ServletHolder.start方法->执行doStart方法
- 执行org.eclipse.jetty.servlet.ServletHolder.initailize方法
- 如果_initOnStartup则执行initServlet方法,即执行servlet的init方法
- 获取Holder beans,遍历执行start、initialize方法
- 调用handle链,第一个handle是前面讲到的:JettyEmbeddedWebAppContext,其他的handle节点,例如:ScopedHandler句柄处理,handle方法调用doHandle方法-》org.eclipse.jetty.server.handler.ContextHandler#doHandle方法-》调用org.eclipse.jetty.servlet.ServletHandler#doHandle方法
- 执行org.eclipse.jetty.servlet.ServletHolder#prepare方法:如果处于running状态并且servlet为null且servlet class不为null,则初始化servlet:org.eclipse.jetty.servlet.ServletHolder#initServlet-》javax.servlet.Servlet#init
- 如果过滤器链不为空调用过滤链,执行javax.servlet.FilterChain#doFilter方法
- 否则调用org.eclipse.jetty.servlet.ServletHolder#handle方法:执行servlet的javax.servlet.Servlet#service方法
- springboot与jetty对接:org.springframework.boot.context.embedded.jetty.JettyEmbeddedWebAppContext,上下文在springboot获取内嵌Servlet容器时注册至Server(也是handle链中的一个节点);JettyEmbeddedServletContainerFactory.getEmbeddedServletContainer
Servlet注册完成等待处理业务请求
- DispatcherServlet,派发请求:dispatch()
- 预处理句柄回调:org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle-》org.springframework.web.servlet.HandlerInterceptor#preHandle
- 执行句柄处理业务逻辑
总结
- 整体可以看到jetty容器为我们做了很多事情,端口的监听已经服务的线程池,网络io处理。而servlet则是我们的应用控制器,处理请求和响应(图片摘自Head First Servlet and JSP)
Servlet
servlet(server applet)是web应用的一套接口标准,用于web应用的数据交换
- 容器将http协议请求数据转换为ServletRequest请求数据
- servlet转发请求至业务逻辑处理数据
- 响应ServletResponse
- 容器将ServletResponse转换为http协议响应数据
- 响应客户端
状态机
后面再补充图片
/*
* The state of the request processing lifecycle.
* <pre>
* BLOCKING <----> COMPLETING ---> COMPLETED
* ^ | ^ ^
* / | \ |
* | | DISPATCH |
* | | ^ ^ |
* | v / | |
* | ASYNC -------> COMPLETE
* | | | ^
* | v | |
* | EXPIRE | |
* \ | / |
* \ v / |
* EXPIRING ----------+
* </pre>
*/
private enum RequestState
/*
* The state of the HttpChannel,used to control the overall lifecycle.
* <pre>
* IDLE <-----> HANDLING ----> WAITING
* | ^ /
* | \ /
* v \ v
* UPGRADED WOKEN
* </pre>
*/
public enum State
链路简述
- client -> send ServletRequest -> encode -> http data
- decode -> ServletRequest -> init -> servlet do filter chain -> servlet handle process -> servlet service process -> servlet do filter chain -> ServletResponse -> encode -> http data
- client decode -> ServletResponse -> client response