跟着springboot的内嵌jetty走进servlet

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/u010597819/article/details/90745546

springboot启动刷新上下文也是走的AbstractApplicationContext细节不再多说,我们直接进入正题,在onRefresh这个环节是springboot接入内嵌web容器的环节。在创建上下文时系统会判断当前是否是web环境,如果是则创建AnnotationConfigEmbeddedWebApplicationContext默认的web上下文。该上下文中的onRefresh环节进行创建内嵌的容器

@Override
protected void onRefresh() {
	super.onRefresh();
	try {
		createEmbeddedServletContainer();
	}
	catch (Throwable ex) {
		throw new ApplicationContextException("Unable to start embedded container",
				ex);
	}
}

创建内嵌的Servlet容器

  1. 获取内嵌的容器工厂EmbeddedServletContainerFactory,假定为jetty环境即JettyEmbeddedServletContainerFactory
  2. 使用工厂方法获取内嵌的servlet容器,入参为_ServletContextInitializer上下文初始化对象列表_

获取spring-web自身的ServletContextInitializer列表

org.springframework.boot.web.servlet.ServletContextInitializer

  1. 创建ServletContextInitializerBeans
  2. 根据类型ServletContextInitializer从工厂中获取对象列表遍历
  3. 如果是ServletRegistrationBean类型以Servlet类型为key添加至initializers
  4. 如果是FilterRegistrationBean类型,获取对象的Filter属性并以Filter类型添加至initializers
  5. 如果是DelegatingFilterProxyRegistrationBean类型同Filter相同,source需要通过代理的getTargetBeanName获取
  6. 如果是ServletListenerRegistrationBean类型,获取对象的listener属性并以EventListener类型添加至initializers
  7. 其他按照ServletContextInitializer类型添加至initializers
  8. addAdaptableBeans:根据类型(Servlet、Filter、EventListener)从工厂中获取bean,将bean封装为对应的RegistrationBean并添加至initializers(后面会遍历列表注册bean至ServletContext上下文)
  9. 排序initializers

springboot自动注册Servlet至ServletContext

回调spring-web自身ServletContextInitializer.onStartup

遍历ServletContextInitializer回调onStartup方法入参为ServletContext,与Servlet对接

  1. 创建ServletContextInitializerBeans:根据类型(ServletContextInitializer)从工厂中获取ServletContextInitializer列表
  2. 遍历ServletContextInitializerBeans集合,调用结合元素的onStartup方法,即sorted列表,常见的列表(ServletContextInitializer的ServletRegistrationBean实现类型):characterEncodingFilter、hiddenHttpMethodFilter、httpPutFormContentFilter、springSecurityFilterChain、dispatcherServlet、requestContextListener、SessionRepositoryFilter(共享session的Filter)
