《深入剖析tomcat》读书笔记准备写四篇,这篇是第二篇,分析默认连接器,对应书籍的第四章。第三篇分析容器,第四篇来个纵向总结,顺便回答第一篇开头提出的问题。
第四章 Tomcat默认连接器
1. Tomcat连接器
Tomcat连接器是一个独立模块,可以被插入到servlet容器中。连接器有很多种,coyote,mod_jk等等。Tomcat使用的连接器必须满足下面要求:
(1) 实现Connector接口;
(2) 创建request对象;
(3) 创建response对象;
2.连接器和servlet容器如何一起工作?
连接器首先和一个container关联,然后等待Http请求,创建request对象、response对象。然后调用container接口的invoke()方法,把request对象和response对象传递给servlet容器。servlet容器会载入相应的servlet类,调用其service()方法,管理session对象等等。
3.Http1.1新特性
Http1.1新特性引入了3个新特性:持久链接(keep-alive)、块编码、状态码100。
a.持久链接
持久链接定
HTTP持久连接是使用同一个TCP连接来发送和接收多个HTTP请求/应答,而不是为每一个新的请求/应答打开新的连接的方法。
为什么使用持久链接?
非持久连接每请求一个资源都要建立HTTP链接,开销太大。如果一个页面有10张图片,使用非持久链接至少总共建立11个TCP链接来完成这个请求1
持久链接为使用同一个链接来下载所有资源,节省资源。具体优势如下2:
1.较少的CPU和内存的使用(由于同时打开的连接的减少了);
2.允许请求和应答的HTTP管线化;
3.降低网络阻塞 (TCP连接减少了);
4.减少了后续请求的延迟(无需再进行握手);
5.报告错误无需关闭TCP连接;
b.块编码
块编码定义
输编码是超文本传输协议一种数据传输机制,允许HTTP由网页服务器发送给客户端应用( 通常是网页浏览器)的数据可以分成多个部分。
为什么使用块编码?
1. 对于动态生成的内容来说,在内容创建完之前是不可知的
2. 分块传输编码允许服务器在最后发送消息头字段
3. 分块编码有利于一边进行压缩一边发送数据,而不是先完成压缩过程以得知压缩后数据的大小
c.状态码100的使用
当客户端准备发送一个较长的请求体,而不确定服务器是否会接受的时候,可以在请求体之前发送:Expect: 100-continue。若服务器可以接受并处理该请求,可以发送相应:
HTTP/1.1 100continue。
为什么引入状态码100?
如果客户端发送一个较长的请求体最后却被服务器拒绝接受时,发生较大浪费。
总的说来,3个特性的引入,目的都是让web服务器更节省资源开销,更快地响应速度。
4.HttpConnector类
HttpConnector做的事情:
a. 创建服务器套接字
通过一个服务器套接字工厂获得服务器套接字。
b. 维护HttpProcessor实例
HttpProcessor的作用已经在第三章提过了,不再重复。HttpConnector通过维护一个HttpProcessor对象池来避免每次为新请求创建HttpProcessor对象。维持HttpProcessor对象池策略如下:a.对象池大小由minProcessor和maxProcessor来确定。如果请求的数目超过了HttpProcessor的实例,那么创建更多的HttpProcessor对象,但是保证会不超过maxProcessor大小。b. 对象池的数据结构是栈。
c. 提供http请求服务
从对象池栈中弹出一个HttpProcessor对象,将接收到的socket传给这个HttpProcessor对象,进行处理。这里HttpProcessor在独立的线程里面,所以connector会立即返回,再次接受请求。
5. HttpProcessor类
HttpProcessor对象和HttpConnector对象的协作方式是这章的精华。
直接贴代码
public class HttpConnector implements Connector, Runable{ public void run(){ while(!loop){ //...省略 ServerSocket serverSocket = new(...) Socket socket = setverSocket.accept(); HttpProcessor httpProcessor = createHttpProcessor(this); //从processor池中读取,如果无且小于max,则创建 if (httpProcessor==null) //processor池中无实例 socket.close(); httpProcessor.assign(socket); } } } public class HttpProcessor implements Runable{ public void run(){ while(!loop){ Socket socket = await(); if (socket == null){ continue; } //... process(socket); connector.recycle(this); synchronized (threadSync){ threadSync.notifyAll(); } } } private synchronized void assign(Socket socket){ while (available){ wait(); } //... this.socket = socket; available = true; notifyAll(); //唤醒await处理 } private synchronized Socket await(){ while (!available){ wait(); } Socket socket = this.scoket; available = false; notifyAll(); //告诉assign,处理完成,可以接收下一个 return socket; } }
HttpProcessor有2个方法assign(Socketsocket)和await()。理解这两个方法的协作实际上就理解了HttpProcessor对象和HttpConnector对象的协作方式。
5.1assign()和await()做的事情
assign(Socketsocket)由HttpConnector调用,HttpConnector通过
Socket socket = setverSocket.accept();
接受到一个socket,立马调用httpProcessor.assign(socket),将接收到的socket扔给httpProcessor处理。
await()的实现,什么都没有做,就在等待socket。await()运行在HttpProcessor的线程中。
5.2assign()和await()如何协作
HttpProcessor实现了runable接口,和HttpProcessor工作在不同的线程空间。通过修改available变量来协作。available变量表示有没有socket:有socket,available=true;没有socket,available=fasle。assign()和await()方法共享scoket和available
HttpProcessor线程启动后,执行await()方法。此时available=false,表示没有socket,那么wait();把CPU时间片让给其它线程。HttpConnector线程也在运行中,在没有socket链接的时间里,该线程一直阻塞在了Socket socket = serverSocket.accept();这个方法里面。在某个时刻,setverSocket.accept()接收到了socket,立即执行到调用了HttpProcessor的assign()方法。assign()方法中,available=false,直接做:
available= true; //修改available为 true,表示有socket
notifyAll(); //唤醒await处理
此时,cpu时间片切换到了HttpProcessor线程。又开始执行await()方法。
Socket socket = this.scoket; available = false; notifyAll(); //告诉assign,处理完成,可以接收下一个。此时,便可以执行process(socket);5.3 assign()和await()如何协作背后
assign()和await()分别被connector和processor调用。connector负责提供socket,processor在等待socket,其实就是
上生产者—消费者模型。通过available变量来表明是否有socket。不然,消费者processor的process()方法执行的时候,
发现都没有socket。生成者connector在分配socket的时候,发现有socket还没有处理完,绝不分配Socket。他们通过
修改avaible变量来通知对方是否有资源socket,进行通信。
1. 10.1.5 HTTP非持久连接和持久连接:http://book.51cto.com/art/201108/282330.htm
2. HTTP持久链接
http://zh.wikipedia.org/wiki/HTTP%E6%8C%81%E4%B9%85%E9%93%BE%E6%8E%A5