`
ss1
  • 浏览: 77673 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

《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();
分享到:
评论
1 楼 jiangtao_1987 2011-02-11  
HttpProcessor是放在堆栈中的,凡是“非空闲”的HttpProcessor都不在堆 栈中,它自己忙完了才会recycle回去,所以HttpConnector从堆栈中取出、并调用assign时的HttpProcessor肯定是“空 闲”的;--应该是这样的

让HttpConnector专门“wait”在那里,等于是整个tomcat都卡在那里,无法再接受新的Socket 这个不会的.书中有解释
“Why does the await method need to call notifyAll? Just in case another socket
arrives when the value of available is true. In this case, the "connector thread" will
stop inside the assign method's while loop until the nofifyAll call from the "processor
thread" is received. ”

我比较疑惑的是这句话
Why does the await method need to use a local variable (socket) and not return the
instance's socket variable? So that the instance's socket variable can be assigned to
the next incoming socket before the current socket gets processed completely.
不是当前socket处理完后才会空闲,此时这个processor不可能再接受别的socket?

相关推荐

Global site tag (gtag.js) - Google Analytics