zoukankan      html  css  js  c++  java
  • Tomcat处理HTTP请求源码分析(下)

    转载:http://www.infoq.com/cn/articles/zh-tomcat-http-request-2

    很多开源应用服务器都是集成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类去做,如下图:

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

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

    可以看出,在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。

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

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

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

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

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

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

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

      1. 委托线程池处理

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

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

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

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

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

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

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

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

    注意:

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

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

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

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

    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实现 蓝桥杯VIP 基础练习 完美的代价
    Java实现 蓝桥杯VIP基础练习 矩形面积交
    Java实现 蓝桥杯VIP 基础练习 完美的代价
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    Java实现 蓝桥杯 蓝桥杯VIP 基础练习 数的读法
    核心思想:想清楚自己创业的目的(如果你没有自信提供一种更好的产品或服务,那就别做了,比如IM 电商 搜索)
    在Linux中如何利用backtrace信息解决问题
  • 原文地址:https://www.cnblogs.com/wenlj/p/4901124.html
Copyright © 2011-2022 走看看