几个重要的概念
- Connector: jetty网络接口的封装,用于监听网络连接
- SelectorManager:底层selector封装,管理网络事件,主要是向底层selector注册感兴趣的网络事件,并从selector中轮询出准备好的事件
- EndPoint:socket的封装,用于底层网络的读写,一旦网络读写准备好,会调用相应的connection的handle方法
- Connection:请求的抽象,比如解析请求的http协议,并调用servlet 容器,依赖EndPoint
见图
几个重要的线程
1) Acceptor线程
- task:org.eclipse.jetty.server.AbstractConnector.Acceptor
- 数量设定:setAcceptors(Math.max(1,(Runtime.getRuntime().availableProcessors()+3)/4));见SelectChannelConnector构造函数
- 触发时机:见SelectChannelConnector(AbstractConnector).doStart()
- 执行线程:QueuedThreadPool中的线程
- 执行逻辑
- 轮询SelectChannelConnector.accept(int acceptorID)
- 以阻塞的方式获取连接请求
- 一旦获得连接,调用ConnectorSelectorManager.register(SocketChannel channel)
- 向SelectorManager$SelectSet 内部的_changes队列中添加该事件(有新的连接)
- 唤醒selector
2)Selector 线程
- task:匿名内部类(new Runnable(){......})
- 数量设定:在jetty.xml中配置
- 触发时机:见SelectChannelConnector$ConnectorSelectorManager(SelectorManager).doStart()
- 执行线程:QueuedThreadPool中的线程
- 执行逻辑
- 轮询SelectorManager$SelectSet.doSelect()
- 从_changes队列获取感兴趣的事件
- 如果是EndPoint类型(读写事件)
- 如果已在selector中注册过,则更新selection key, 否则向selector注册
- 如果是ChannelAndAttachment类型
- 如果关联的SocketChannel已连接,则向selector注册读事件
- 否则注册连接事件
- 如果是SocketChannel类型(连接事件)
- 则向selector注册读事件//key = channel.register(selector,SelectionKey.OP_READ,null);
- 实例化SelectChannelEndPoint //createEndPoint(channel,key);
- 调用SelectChannelEndPoint.schedule()
- 调用SelectChannelConnector$ConnectorSelectorManager.dispatch(Runnable task)将请求扔给QueuedThreadPool(存放在内部jobs队列)
- 如果是ChangeTask类型
- 直接执行
- 如果是Runnable
- 直接丢给QueuedThreadPool
- 运行selector.select获取准备好的SelectionKey,遍历SelectionKey
- 如果key无效则去更新
- 如果有事件发生则调用SelectChannelEndPoint.schedule()
- 调用selector.selectedKeys().clear()
3)Worker线程
- task:匿名内部类(new Runnable(){......})
- 数量设定:在jetty.xml中配置
- 触发时机:QueuedThreadPool.startThread
- 执行线程:QueuedThreadPool中的线程
- 执行逻辑:
- 轮询从jobs队列取job(通常是SelectChannelEndPoint的匿名内部类(new Runnable(){......}))
- 然后执行job.run(通常是SelectChannelEndPoint.handle())
- 调用SelectChannelConnector$SelectChannelHttpConnection(AsyncHttpConnection).handle()
- HttpParser.parseAvailable()(不断调用parseNext读取请求的内容)处理请求
- flush内容
- 如果过程中存在写阻塞,会调用SelectChannelEndPoint.scheduleWrite()或者直接调用updateKey()往_changes队列中添加写事件
线程关系见图
小结(请求处理流程)
- Acceptor线程负责监听连接,一但有连接过来,写入changes队列
- Selector轮询changes队列,将队列中的感兴趣事件往selector中注册,并从selector中查出准备好的网络事件,一旦有准备好的网络事件,通过调用endpoint.schedule()将task丢入线程池的jobs队列
- Work线程会从jobs队列取出任务,然后执行
- 上面的3组线程全来自QueuedThreadPool中的线程