【读书笔记/源码】How Tomcat Works 笔记 - c1~c10

https://github.com/StevenGerrad/how-tomcat-work-lab

  • chapter1: sample-server
  • chapter2: sample-servlet-container
  • chapter3: sample-connector
  • chapter4: user-default-connector
  • chapter5: servlet-container
  • chapter6: lifecycle
  • chapter7: logger
  • chapter8: classloader
  • chapter9: session
  • chapter10: security

TODO

  • PrintWriter out = response.getWriter(); writer的内部实现

第一章 一个简单的Web服务器

HttpServer中,使用ServerSocket模拟服务器(只能解析静态资源文件)

第二章 一个简单的Servlet容器

ServletProcessor继承javax.servlet.Servlet接口,使用URLClassLoader加载参数指定servlet类

  • HttpServer实例化ServerSocket并监听8080端口
  • HttpServer接受请求后
    • 实例化RequestResponse
    • 并根据情况实例化ServerProcessorStaticResourceProcessor,将RequestResponse作为参数传给它们

第三章 连接器

Catalina中有两个主要的模块,连接器(connector)和容器(container)。连接器不需要知道servlet对象的类型,只负责提供HttpServletRequest实例和HttpServletResponse实例。
本章连接器解析HTTP请求头,使servlet实例获取到请求头、cookie和请求参数/值等信息,从而进行功能增强,可以运行更复杂一点的servlet类。

之后每章的应用程序都会按照模块划分。本章包含三个模块:连接器模块、启动模块和核心模块。

  • Bootstrap实例化并调用HttpConnectorHttpConnector继承Runnable,每次被调用会为自己创建新的线程并启动
  • HttpConnector实例化ServerSocket并监听8080端口
    • 接受请求后实例化并调用HttpProcessor
  • HttpProcessor实例化StringManager以及SocketInputStream等专职功能类,从而帮助组装同时实例化出来的HttpRequestHttpResponse
    • 并根据情况实例化ServerProcessorStaticResourceProcessor,将HttpRequestHttpResponse作为参数传给它们,其中
      • <font style="background-color:#8A8F8D;">SocketInputStream</font>用于解析Http请求头
        • HttpHeader辅助SocketInputStream进行解析
      • <font style="background-color:#8A8F8D;">StringManager</font>用于读取.properties配置文件(单例模式)
      • HttpRequestLine

<font style="background-color:#8A8F8D;">StringManager</font>等“灰色底”类几乎为Tomcat同名类副本)

上一章的HttpServer是在判断请求类型(Servlet/静态资源)前就对与请求进行了全部解析

  • 本章HttpConnector不进行请求解析
  • HttpProcessor在判断请求类型前只解析servlet用到的参数
    • request.getParameterXxx()等方法在具体的Servlet中才调用

对应下表:

具体任务 第二章 负责对象 第三章 负责对象
等待HTTP请求 HttpServer类 HttpConnector实例
创建Request和Response对象 HttpServer类 HttpProcessor实例

创建HttpRequest对象(HttpRequestFacade使用了外观设计模式)

在servlet/JSP编程中,参数名jessionid用于携带一个会话标识符。会话标识符通常是作为Cookie嵌入的。

第四章 Tomcat的默认连接器

本章的“默认连接器”是Tomcat4中的默认连接器。已经被Coyote取代,但仍是一个不错的学习工具。

先上源码

import com.lab.tomcat.core.SimpleContainer;
import org.apache.catalina.connector.http.HttpConnector;

HttpConnector connector = new HttpConnector();
SimpleContainer container = new SimpleContainer();
connector.setContainer(container);
try{
   
   
    connector.initialize();
    connector.start();

    // make the application wait until we press any key.
    System.in.read();
}
catch (Exception e){
   
   
    e.printStackTrace();
}

该模块除了Bootstrap,只自定义了SimpleContainer和Servlet,而HttpConnector和HttpProcessor都采用了apache.catalina包。编写SimpleContainer继承Container接口,实现public void invoke(Request request, Response response)方法从而创建并调用具体Servlet就好。