@Override
public Iterator<ServletContextInitializer> iterator() {
    //sortedList排序后的ServletRegistrationBean列表
	return this.sortedList.iterator();
}
  1. springmvc的dispatchServlet由DispatcherServletAutoConfiguration配置自动注册,其余由应用本身配置注册或其他自动配置注册
  2. 均为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内嵌容器工厂获取容器

  1. 获取容器JettyEmbeddedServletContainerFactory.getEmbeddedServletContainer
  2. 创建应用上下文JettyEmbeddedWebAppContext(ServletContextHandler类型),在后面的handle链调用中回调。Server调用handle链的根节点(即第一个handle)
  3. 父类ServletContextHandler执行relinkHandlers,doSetHandler将handler关联至当前handler的_handler属性,并更新注册至当前容器的_beans中,下面均相同逻辑不赘述
  4. 创建SessionHandler并关联至_sessionHandler,doSetHandler
  5. 创建SecurityHandler并关联至_securityHandler,doSetHandler
  6. 创建GzipHandler并关联至_gzipHandler,doSetHandler
  7. 创建ServletHandler并关联至_servletHandler,doSetHandler
  8. 获取端口号port默认为0
  9. 根据地址与端口创建SocketAddress实例InetSocketAddress
  10. 如果Server对象存在ThreadPool类型入参的构造器则使用jetty9的工厂方法创建Server(org.eclipse.jetty.server.Server);否则使用jetty8(8使用set方法注入线程池);入参为工厂线程池属性。如果连接池为空,构造器中会指定QueuedThreadPool为默认的线程池,最大200线程,最小线程数为8,空闲超时时间60000毫秒(1分钟),线程池名称为qtp前缀+hashcode,线程名称为qtp前缀+线程id
  11. 根据InetSocketAddress与Server创建Connector,jetty8使用org.eclipse.jetty.server.nio.SelectChannelConnector,jetty9使用org.eclipse.jetty.server.ServerConnector
  12. Server绑定connector
  13. 配置WebAppContext上下文configureWebAppContext
  14. web上下文设置contextPath,如果有则设置,默认为根"/"
  15. 如果指定则添加默认servlet:addDefaultServlet
  16. 如果需要注册JspServlet则添加,并添加JasperInitializer bean
  17. 添加字符集
  18. 设置ServletContextInitializerConfiguration入参为Initializer列表
  19. 配置session:configureSession;上下文获取SessionHandler句柄,如果存在超时则设置超时时间,默认为-1;如果指定persistSession需要持久化session则创建默认缓存DefaultSessionCache,为缓存设置文件存储FileSessionDataStore实例。将session缓存绑定至SessionHandler句柄
  20. 为Server设置handler句柄
  21. 如果启用了压缩,使用GzipHandler包装JettyEmbeddedWebAppContext(原始handler)
  22. 如果header包含text,使用ServerHeaderHandler包装handler
  23. _handler更新后更新bean:updateBean,添加至当前Server的_beans中在Server容器start启动时同时启动_beans列表中的管理bean
  24. 如果启用ssl,配置ssl并使用ssl的Connector覆盖原Server的Connector
  25. 获取JettyServerCustomizer列表,为server绑定自定义配置
  26. 如果启用useForwardHeaders,为server绑定ForwardHeadersCustomizer自定义配置
  27. 创建JettyEmbeddedServletContainer实例,入参为Server。如果端口大于0则设置自动启动容器为true。

创建容器JettyEmbeddedServletContainer

  1. 初始化
  2. 启动server:org.eclipse.jetty.server.Server
  3. 调用父类启动方法AbstractLifeCycle.start
  4. 调用子类Server重写的doStart方法
  5. 配置ErrorHandler
  6. 调用父类doStart,遍历ContainerLifecycleLifecycle的_beans,如果bean是managed类型并且没启动调用start方法启动,如果是auto类型,running状态则移除该bean,否则置为managed类型并启动bean
  7. 遍历Server的connectors列表,启动connector,假定为jetty9的ServerConnector
for (Connector connector : _connectors)
{
    try
    {
        connector.start();
    }
    catch(Throwable e)
    {
        mex.add(e);
    }
}

服务器连接器ServerConnector

创建ServerConnector

  1. 根据工厂acceptors数量、selector数量创建连接器
  2. 调用父类构造器AbstractConnector
  3. 将server添加至ContainerLifecycle中的beans,类型为UNMANAGED
  4. 将_executor添加至ContainerLifecycle中的beans,类型为:如果已经running则为UNMANAGED,否则为AUTO类型,如果不是Lifecycle类型则类型为POJO
  5. _scheduler注册至beans同_executor逻辑
  6. _byteBufferPool(默认为ArrayByteBufferPool类型)注册至beans同_executor逻辑
  7. 如果acceptors处理数量指定数量小于0,取1与(4与核心数量除以8之间的最小值)两者中的最大值
  8. 根据acceptor数量创建线程数组_acceptors
  9. 创建新的Selector管理器ServerConnectorManager,根据executor、scheduler、selectors数量
  10. 添加_manager至beans,类型为Managed
  11. 为Connector设置host、port

