tomcat网络模型
Tomcat启动流程
tomcat中基本所有的组件均通过LifecycleMBeanBase管理,在启动时,依次调用该LifecycleMBeanBase实现类所管理的其它组件的start方法,实际调用为startInternal()方法
其中tomcat中的容器实现的是LifecycleMBeanBase的子类ContainerBase,在ContainerBase.startInternal()方法中会依次启动 Cluster,Realm,子容器,Pipleline等
- selector.select(1000)。当 Poller 启动后因为 selector 中并没有已注册的 Channel,所以当执行到该方法时只能阻塞。所有的 Poller 共用一个 Selector,其实现类是 sun.nio.ch.EPollSelectorImpl
- events() 方法会将通过 addEvent() 方法添加到事件队列中的 Socket 注册到 EPollSelectorImpl,当 Socket 可读时,Poller 才对其进行处理
- createSocketProcessor() 方法将 Socket 封装到 SocketProcessor 中,SocketProcessor 实现了 Runnable 接口。worker 线程通过调用其 run() 方法来对 Socket 进行处理。
- execute(SocketProcessor) 方法将 SocketProcessor 提交到线程池,放入线程池的 workQueue 中。workQueue 是 BlockingQueue 的实例。到此 Poller 的任务就完成了。
- worker 线程被创建以后就执行 ThreadPoolExecutor 的 runWorker() 方法,试图从 workQueue 中取待处理任务,但是一开始 workQueue 是空的,所以 worker 线程会阻塞在 workQueue.take() 方法。
- 当新任务添加到 workQueue后,workQueue.take() 方法会返回一个 Runnable,通常是 SocketProcessor,然后 worker 线程调用 SocketProcessor 的 run() 方法对 Socket 进行处理。
- createProcessor() 会创建一个 Http11Processor, 它用来解析 Socket,将 Socket 中的内容封装到 Request 中。注意这个 Request 是临时使用的一个类,它的全类名是 org.apache.coyote.Request,
- postParseRequest() 方法封装一下 Request,并处理一下映射关系(从 URL 映射到相应的 Host、Context、Wrapper)。
tomcat中的概念
StandardService
MapperListener
StandardThreadExecutor
Connector
- 在Connector构造方法中,会判断是否设置protocol,如果未设置则默认为
org.apache.coyote.http11.Http11NioProtocol
- 在
Connector.startInternal()
中会调用this.protocolHandler.start()
,从而进入AbstractProtocol.start()
方法,此处虽然是start()方法,但是该类并没有实现Runnable方法 - 在该方法中会调用
endpoint.start()
,endpoint默认为NioEndpoint,然后进入AbstractEndpoint.start()方法,endpoint也不是runnalbe实现类
NioEndpoint
- 在NioEndpoint中会通过startInternal()方法启动Poller和Acceptor
- 通过
new Thread(this.poller, this.getName() + "-ClientPoller").start()
启动Poller - 通过
new Thread(this.acceptor, threadName)
启动acceptor
- 通过
- NioEndpoint.Poller
- Poller通过
SynchronizedQueue<NioEndpoint.PollerEvent> events
保存PollerEvent事件 - Poller是Runnable的实现类
- Poller使用nio来进行socket数据的读写
- 对Poller的注册,先进入Poller内部维护的一个事件队列上。
- Poller线程在执行过程中会去检查队列,将channel注册到selector上。
- 为了保证在多线程同时访问时数据的一致性,这个队列是一个SynchronizedQueue,使用synchronized来保证对队列中数据的一致性
- 注册的时候,每个channel会有KeyAttachment对象,用来进行channel上的多线程并发执行时的控制
- ServerSocketChannel建立连接是在Acceptor上。
- 在run方法中是一个while循环
- 判断NioEndPoint是否已关闭,关闭则退出循环
- 如果未close,则调用
this.events()
- 在
this.events()
中通过this.events.poll()
将PollerEvent
依次取出 - 执行事件的run()方法,值得注意的是此处执行的是run方法,而非thread.start()方法
- 并将该PollerEvent放入
NioEndpoint.this.eventCache
- 在
- 通过cas竞争锁
- 竞争成功,则通过
selector.selectNow()
返回事件- selectNow()不会阻塞,不管什么通道就绪都立刻返回
- 未竞争成功,则通过
selector.select(1000L)
最长阻塞1s,获取事件 - 如果获取到的事件大于0,则对selectedKeys进行循环处理
- 通过
this.processKey(sk, socketWrapper)
处理某个socket事件 - 如果是读事件,则调用
NioEndpoint.this.processSocket(socketWrapper, SocketEvent.OPEN_READ, true)
- 在该方法中通过
sc = this.createSocketProcessor(socketWrapper, event);
创建SocketProcessor,SocketProcessorBase为Runnable实现类 - 获取NioEndpoint中的executor并执行SocketProcessor
- 将SocketProcessor放入线程池executor的 workQueue 中。workQueue 是 BlockingQueue 的实例。到此 Poller 的任务就完成了。
- Poller通过
- PollerEvent中保存有NioChannel(即tcp连接socket)
- PollerEvent虽然是Runnable的实现类,但是在执行时,是直接在Poller中通过run方法执行的
- 在run方法中会向selector中注册socket 读事件
- Acceptor
- 用于创建新的连接
- 首先获取serverSocketAccept
this.endpoint.serverSocketAccept()
- 上述方法实际调用为
ServerSocketChannel.accept()
- 然后调用
this.endpoint.setSocketOptions(socket)
- 在setSocketOptions方法中实际通过
this.poller.register((NioChannel)channel, socketWrapper)
向poller.events中添加PollerEvent - 之后Poller线程通过run方法注册selector
- executor
- 用于处理poller中的事件,如读事件
- poller中的每个事件都会封装为SocketProcessor交给NioEndPoint.executor执行
- 故执行SocketProcessorBase.run()方法
- 进而执行SocketProcessor.doRun()方法
- 在doRun方法中执行
state = NioEndpoint.this.getHandler().process(this.socketWrapper, this.event);
- 在上述方法中通过
processor = this.recycledProcessors.pop();
获取Http11Processor - 通过
state = processor.process(wrapper, status);
处理socket - 然后调用
state = this.service(socketWrapper);
- 在service方法中
- 处理response及request等http相关的功能
- 调用
this.prepareRequest();
处理request this.getAdapter().service(this.request, this.response);
通过pipeline调用engine处理request请求
StandardEngine
StandardPipeline
NioEndPoint
总结
- NioEndPoint
- acceptor是一个线程执行的,作用是接收新的连接,并封装为PollerEvent后添加到Poller.events中
- Poller是一个线程执行的,作用是注册PollerEvent至selector及监听到selector事件后封装为SocketProcessor放到线程池中执行
- executor负责执行SocketProcessor,将请求转换为Request和Response并通过Pipeline调用engine