Tomcat4的默认连接器,相比于第三章简单连接器的优化处:使用了对象池避免频繁创建对象;在很多地方使用字符数组来代替字符串。该连接器实现了HTTP1.1的全部新特性,也可以为使用HTTP1.0和HTTP0.9协议的客户端提供服务。

4.1 HTTP 1.1 的新特性

4.1.1 持久连接

HTTP1.1前,无论浏览器何时连接到Web服务器,当服务器请求资源返回后,就会断开与浏览器的连接,但是网页上会包含一些自他资源如静态资源、applet等。如果页面和它引用的所有资源文件都使用不同链接进行下载的话,处理过程会很慢。——因而HTTP1.1引入持久连接

HTTP1.1默认使用持久连接,也可以显式使用,方法是请求头信息

connection: keep-alive

4.1.2 块编码

建立了持久连接后,服务器可以从多个资源发送字节流,而客户端也可以使用该连接发送多个请求。因而发送方必须在每个请求和响应中添加"content-length"头信息,这样接受方才知道如何解释这些字节信息。

但通常发送方并不知道要发送多少字节,如servlet容器可能在接收到一些字节后就开始发送响应信息而非接收完所有信息。因而需要接收方能在不知道发送内容长度的情况下解析。

其实即使没有发出多个请求或多个响应,服务器或客户端也不需要知道有多少字节要发送。在HTTP1.0中服务器可以不写"content-length"头信息,发送完响应信息后,它就直接关闭连接。此时客户端一致读取内容知道读方法返回-1

HTTP1.1使用一个名为transfer-encoding的特殊请求头,来指明字节流会分块发送。对每个块,块的长度(十六进制表示)后面会有一个回车/换行符(CR/LF),然后是具体数据,一个事务以一个长度为0的块标记。

eg. 假设用两个块发送下面38个字节内容,其中i的一个块为29个字节,第二个块为9个字节:

I'm as helpess as a kitten up a tree.

实际应该发送如下内容

1D\r\n
I'm as helpess as a kitten u
9\r\n
p a tree.
0\r\n

其中0\r\n表明事务已经完成。

4.1.3 状态码100的使用

使用HTTP1.1的客户端可以在向服务器发送请求体前发送如下请求头并等待确认:

Expect: 100-continue

当客户端准备发送较长的请求体而不确定服务端是否会接收时,可以发送该头信息避免浪费。若服务器可以接收并处理该请求则发送响应头:

HTTP/1.1 100 Continue

4.2 Connector 接口

Tomcat连接器必须实现org.apache.catalina.Connector接口,接口中最重要的方法是getContainer()setContainercreateRequest()createResponse()

连接器与servlet容器是一对一的关系,箭头指向表示的关系是连接器知道要用哪个servlet容器。

com.lab.tomcat.Bootstrap#main

  • org.apache.catalina.connector.http.HttpConnector#initialize (Connector接口限定方法)
    • 从工厂中获取自己的持有的ServerSocket
  • org.apache.catalina.connector.http.HttpConnector#start(LifeCycle接口限定方法)
    • 为自己另创建并启动一个独立线程
      • 通过自己的#threadStart()方法与start()方法循环调用配合start布尔值属性实现。
    • 依据上下范围参数(minProcessors、maxProcessors)先创建minProcessor个HttpProcessor入栈(用栈方式管理的HttpProcessor对象池
      • org.apache.catalina.connector.http.HttpConnector#newProcessor
        • 创建HttpProccessor实例并启动
          • org.apache.catalina.connector.http.HttpProcessor#start

org.apache.catalina.connector.http.HttpConnector#run 提供HTTP请求服务

  • java.net.ServerSocket#accept
  • org.apache.catalina.connector.http.HttpConnector#createProcessor
    • 获取或创建一个HttpProcessor,不超过maxProcessors参数
  • org.apache.catalina.connector.http.HttpProcessor#assign(Socket)
    • 注意HttpProcessor#assign方法是HttpConnector#run的“连接器线程”调用的
    • 使用java.lang.Object#wait()方法阻塞监听this.available布尔属性值
    • 传入参数socket
    • 修改this.available属性值
    • java.lang.Object#notifyAll

第三章中的HttpProcessor中的processor()方法是同步的,因此在接受下一个HTTP请求前,run()方法会一致等待,直到processor()方法返回

or

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值