启动ServerConnector

  1. 调用父类start方法回调子类doStart方法
  2. AbstractNetworkConnector父类回调子类open方法,调用父类doStart方法
  3. 如果_acceptChannel为空创建,打开ServerSocketChannel通道绑定地址端口,设置_acceptChannel阻塞为true
  4. 添加_acceptChannel至beans,类型为managed
  5. 继续调用父类AbstractConnector doStart方法
  6. 继续调用父类ContainerLifecycle doStart方法,即调用新注册进来bean的start方法,如果存在auto类型并且running状态且不是unmanaged类型,是InheritedListener类型则移除bean,否则置为unmanaged类型。否则调用start方法
  7. 根据_acceptors长度创建CountDownLatch监听_stopping
  8. 根据_acceptors长度创建Acceptor,并将Acceptor注册至bean(POJO类型),获取线程池执行Acceptor

执行Acceptor

  1. 如果不是处于accepting并且是running状态,_setAccepting等待通知
  2. 回调ServerConnector连接器accept方法
@Override
public void accept(int acceptorID) throws IOException
{
    ServerSocketChannel serverChannel = _acceptChannel;
    if (serverChannel != null && serverChannel.isOpen())
    {
        SocketChannel channel = serverChannel.accept();
        accepted(channel);
    }
}
  1. accept打开channel并设置为非阻塞类型
  2. 设置socket的SocketOptions.TCP_NODELAY属性打开
  3. 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();
}
  1. 选择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();
}
  1. ServerConnectorManager容器管理启动时,初始化创建ManagedSelector,ManagedSelector构造器中调用_selectorManager打开Selector,容器管理的doStart中执行EatWhatYouKill策略的produce生产方法,即SelectorProducer

选择器生产者SelectorProducer

EatWhatYouKill不断的调用SelectorProducer执行生产方法返回的任务

processSelected

  1. 获取已存在的SelectionKey(channel注册至selector上时返回的SelectionKey,attachment为SocketChannelEndPoint)
  2. 如果SelectionKey.attachment对象是ManagedSelector.Selectable类型返回task
  3. 回调attachment的onSelected方法,即SocketChannelEndPoint.onSelected,调用父类的ChannelEndPoint.onSelected
  4. 根据SelectionKey的操作类型返回读(_runFillable)、写(_runCompleteWrite)或读写(_runCompleteWriteFillable)双工的入任务
  5. 执行Callback回调任务,例如:ReadCallback回调succeeded,调用连接的onFillable方法,假定为HttpConnection,连接读取数据
  6. 如果是isConnectable则processConnect
  7. 如果是EndPoint则关闭EndPoint

processUpdates

将_updates队列中的SelectorUpdate数据封装的channel注册至指定的selector,并将当前channel与SelectionKey、Selector封装为相应的EndPoint对象(例如:SocketChannelEndPoint),将EndPoint绑定至SelectionKey的attachment

  1. 处理updates队列
  2. 将updates队列转移至updateable队列
  3. 遍历_updateable调用Start的update方法,设置ManagedSelector.this.started为true
  4. Start._started执行countdown
  5. 如果_updates不为空则唤醒selector(同Accept类型消息处理逻辑)
  6. 处理updates队列(Accept类型消息)
  7. 调用Accept的update方法,将Accept绑定的channel注册至当前选择的Selector,返回的SelectionKey的attachment设置为后面创建的EndPoint
  8. 调用_selectorManager执行当前Accept,即提交任务至线程池
  9. 创建EndPoint:选择管理器_selectorManager将当前服务器channel及SelectionKey封装为相应的EndPoint(例如:Selectable、isConnectable、EndPoint类型)绑定至SelectionKey
  10. 创建Connection:例如:HttpConnection
  11. 打开SocketChannelEndPoint.onOpen
  12. 将endPoint添加至连接器的_endPoints
  13. 打开连接器onOpened
  14. 将ReadCallback注册至endPoint的_fillInterest
  15. 遍历监听器_acceptListeners回调onAccept方法
  16. 清空_updateable
  17. 如果_updates为空则设置_selecting标识为true,表示正在选择,并唤醒selector,即通知selector的select方法阻塞消费

