《How Tomcat Works》读书笔记(三):Tomcat default connector

Chapter 4: Tomcat default connector

何为default Connector?其实这里指的是tomcat最初设计时使用的Connector,尽管问题多多,现在已经被coyote所取代,但作为教学用例,default Connector仍然不失为一个优秀的组件,值得一学!

这一章的目的是系统的讲述tomcat的Connector,同时为介绍后面的容器作铺垫。

从这一章开始,内容越来越多也逐步深化,因此做笔记也只能摘录一部分,没有基础的人看了也许会觉得不连贯,那么推荐你先去看看原书或者它的翻译。

Http 1.1

default Connector支持Http 1.1标准,相比Http1.0,1.1最大的特色在于增加了对长连接的支持。举个例子,我们浏览网页的时候,一个html页面中,其实包含了很多资源, 如图像、视频、声音,等等。每个资源都有独立的URL地址,在过去1.0时,一次只能针对一个URL获取资源,因此往往打开一个页面的操作其实包含了很多 次的“客户端/服务器”之间的连接过程。反反复复的建立、断开连接是非常耗时的,而且也无疑增加了服务器的负担。在Http1.1中,通过长连接,可以一 次性获取多个资源,无疑是大大节约了网络资源。

客户端可以通过在请求中包含“connection: keep-alive”这一头字段来要求服务器提供长连接

Chunked Encoding(块编码)

有了长连接,客户端应该如何区分在一个连接中传输的多个资源呢?在过去的1.0中,由于每次只传输一个资源,因此不会有这个问题。能否使用 content-length字段?不行。因为服务器并不会等到一整个图像文件都准备好了才传给你,往往是一次传若干字节,这种做法相当于操作系统的“分 时”,有利于提高并发性和用户的使用感受。专家们最后终于想出一个办法,就是在每次传输时加上一些信息表示这次要传多少字节,例如:

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

通过“十六进制数字”+“\r\n”,代表一次传输的字节数,最后的“0\r\n”表示一次会话的结束。但书上并没有说一次会话就相当于传一个资源,因此到底如何区分多个资源,还不是很清楚,需要研究Http协议才能知道答案。

Connector接口

二话不说,上图:

image

tomcat里面最基本的两个接口应该就是Connector和container了,接口本身很简单很抽象,但衍生出来的东西却非常复杂。

从图中可以看出,Connector和container是一一对应的,Connector有setContainer方法,但container 没有类似的setConnector,说明只有Connector才知道container的存在,反之是不成立的。Connector是为了把 Request和Response传递给container;而container不必理会谁给他Request,只要能拿到Request、再把响应写 入Response就行了

此外,一个HttpConnector中包含了多个HttpProcessor,目的是为了支持多线程,可以同时处理多个Socket连接。

多线程处理Socket

old version

先来回顾之前的实现:

while (!stopped) {
       Socket socket = null;
       try {
         socket = serversocket.accept();
       }        catch (Exception e) {
         continue;
       }
       // Hand this socket off to an Httpprocessor
       HttpProcessor processor = new Httpprocessor(this);
       processor.process(socket);
     }

这种做法的后果是,整个处理流程完全是同步的,因为 processor.process 方法是同步的。这样只有一个请求完全处理完毕后,processor.process 才会返回,while循环才能继续,效率之低可想而知,根本应付不了几个并发请求。

new version

既然找到了问题所在,接下来就要提出解决方案。很自然的想法是:线程。

大致思路是这样的:通过一个堆栈,我们保存若干数量的HttpProcessor,这些HttpProcessor都实现了Runnable接口。 HttpConnector和HttpProcessor分别代表了生产者和消费者。一方面,HttpConnector获得一个个的Socket,放入 空闲的HttpProcessor中,HttpProcessor处理完毕后,通过recycle方法将自己“回收”。如果没有空闲的 HttpProcessor,则HttpConnector会将该Socket丢弃,直接关闭。

说到生产者和消费者这种同步模型,那么控制同步的“关键域”是什么呢?此处其实是一个布尔变量available,表示此时 HttpProcessor是否空闲。HttpConnector调用HttpProcessor的同步的assign方法,将Socket交给 HttpProcessor,如果available为假,那么assign方法调用完成直接返回,如果为真,则HttpConnector就wait在 那里直到可用为止。HttpProcessor本身则采取相反的策略。

any question?

仔细推敲一下,会觉得这里的算法有些奇怪。首先,HttpProcessor是放在堆栈中的,凡是“非空闲”的HttpProcessor都不在堆 栈中,它自己忙完了才会recycle回去,所以HttpConnector从堆栈中取出、并调用assign时的HttpProcessor肯定是“空 闲”的;其次,让HttpConnector专门“wait”在那里,等于是整个tomcat都卡在那里,无法再接受新的Socket,不合情理。希望能 有高人指点一下其中的原因,到底是bug,还是有更深层次的考虑。

Request & Response

Request的主要功能基本没有变,Facade依然存在,但是其继承关系比原来复杂了许多,书中也没有一一解释,让人云里雾里,或许后面的章节会有补充。

image

在HttpProcessor取得新的Socket后,依旧是通过process方法对Socket进行处理,流程上还是按照之前的做法,依次解析

  1. Connection:检查有没有使用代理
  2. Request:类似第三章
  3. Headers:类似第三章,但是用char[]取代String作为头字段的名称,例如:static final char[] AUTHORIZATION_NAME =  "authorization".toCharArray();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值