- 浏览: 101080 次
- 性别:
- 来自: 成都
文章分类
最新评论
-
daichenqiu:
写的很好啊,赞!
Tomcat :一个简单的Servlet容器 -
Jnerd:
Handler初始化的应该注册read,否则select就会阻 ...
NIO Reactor模式(阅读NIO笔记) -
Jnerd:
运行了不正确呢
NIO Reactor模式(阅读NIO笔记)
Tomcat4默认连接器
tomcat连接器是一个独立的模块,可以插入到一个servlet容器。一个tomcat连接器必须符合以下要求:
- 必须实现org.apache.catalina.Connector接口
- 必须创建一个实现org.apache.catalina.Request接口的request对象
- 必须创建一个实现org.apache.catalina.Response接口的response对象
Container接口的invoke方法:
/** * Process the specified Request, and generate the corresponding Response, * according to the design of this particular Container. * * @param request Request to be processed * @param response Response to be produced * * @exception IOException if an input/output error occurred while * processing * @exception ServletException if a ServletException was thrown * while processing this request */ public void invoke(Request request, Response response) throws IOException, ServletException;
在invoke方法中,容器加载servlet类、调用service方法、管理session、记录错误信息日志等等。
使用对象池来降低复杂对象的创建开销。
*HTTP1.1新特性
- 持久连接
在HTTP1.0中,每对Request/Response都使用一个新的连接。
HTTP 1.1则支持Persistent Connection, 并且默认使用persistent connection.
connection: keep-alive
- 块编码
HTTP1.1支持chunked transfer,所以可以有Transfer-Encoding头部域,HTTP1.0则没有。
Transfer-Encoding: chunked
1D\r\n I'm as helpless as a kitten u 9\r\n p a tree. 0\r\n
- 状态100的使用
100 (Continue) 状态代码的使用,允许客户端在发request消息body之前先用request header试探一下server,看server要不要接收request body,再决定要不要发request body。
客户端在Request头部中包含Expect: 100-continue
Server看到之后呢如果回100 (Continue) 这个状态代码,客户端就继续发request body。
HTTP/1.1 100 Continue
HttpConnector类
(1)如何创建一个server socket?
(2)如何维护HttpProcessor池?
(3)如何处理Http请求?
org.apache.catalina.connetor.http.HttpConnector类,实现了org.apache.catalina.Connector、java.lang.Runnable和org.apache.catalina.LifeCycle接口。LifeCycle接口用来维护每一个实现了此接口的Catalina组件的生命周期。
- 创建一个server socket
- private ServerSocket open() throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException,
- UnrecoverableKeyException, KeyManagementException {
- // Acquire the server socket factory for this Connector
- ServerSocketFactory factory = getFactory();
- // If no address is specified, open a connection on all addresses
- if (address == null) {
- log(sm.getString("httpConnector.allAddresses"));
- try {
- return (factory.createSocket(port, acceptCount));
- } catch (BindException be) {
- throw new BindException(be.getMessage() + ":" + port);
- }
- }
- // Open a server socket on the specified address
- try {
- InetAddress is = InetAddress.getByName(address);
- log(sm.getString("httpConnector.anAddress", address));
- try {
- return (factory.createSocket(port, acceptCount, is));
- } catch (BindException be) {
- throw new BindException(be.getMessage() + ":" + address + ":" + port);
- }
- } catch (Exception e) {
- log(sm.getString("httpConnector.noAddress", address));
- try {
- return (factory.createSocket(port, acceptCount));
- } catch (BindException be) {
- throw new BindException(be.getMessage() + ":" + port);
- }
- }
- }
- 如何维护HttpProcessor池
首先采用栈来存储HttpProcessor实例,HttpProcessor池动态扩容,根据三个属性来设置:
curProcessors: 当前HttpProcessor实例的个数
minProcessors : int 初始化时,最小的HttpProcessor实例个数
maxProcessors:最大HttpProcessor实例个数,当小于0时,不做限制
初始化时创建最小HttpProcessor实例个数代码:
- // Create the specified minimum number of processors
- while (curProcessors < minProcessors) {
- if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
- break;
- HttpProcessor processor = newProcessor();
- recycle(processor);
- }
- 处理http请求
HttpConnector类在自己的run方法中有自己的主要逻辑,直到HttpConnetor停止之前都会一直等待接收http请求。对于每一个http请求,通过调用createProcessor方法获得一个HttpProcessor实例。然而,大多数时间,createProcessor方法并不会创建一个新的HttpProcessor对象,而是从一个HttpProcessor池中获取。如果在这个池中(实际采用的是堆栈来存储)有一个HttpProcessor实例可供使用,执行出栈操作。如果栈为空且仍然没有超过HttpProcessor实例的最大个数,则创建一个新的HttpProcessor实例,否则,createProcessor方法将返回null,此时socket执行关闭操作而不会响应到来的http请求。如果createProcessor方法没有返回null,客户端的socket传递给HttpProcessor的assign方法。
- private HttpProcessor createProcessor() {
- synchronized (processors) {
- if (processors.size() > 0) {
- return ((HttpProcessor) processors.pop());
- }
- if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {
- return (newProcessor());
- } else {
- if (maxProcessors < 0) {
- return (newProcessor());
- } else {
- return (null);
- }
- }
- }
- }
HttpProcessor类
在本章,我们最感兴趣的是HttpProcessor类如何使assign方法异步以便HttpConnetor实例可以同时为多个http请求服务。HttpProcessor类中另外一个重要的方法是私有方法process方法,它解析了http请求并且调用了容器的invoke方法。
第三章中,HttpConnetor在自己的线程中运行,然而它必须等待当前处理的http请求结束之后才可以处理下一个请求。
第三章的HttpConnetor类的run方法代码如下:
public void run() { ServerSocket serverSocket = null; int port = 8080; try { serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1")); } catch (IOException e) { e.printStackTrace(); System.exit(1); } while (!stopped) { // Accept the next incoming connection from the server socket 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); } }
HttpProcessor类的process方法在第三章中是同步方法。因此,它的run方法等待直到process方法处理结束才接收下一个请求。在本章,默认的连接器的HttpProcessor类实现了Runnable接口,因此每一个HttpProcessor实例都运行在自己的线程中,我们称为“processor线程”
Lifecycle接口的start和stop方法
/** * Prepare for the beginning of active use of the public methods of this * component. This method should be called before any of the public * methods of this component are utilized. It should also send a * LifecycleEvent of type START_EVENT to any registered listeners. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ public void start() throws LifecycleException; /** * Gracefully terminate the active use of the public methods of this * component. This method should be the last one called on a given * instance of this component. It should also send a LifecycleEvent * of type STOP_EVENT to any registered listeners. * * @exception LifecycleException if this component detects a fatal error * that needs to be reported */ public void stop() throws LifecycleException;
HttpProcessor实现了Lifecycle接口,因此HttpProcessor类的start方法代码如下:
/** * 判断HttpProcessor组件是否启动 */ private boolean started = false;
/** * Start the background thread we will use for request processing. * * @exception LifecycleException if a fatal startup error occurs */ public void start() throws LifecycleException { if (started) throw new LifecycleException (sm.getString("httpProcessor.alreadyStarted")); lifecycle.fireLifecycleEvent(START_EVENT, null); started = true; threadStart(); }
/** * 开启后台处理线程 */ private void threadStart() { log(sm.getString("httpProcessor.starting")); thread = new Thread(this, threadName); thread.setDaemon(true); thread.start(); if (debug >= 1) log(" Background thread has been started"); }
HttpProcessor的run方法的while循环执行流程:获得一个socket,处理它,调用connector的recycle(回收)方法把当前的HttpProcessor实例压回栈中。注意到while循环中停止到await方法处,await方法掌握着“processor thread”的控制流,直到它从HttpConnetor获得到一个新的socket对象。换句话说,直到HttpConnetor类调用HttpProcessor实例的assign方法。
HttpProcessor processor = createProcessor(); processor.assign(socket);
HttpProcessor的run方法代码如下:
/* * The background thread that listens for incoming TCP/IP connections and * hands them off to an appropriate processor. */ public void run() { // Process requests until we receive a shutdown signal while (!stopped) { // Wait for the next socket to be assigned Socket socket = await(); if (socket == null) continue; // Process the request from this socket try { process(socket); } catch (Throwable t) { log("process.invoke", t); } // Finish up this request connector.recycle(this); } // Tell threadStop() we have shut ourselves down successfully synchronized (threadSync) { threadSync.notifyAll(); } }
将调用完的HttpProcessor实例压回栈中的代码实现:
void recycle(HttpProcessor processor) { processors.push(processor); }
然而,await方法和assign方法运行在不同的线程中,assign方法是在HttpConnetor的run方法中被调用的,即“connector线程”。
那么assign方法是如何告诉await方法它被调用了呢?
利用一个布尔型变量available和java.lang.Object类的wait和notifyAll方法。
注:Object的wait方法导致当前线程等待直到其他线程对这个对象调用notify或者nitifyAll方法。
connetor线程调用的assign方法:
/** * Process an incoming TCP/IP connection on the specified socket. Any * exception that occurs during processing must be logged and swallowed. * <b>NOTE</b>: This method is called from our Connector's thread. We * must assign it to our own thread so that multiple simultaneous * requests can be handled. * * @param socket TCP socket to process */ synchronized void assign(Socket socket) { // Wait for the Processor to get the previous Socket while (available) { try { wait(); } catch (InterruptedException e) { } } // Store the newly available Socket and notify our thread this.socket = socket; available = true; notifyAll(); if ((debug >= 1) && (socket != null)) log(" An incoming request is being assigned"); }
HttpProcessor线程调用的await方法:
/** * Await a newly assigned Socket from our Connector, or <code>null</code> * if we are supposed to shut down. */ private synchronized Socket await() { // Wait for the Connector to provide a new Socket while (!available) { try { wait(); } catch (InterruptedException e) { } } // Notify the Connector that we have received this Socket Socket socket = this.socket; available = false; notifyAll(); if ((debug >= 1) && (socket != null)) log(" The incoming request has been awaited"); return (socket); }
初始时,当“processor thread”刚启动时,available为false,即还没有可用的socket。因此线程在while循环中等待,直到其他线程调用notify或者notifyAll方法。也就是说,调用wait方法导致“processor thread”暂停直到“connector thread”对这个HttpProcessor实例调用notifyAll方法。当一个新的socket被分配,“connector thread”调用HttpProcessor的assign方法。avilable置为true,唤醒“processor thread”。
为什么await方法需要使用一个本地变量(socket)且不返回这个socket变量的实例?
因为HttpProcessor实例的socket变量在当前socket处理完之前还可以分配给下一个到来的socket。
为什么await方法需要调用notifyAll?
为了当另外一个socket到达的时候此时available为true,这时候,“connector thread”将会停止里面的assign方法直到收到“processor thread”的notifyAll方法。
发表评论
-
web.xml配置
2012-11-08 18:22 10381.< context-param> conte ... -
Tomcat与web开发技术详解读书笔记(2)过滤器(7)
2011-11-26 14:05 0补充:Servlet监听器 类似与Swing界 ... -
Tomcat与web开发技术详解读书笔记(2)过滤器(6)
2011-11-26 14:03 0web开发中常用的字符集编码过滤器 < ... -
Tomcat与web开发技术详解读书笔记(2)过滤器(4)
2011-11-26 14:02 0public class LoginFilter imp ... -
Tomcat与web开发技术详解读书笔记(2)过滤器(3)
2011-11-26 14:01 0doFilter(ServletRequest req, ... -
Tomcat与web开发技术详解读书笔记(2)过滤器(2)
2011-11-26 13:59 0init(FilterConfig config):过滤 ... -
Tomcat与web开发技术详解读书笔记(2)过滤器
2012-09-18 17:31 25851.简介 过滤器能够在Servlet、JSP或HTML等w ... -
mybatis入门
2011-11-21 20:13 01.什么是MyBatis MyBatis是支持普通SQ ... -
How tomcat works 第四章学习笔记(6)
2011-11-20 21:40 0利用一个布尔型变量available和java.lang.Ob ... -
How tomcat works 第四章学习笔记(5)
2011-11-20 21:19 0HttpProcessor实现了Lifecycle接口,因此H ... -
How tomcat works 第四章学习笔记(4)
2011-11-20 20:42 0HttpProcessor类 在本章,我们最感兴趣的是 ... -
How tomcat works 第四章学习笔记(2)
2011-11-20 20:01 51. HttpConnector类 (1)如何创建一个ser ... -
Tomcat与web开发技术详解读书笔记(2)
2011-11-20 17:01 51. 浏览器端与用户的动态交互 脚本语言:web服务器直接把 ... -
Tomcat与web开发技术详解读书笔记(1)Http协议简介
2011-11-20 16:54 9231.HTTP协议简介 超文本传输协议,是关于如何 ... -
How tomcat works 第四章学习笔记(3)
2011-11-16 20:10 9HttpConnector类 如何维护HttpProces ... -
Servlet监听器
2011-09-04 18:03 7068Servlet监听器用于监听一些重要事件的发生,监听器对象可以 ... -
工作相关的术语
2011-09-04 01:27 9901.SOA (Service-Oriented Archite ... -
MySql慢查询日志
2011-09-04 00:26 1104转自:http://www.cnblogs.com/wenan ... -
Quartz—调度框架
2011-09-02 23:45 0Quartz Scheduler 2.0 对以前的API ... -
【转】svn文件清除批处理工具
2011-03-24 00:27 1978来源:http://darkmasky.iteye.com/b ...
相关推荐
How Tomcat Works中文版
How Tomcat Works【英文PDF+中文HTML+源码】 How Tomcat Works 主要是讲解Tomcat如何运行的一些核心资料。
How Tomcat Works 中文版+例程源码; 源码在src目录下
HowTomcatWorks(书和源码)
tomcat的基本思想,学习完可以对理解spring的基本原理有大致了解,很值得学习
How Tomcat Works 全书共20章!
How Tomcat works(PDF),不可用于商业用途,如有版权问题,请联系删除!
How Tomcat Works》这本书的读书笔记,及主要内容感想。 作为一个世界范围广泛使用的强大框架,Tomcat必然有非常多的设计思想、设计模式,让我们学习。
how tomcat works中文版 + 英文版,深入解析了tomcat的实现机制
HowTomcatWorks 中文版+源码.rar HowTomcatWorks 中文版+源码.rar
how tomcat works 高清版 学习tomcat必备书籍 how tomcat works
How Tomcat Works Tomcat原理的书
How Tomcat Works 深入剖析Tomcat (英文版)
How Tomcat Works 中文版+例程源码,源码在src包下面, 祝大家学习愉快
How Tomcat Works,讲述Tomcat工作原理的英文教程。
tomcat工作原理深入详解——HowTomcatWorks中文版.pdf
how tomcat works( 深入剖析tomcat) 的随书源码 之前找了很久,后来从官网下载下来的
Welcome to How Tomcat Works. This book dissects Tomcat 4.1.12 and 5.0.18 and explains the internal workings of its free, open source, and most popular servlet container code-named Catalina. Tomcat is ...