updateKeys

  1. 如果SelectionKey的attachment是Selectable类型,则更新SelectionKey

select

  1. 执行生产者的select选择方法,执行Selector的select方法
  2. 阻塞在selector的select方法中等待数据到达
  3. 如果存在数据则将selectedKeys绑定至_keys
  4. 将_keys列表的迭代器赋值至_cursor游标等待processSelected消费游标中的数据
  5. 如果selector已关闭返回false不再继续消费

HttpConnection连接

  1. onFillable读取数据
  2. fillRequestBuffer填充request的堆外内存buffer
  3. parseRequestBuffer解析requestBuffer,解析器:HttpParser
  4. handle通道事件channel event,如果不能handle事件继续读取,如果可以则执行handle
  5. HttpChannelOverHttp处理事件:handle(),调用父类HttpChannel.handle
  6. HttpChannelState获取Http通道状态Action:HttpChannelState.Action
  7. 根据Action类型处理事件,例如:Dispatch事件,将事件封装为Request请求
  8. 遍历监听器列表回调监听器accept方法入参为Request对象
  9. get获取Server处理(handle)当前通道,假定为异步请求事件
  10. Server.handleAsync处理HttpChannel通道
  11. 解析事件获取HttpServletRequest、HttpServletResponse、HttpChannel通道的Request、target资源路径,调用handle
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处理

  1. handle链例如:JettyEmbeddedWebAppContext->ServletContextHandler->SessionHandler->SecurityHandler->ServletHandler
  2. 调用handle链,第一个handle是前面讲到的:JettyEmbeddedWebAppContext,Server容器启动时回调handle的WebAppContext.doStart方法
  3. 调用ServletContextHandler.doStart方法,ContextHandler调用startContext方法,获取getChildHandlerByClass子节点(ScopedHandler类型),
  4. ServletHandle由ContainerLifecycle管理启动doStart,doStart中执行ServletHandle初始化方法
  5. ServletHandler.doStart执行
  6. 更新NameMapping:updateNameMappings,更新filterNameMapping、servletNameMapping
  7. 更新updateMappings,如果contextHandler不为空并且已经启动,则执行initialize初始化方法
  8. 执行FilterHolder.start方法
  9. 执行FilterHolder.initialize方法
  10. 遍历ServletHolder列表
  11. 执行ServletHolder.start方法
  12. 执行doStart方法
  13. 执行ServletHolder.initailize方法
  14. 如果_initOnStartup则执行initServlet方法,即执行servlet的init方法
  15. 获取Holder beans,遍历执行start、initialize方法
  16. 调用handle链,第一个handle是前面讲到的:JettyEmbeddedWebAppContext,其他的handle节点,例如:ScopedHandler句柄处理,handle方法调用doHandle方法
  17. 调用ContextHandler.doHandle方法
  18. 调用ServletHandler.doHandle方法
  19. 执行ServletHandler.prepare方法:如果处于running状态并且_unavailable不等于0或者不是_initOnStartup则执行servlet的init方法
  20. 如果过滤器链不为空调用过滤链,执行filter.doFilter方法,否则调用handle方法:执行servlet的service方法
  21. springboot与jetty对接:org.springframework.boot.context.embedded.jetty.JettyEmbeddedWebAppContext,上下文在springboot获取内嵌Servlet容器时注册至Server(也是handle链中的一个节点);JettyEmbeddedServletContainerFactory.getEmbeddedServletContainer

总结

  1. 整体可以看到jetty容器为我们做了很多事情,端口的监听已经服务的线程池,网络io处理。而servlet则是我们的应用控制器,处理请求和响应(图片摘自Head First Servlet and JSP)
    图片摘自Head First Servlet and JSP

猜你喜欢

转载自blog.csdn.net/u010597819/article/details/90745546