• 模拟实现 Tomcat 的核心模块:NIO,HTTP,容器和集群


    如果你想看 Tomcat 源码但又无从入手,不妨从这个项目开始,代码量不多,但包含了 Tomcat 的核心处理流程,并且源码中有相当丰富的注释。相信通过此项目你能了解:

    • NIO 基本编程、HTTP 协议的本质、基本的单元测试
    • Tomcat 应用部署、自定义类加载器的实现、Servlet 的管理和加载运行以及静态资源的处理和缓存等
    • Maven 生成可执行 jar,生成 javadoc,使用 assembly 构建项目,使用 release 插件发布到 git等

    文末有源码地址。本文就 NIO 模型、HTTP 协议解析、Digester 工具以及Servlet 容器这些核心模块的设计和实现的难点以及重点进行简单介绍。

    1. NIO 服务器的实现

    由于非阻塞的特性,NIO 的编写相比于 BIO 很复杂,而原生 NIO 编程就更复杂了,关键是处理好通道和处理器的映射,以及各种状态的管理。此模块的名称是 rxtomcat-net,结构如下:

    rxtomcat-net

    主要实现了以下功能:

    • Acceptor 使用信号量对总连接数进行控制
    • Acceptor 和 Poller 使用队列协作完成新连接通道的注册,因为直接注册可能会造成死锁
    • Poller 既能通知非阻塞读写事件,也能通知模拟阻塞的读写事件
    • Poller 在通知 I/O 事件时,也会模拟 Tomcat 把通道就绪的事件从关注事件集合中移除
    • 利用两个用于读和写的 CountDownLatch 实现模拟阻塞
    • Poller 还对通道处理超时,通道超时或关闭时会移除它对应的 Processor,防止内存泄露
    • 因为是非阻塞,所以当处理中途发现读或写的数据不完整,要再次处理时,需要找到原先的处理器,Handler 内部就是使用 ConcurrentHashMap 保持通道和处理器的映射
    • NioChannel 是对 SocketChannel 的封装,主要包含两个 ByteBuffer 用于读和写以及两个模拟阻塞读和写使用的闭锁,以及提供实际的非阻塞和模拟阻塞读写功能
    • 为了适配不同协议的处理器定义了一个 Processor 接口

    EchoProcessor 是实现的一个回显处理器,它包含一个 main 方法,可直接运行进行测试。

    有一点需要注意,从通道读取字节到处理请求都是一个线程,只有在非阻塞读取不完整的请求头数据时,才有可能切换线程

    2. HTTP 协议的解码和编码

    完整的实现 HTTP 协议是很复杂的,这里的实现比较简单,模块的名称是 rxtomcat-http,结构如下:

    rxtomcat-http

    主要实现了以下功能:

    • 消息行(请求或响应)的解析和构造,解析时采用有限状态机的方法,这是非阻塞编程常用的手段
    • chunked 和 identity 消息体的解析和构造
    • 实现 keepAlive 长连接
    • 特殊 URL 解码,比如 param=%E5%88%9B+a
    • 为容器提供底层 Processor 的回调机制,ActionHook

    解析协议麻烦的地方在于处理 TCP 粘包拆包的问题,以及各种缓冲区的清空和重用。在实现时,缓存区大部分使用的是 ByteBuffer。

    3. 简单的 Servlet 容器

    实现一个简单的 Servlet 容器,模块的名称是 rxtomcat-container,结构如下:

    rxtomcat-container

    简单起见,只设计了 Context 和 Wrapper 两个容器,主要实现了以下功能:

    • Pipeline 和 Valve 的管道处理模型,以及容器 Lifecycle 生命周期的设计
    • DefaultServlet 静态资源的处理和缓存
    • 根据 web.xml 部署应用,提取 Servlet 和 Filter 及其配置的映射
    • 打破双亲委托的类加载器 Loader,实现从 WEB-INF/classes 和 WEB-INF/lib 加载类,以及 class 文件热加载的功能
    • 实现 Servlet 的三种 URL 路由规则,以及规范中的 Cookie, HttpSession, FilterChain, HttpServletRequest, HttpServletResponse
    • 实现 Session 以及它的管理器 Manager
    • 实现了 ServletInputStream 用于支持文件上传的处理

    这部分的实现稍微繁琐,也基本复现了 Tomcat 的处理流程,其中唯一有点绕的就是使用 Lifecycle 实现的观察模式,触发特定的生命周期事件,使用特定的类来配置和初始化 Context。

    4. 其他工具

    模块 rxtomcat-utils 主要是一些工具类:

    • 简单实现了 Digester XML 解析工具
    • 实现了一个字节数组功能类,主要有字节数组转整形,转十六进制字符串

    5. Maven 构建模块

    模块 rxtomcat-bootstrap 使用 maven-assembly-plugin 打包发布二进制版本,最终构建生成的项目运行目录结构是:

    目录结构

    6. 小结

    造轮子确实很费时间,但效果很好。平时写代码,知道原理是什么,但在编写时却无从下手,这就是代码写的少,模仿的少导致的。所以,如果时间充裕,不妨多造造轮子。

    • 本文模拟实现的 Tomcat 源码地址是:「github.com/dwosc/rxtomcat」
    • 使用的版本是 Tomcat 6.0.53,公众号「顿悟源码」后台回复关键字「Tomcat」可获取带有比较详细中文代码注释的,可直接导入 Eclipse 运行的 Tomcat 工程。

    读完一个完整的开源项目,实在太费时间了v_v,后续时间充足的话,计划继续实现集群、异步 Servlet 和 websocket 的代码,欢迎 star 关注

  • 相关阅读:
    OpenCV 2.4.11 VS2012 Configuration
    [LeetCode] 11. Container With Most Water 装最多水的容器
    Android rxjava2的disposable
    Android在应用设置里关闭权限,返回生命周期处理
    Android 使用greenDAO 3.2.2 操作外部数据库
    Android中intent相关,setFlag(xx);
    Android 关于Acitivity 的setFlag以及launchmode的总结
    GreenDao3.0新特性解析(配置、注解、加密)
    Android RecycleView实现混合Item布局
    Android各大手机系统打开权限管理页面
  • 原文地址:https://www.cnblogs.com/chuonye/p/10939562.html
Copyright © 2020-2023  润新知