Tomcat处理HTTP请求源码分析(下)

Tomcat处理HTTP请求源码分析(下)

作者 张华 发布于 2011年12月16日 

 

很多开源应用服务器都是集成tomcat作为web container的,而且对于tomcat的servlet container这部分代码很少改动。这样,这些应用服务器的性能基本上就取决于Tomcat处理HTTP请求的connector模块的性能。本文首先从应用层次分析了tomcat所有的connector种类及用法,接着从架构上分析了connector模块在整个tomcat中所处的位置,最后对connector做了详细的源代码分析。并且我们以Http11NioProtocol为例详细说明了tomcat是如何通过实现ProtocolHandler接口而构建connector的。

上篇地址为《Tomcat处理HTTP请求源码分析(上)》 ,本文是系列下篇。

4 如何实现Connector

由上面的介绍我们可以知道,实现Connector就是实现ProtocolHander接口的过程。

 

AjpAprProtocol、AjpProtocol、Http11AprProtocol、Http11Protocol、JkCoyoteHandler、MemoryProtocolHandler这些实现类的实现流程与Http11NioProtocol相同,下面我们以Http11NioProtocol为类重点说明tomcat中如何实现ProtocolHander接口的。

Http11NioProtocol实现了ProtocolHander接口,它将所有的操作委托给NioEndpoint类去做,如下图:

《Tomcat处理HTTP请求源码分析(下)》

相关厂商内容

《PHP7 – New engine for good old train》惠新宸

基于 Erlang/OTP 构建大规模实时系统

Lua 在 Nginx 中的应用

ArchSummit深圳大会:探索在线教育领域的最佳技术实践

云计算高可用架构设计与实践——阿里云技术专场

 

相关赞助商

《Tomcat处理HTTP请求源码分析(下)》

全球软件开发大会,4月23-25日,北京,敬请期待

 

NioEndpoint类中的init方法中首先以普通阻塞方式启动了SocketServer:

《Tomcat处理HTTP请求源码分析(下)》

NioEndpoint类的start方法是关键,如下:

《Tomcat处理HTTP请求源码分析(下)》

可以看出,在start方法中启动了两个线程和一个线程池:

  • Acceptor线程,该线程以普通阻塞方式接收客户端请求(socket.accep()),将客户Socket交由线程池是处理,线程池要将该Socket配置成非阻塞模式(socket.configureBlocking(false)),并且向Selector注册READ事件。该线程数目可配置,默认为1个。
  • Poller线程,由于Acceptor委托线程为客户端Socket注册了READ事件,当READ准备好时,就会进入Poller线程的循环,Poller线程也是委托线程池去做,线程池将NioChannel加入到ConcurrentLinkedQueue<NioChannel>队列中。该线程数目可配置,默认为1个。
  • 线程池,就是上面说的做Acceptor与Poller线程委托要做的事情。

4.1 Init接口实现方法中阻塞方式启动ServerSocketChannel

在Init接口实现方法中阻塞方式启动ServerSocketChannel。

《Tomcat处理HTTP请求源码分析(下)》

4.2 Start接口实现方法中启动所有线程

Start方法中启动了线程池,acceptor线程与Poller线程。其中acceptor与poller线程一般数目为1,当然,数目也可配置。

《Tomcat处理HTTP请求源码分析(下)》

可以看出,线程池有两种实现方式:

  • 普通queue + wait + notify方式,默认使用的方式,据说实际测试这种比下种效率高
  • JDK1.5自带的线程池方式

4.3 Acceptor线程接收客户请求、注册READ事件

在Acceptor线程中接收了客户请求,同时委托线程池注册READ事件。

    1. 在Acceptior线程中接收了客户请求(serverSock.accept())

《Tomcat处理HTTP请求源码分析(下)》

    1. 委托线程池处理

《Tomcat处理HTTP请求源码分析(下)》

    1. 在线程池的Worker线程的run方法中有这么几句:

《Tomcat处理HTTP请求源码分析(下)》

在setSocketOptions方法中,首先将socket配置成非阻塞模式:

《Tomcat处理HTTP请求源码分析(下)》

在setSocketOptions方法中,最后调用getPoller0().register(channel);一句为SocketChannel注册READ事件,register方法代码如下(注意:这是Poller线程的方法):

《Tomcat处理HTTP请求源码分析(下)》

其中attachment的结构如下,它可以看做是一个共享的数据结构:

《Tomcat处理HTTP请求源码分析(下)》

4.4 Poller线程读请求、生成响应数据、注册WRITE事件

    1. 在上面说的setSocketOptions方法中调用Poller线程的register方法注册读事件之后,当READ准备就绪之后,就开始读了。下面代码位于Poller线程的run方法之中:

《Tomcat处理HTTP请求源码分析(下)》

    1. 可以看到,可读之后调用processSocket方法,该方法将读处理操作委拖给线程池处理(注意此时加入到线程池的是NioChannel,不是SocketChannel):

《Tomcat处理HTTP请求源码分析(下)》

    1. 线程池的Worker线程中的run方法中的部分代码如下(请注意handler.process(socket)这一句):

《Tomcat处理HTTP请求源码分析(下)》

注意:

      • 调用了hanler.process(socket)来生成响应数据)
      • 数据生成完之后,注册WRITE事件的,代码如下:

《Tomcat处理HTTP请求源码分析(下)》

4.5 Handle接口实现类通过Adpater调用Servlet容器生成响应数据

NioEndpoint类中的Handler接口定义如下:

《Tomcat处理HTTP请求源码分析(下)》

其中process方法通过Adapter来调用Servlet Container生成返回结果。Adapter接口定义如下:

《Tomcat处理HTTP请求源码分析(下)》

4.6 小结

实现一个tomcat连接器Connector就是实现ProtocolHander接口的过程。Connector用来接收Socket Client端的请求,通过内置的线程池去调用Servlet Container生成响应结果,并将响应结果同步或异步的返回给Socket Client。在第三方应用集成tomcat作为Web容器时,一般不会动Servlet Container端的代码,那么connector的性能将是整个Web容器性能的关键。

关于作者

张华,长期从事Java方面的开发工作,有搜索引擎、中间件应用服务器、互联网、云计算等领域的行业经验,目前正在从事基于Power的虚拟化技术研发。博客地址:http://blog.csdn.net/quqi99

感谢张凯峰对本文的审校。

    原文作者:java资料收集
    原文地址: http://www.cnblogs.com/01picker/p/4440071.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