转载: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


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

Tomcat处理HTTP请求源码分析(下)的更多相关文章

  1. Tomcat处理HTTP请求源码分析(上)

    Tomcat处理HTTP请求源码分析(上) 作者 张华 发布于 2011年12月8日 | 8 讨论 分享到: 微博 微信 Facebook Twitter 有道云笔记 邮件分享 稍后阅读 我的阅读清单 ...

  2. Tomcat处理HTTP请求源码分析(上)(转)

    转载自:http://www.infoq.com/cn/articles/zh-tomcat-http-request-1 很多开源应用服务器都是集成tomcat作为web container的,而且 ...

  3. 知识小罐头07(tomcat8请求源码分析 下)

    感觉最近想偷懒了,哎,强迫自己也要写点东西,偷懒可是会上瘾的,嘿嘿!一有写博客的想法要赶紧行动起来,养成良好的习惯. ok,继续上一篇所说的一些东西,上一篇说到Connector包装了那两个对象,最后 ...

  4. # Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析#

    Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析 Volley源码一共40多个类和接口.除去一些工具类的实现,核心代码只有20多个类.所以相对来说分析起来没有那么吃力.但是要想分析透 ...

  5. 知识小罐头06(tomcat8请求源码分析 中)

    更正上一篇一个小错误,Connector中首先是将socket请求过来的信息封装成一个普通的Request对象(上一篇我写成HttpRequest对象,失误失误,根本就木有HttpRequest这样的 ...

  6. Okhttp同步请求源码分析

    进阶android,OKhttp源码分析——同步请求的源码分析 OKhttp是我们经常用到的框架,作为开发者们,我们不单单要学会灵活使用,还要知道他的源码是如何设计的. 今天我们来分析一下OKhttp ...

  7. 详解Tomcat系列(一)-从源码分析Tomcat的启动

    在整个Tomcat系列文章讲解之前, 我想说的是虽然整个Tomcat体系比较复杂, 但是Tomcat中的代码并不难读, 只要认真花点功夫, 一定能啃下来. 由于篇幅的原因, 很难把Tomcat所有的知 ...

  8. 【MyBatis】MyBatis Tomcat JNDI原理及源码分析

    一. Tomcat JNDI JNDI(java nameing and drectory interface),是一组在Java应用中访问命名和服务的API,所谓命名服务,即将对象和名称联系起来,使 ...

  9. 知识小罐头05(tomcat8请求源码分析 上)

    这一篇我们不看源码,就大概理一下Tomcat内部组成部分!前面花费了两篇博客的篇幅来说说了一般的maven web项目并部署到tomcat运行,其实都是为这篇做铺垫的! 其实我下载了tomcat7,t ...

随机推荐

  1. SQL 查询分析器操作(修改、添加、删除)表及字段等

    一.库操作1..创建数据库命令:create database <数据库名>例如:建立一个名为xhkdb的数据库mysql> create database xhkdb; 2.显示所 ...

  2. CentOS如何分区

    安装Linux CentOS时,需要在硬盘建立CentOS使用的分区,在大多情况下,至少需要为CentOS建立以下3个分区. (1)/boot分区(不是必须的):/boot分区用于引导系统,它包含了操 ...

  3. as 和is的区别

    is 1,检查对象的兼容性,并返回true或false 2,不会抛出异常 3,null永远返回false as 1,检查对象的兼容性,并返回 true或false 2,不会抛出异常 3,null将抛出 ...

  4. SQLdiag Utility

    使用SQLdiag 会进行信息搜集类型 Windows 系统性能日志 Windows 系统日志 SQL Server 性能信息 SQL Server 阻塞信息 SQL Server 配置信息 如何使用 ...

  5. 全部快捷方式图标变成LNK文件怎么办

    windowsLNK文件打开方式恢复 相信有些用户曾试过错误地把LNK文件打开方式更改其文件导致系统所有快捷方式都失效vista与Windows7系统还普遍使用时候相信大家会有点惊慌失措要紧下面只要大 ...

  6. ios开发--旋转、移动、缩放手势实例代码

    代码如下: // 添加所有的手势 - (void) addGestureRecognizerToView:(UIView *)view { // 旋转手势 UIRotationGestureRecog ...

  7. 首页视频播放jquery

    <script type="text/javascript" src="[!--news.url--]skin/default/js/flowplayer-3.2. ...

  8. Mac下运行ASP.NET Core应用程序

    Mac下运行ASP.NET Core应用程序 在Mac下运行ASP.NET Core应用程序 通过参照.NET Core相关官方文档,在我的Mac电脑上用Visual Studio Code创建了我的 ...

  9. C++中const关键字详解

    1.什么是const? const意味着是常量类型,被const修饰的变量或对象是不能被修改和更新的,当然在某些情况下,我们可以偷梁换柱的改变它. 2.为什么要引入const? 最初的目的是为了取代预 ...

  10. Ubuntu修改屏幕默认亮度

    sudo gedit /etc/default/grub 把GRUB_CMDLINE_LINUX="" 改成GRUB_CMDLINE_LINUX="acpi_backli ...