事实上在第三章,就已经有了连接器的样子了,只是那仅仅是一个学习工具,在这一章我们会開始分析tomcat4里面的默认连接器。

连接器

Tomcat连接器必须满足下面几个要求



1 实现org.apache.cataline.Connector接口

2 负责创建实现了org.apache.cataline.Request接口的request对象

3 负责创建实现了org.apache.cataline.Response接口的response对象

这里默认的连接器的原理非常easy,就是等待http请求,创建request与response对象,然后剩下任务交给Container接口的invoke方法处理。(这里是命令模式,详细问题咱们后面谈)

public void invoke(org.apache.cataline.Request request,org.apache.cataline.Response response);

http1.1的新特性

1持久连接

2块编码

3状态码100

这三点在本章的连接器中也有体现,但本人认为这部分不是这篇博客要说明的重点,因而只是多的作解释。

tomcat总的结构框图

事实上这部分内容是在书的前言部分,本来在这一系列读书笔记一開始的时候,我就得给朋友们聊聊这部分,但当时确实没怎么看懂,就仅仅能拖到如今了。

一个servlet容器(Container)能够相应若干个连接器(Connector)。

连接器主要干的事就是接收http请求,产生request与response,然后将处理过程交给容器;

容器干的事就是调用对应的servlet对象(准确的说是调用servlet的service方法,就用传过来的request与response作參数)

Connector接口

类图例如以下

注意,与第三章不同,HttpConnector与HttpProcessor是一对多的关系,为什么?用多线程,有效率,一个HttpProcessor仅仅能同一时候处理一个http请求。这也是与第三章不同的地方。

接口情况例如以下



依据上文,大家也应该能猜出来,我在图中标示的几个方法也就是接口里面最重要的。

HttpConnector类

它既实现了上面说的Connector接口,也实现了Lifecycle接口(这个接口,我们后面再细谈)

与第三章不同,我们这里的HttpConnector类多了三个功能

1创建server套接字

在连接器初始化的时候,会调用HttpConnector类的open方法来填充成员变量serverSocket。

open内部是通过先产生一个DefaultServerSocketFactory,在从工厂里依照port号及acceptCount(本connector能同一时候支持的http请求)来创建ServerSocket对象(事实上这里我也不明确为什么不直接new出那个socket 仅仅是为了使用者 生产者分离吗)

2维护httpprocess实例

我们刚才说了一个connector关联着多个httpprocessor对象。这些对象是以Stack的数据结构存储的(先进后出)

 private Stack processors = new Stack();

  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);
}
}
}
}

上面产生一个新processor的方法中有两个參数,maxProcessor是connector支持的最大prcessor量,还有一个是眼下的process量。上面的逻辑意义不解释。两个參数都是能够通过set方法设置的。

3提供http请求服务

与前面几章的类似,基本的工作都在run方法内部

public void run() {
// Loop until we receive a shutdown command
while (!stopped) {
// Accept the next incoming connection from the server socket
Socket socket = null;
try {
socket = serverSocket.accept();
....

熟悉的accept方法

然后就是

    HttpProcessor processor = createProcessor(); //分线程
if (processor == null) {
try { socket.close();
} catch (IOException e) {
;
}
continue; //话说这个continue是干什么的?
}
processor.assign(socket); //主线程

须要注意的是,上面的代码我做了凝视,有两个线程了,因此processor.assign仅仅会在主线程中调用一下,不须要等待解析的结果,详细的解析在还有一个分线程里了,这样做的结果就是能够同一时候处理多个http请求。

仅仅有createProcessor为什么就产生了新的线程,由于

 private HttpProcessor newProcessor() {

        //        if (debug >= 2)
// log("newProcessor: Creating new processor");
HttpProcessor processor = new HttpProcessor(this, curProcessors++);
if (processor instanceof Lifecycle) {
try {
((Lifecycle) processor).start();
} catch (LifecycleException e) {
log("newProcessor", e);
return (null);
}
}
created.addElement(processor);
return (processor); }

答案就在  ((Lifecycle) processor).start();

HttpProcessor类

既然上面都说了creatprcessor方法会启动一个新的线程,并且也出现了异步的assign方法,那么咱们就看看HttpProcessor类的assign与run方法。



 

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); //将自己又一次push进stack里
} 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"); }

在run里面又有一个await方法,看看

   

  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);
}

里面有个available,似乎是关键,再看看

    /**
* Is there a new socket available?
*/
private boolean available = false;

我就说一句,最開始available是false,我们creatProcessor时,调用了run方法,又调用了await方法,由于available为false,while循环起作用了,调用object的wait()这个分线程就停止在这了。

 processor.assign(socket);          //主线程

 看这个代码,再看assign(),available为false,跳过循环,将available改为true,(堵塞自己)然后通知全部进程。

 run里的await方法由于available为true得以执行,接着就是 process(socket);

Request对象

默认连接器中的request对象是org.apache.cataline.Request接口的实例,uml类图例如以下

Response对象

uml类图例如以下

处理请求

这部分确实比較麻烦,能够理解为总体就是一个大循环,当httpprocessor实例终止,或者链接断开停止循环。

下来就是对request对象与response对象的初始化工作

然后是parseConnection(),parseRequest(),parseHeaders()这三个方法分别解析连接,请求与请求头。

当中解析请求与第三章的内容差点儿相同,对请求头的解析使用了字符数组,避免了代价高昂的字符串操作。

完毕解析后,process方法会把request与response对象作为參数传递给servlet容器的invoke方法

connector.getContainer().invoke(request, response);

简单的Container应用程序

如今再说说Container,我们使用的实现类是SimpleContainer(已经移除了StaticResourceProcessor类,因此不能再訪问静态资源)

我们仅仅看看他的invoke方法

public void invoke(Request request, Response response)
throws IOException, ServletException { String servletName = ( (HttpServletRequest) request).getRequestURI();
servletName = servletName.substring(servletName.lastIndexOf("/") + 1);
URLClassLoader loader = null;
try {
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(WEB_ROOT);
String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
}
Class myClass = null;
try {
myClass = loader.loadClass(servletName);
}
catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println(e.toString());
} Servlet servlet = null; try {
servlet = (Servlet) myClass.newInstance();
servlet.service((HttpServletRequest) request, (HttpServletResponse) response);
}
catch (Exception e) {
System.out.println(e.toString());
}
catch (Throwable e) {
System.out.println(e.toString());
}
}

确实,假设你看了书的前三章,就会发现这里确实没有什么可说的,非常easy。

Bootstrap

public final class Bootstrap {
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
SimpleContainer container = new SimpleContainer();
connector.setContainer(container);
try {
connector.initialize();
connector.start(); //注意这里还仅仅是单纯的函数调用,眼下跟线程还没关系,还没run呢 // make the application wait until we press any key.
System.in.read();
}
catch (Exception e) {
e.printStackTrace();
}
}
}

初始化部分包括了一下lifecircle的问题,不用太在意,以后我们会说。

下一节我们说说tomcat里面的命令模式

博客中图片均来自how tomcat works一书

how tomcat works 读书笔记四 tomcat的默认连接器的更多相关文章

  1. how tomcat works 读书笔记(二)----------一个简单的servlet容器

    app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/arti ...

  2. How tomcat works 读书笔记十四 服务器组件和服务组件

    之前的项目还是有些问题的,例如 1 只能有一个连接器,只能处理http请求,无法添加另外一个连接器用来处理https. 2 对容器的关闭只能是粗暴的关闭Bootstrap. 服务器组件 org.apa ...

  3. How Tomcat Works读书笔记三-------连接器

    几个概念 HttpServlet,Servlet Servlet是一个接口,定义了一种网络服务,我们所有的servlet都要实现它(或它的子类) HttpServlet是一个抽象类,它针对的就是htt ...

  4. How tomcat works 读书笔记十五 Digester库 上

    Digester库 在前面的几个章节里,我们对tomcat里各个组件的配置完全是使用写硬编码的形式完成的. 如 Context context = new StandardContext(); Loa ...

  5. how tomcat works 读书笔记 十一 StandWrapper 上

    方法调用序列 下图展示了方法调用的协作图:  这个是前面第五章里,我画的图:  我们再回顾一下自从连接器里  connector.getContainer().invoke(request, resp ...

  6. how tomcat works 读书笔记(一)----------一个简单的webserver

    http协议 若是两个人能正常的说话交流,那么他们间必然有一套统一的语言规则<在网络上server与client能交流也依赖与一套规则,它就是我们说的http规则(超文本传输协议Hypertex ...

  7. how tomcat works 读书笔记(一)----------一个简单的web服务器

    http协议 若是两个人能正常的说话交流,那么他们间必定有一套统一的语言规则<在网络上服务器与客户端能交流也依赖与一套规则,它就是我们说的http规则(超文本传输协议Hypertext tran ...

  8. how tomcat works读书笔记 七 日志记录器

    大家可以松一口气了,这个组件比较简单,这一节和前面几节想比,也简单的多. Logger接口 Tomcat中的日志记录器都必须实现org.apache.catalina.Logger接口. packag ...

  9. How Tomcat Works 读书笔记 八 载入器 上

    Java的类载入器 详细资料见 http://blog.csdn.net/dlf123321/article/details/39957175 http://blog.csdn.net/dlf1233 ...

随机推荐

  1. shell 1变量注意点

    定义变量时,变量名不加美元符号($),如: variableName="value" 注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样. 删除变量 使用 un ...

  2. PHPStorm——配置修改

    字体修改: FiraCode字体:https://github.com/tonsky/FiraCode 1.双击安装字体 2. 关闭错别字检测

  3. Java: 实现顺序表和单链表的快速排序

    快速排序 快速排序原理 快速排序(Quick Sort)的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可对这两部分记录继续进行排序,以达到 ...

  4. hduoj 1077 Catching Fish 求单位圆最多覆盖点个数

    Catching Fish Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)To ...

  5. golang入门-- 一个2D的图形库学习

    此库叫gg,源码在github. 1.获取源码并安装到本地: 首先要安装git (传送门)  :   https://git-scm.com/download/ 然后就可以通过  go get 命令从 ...

  6. 转-Python optionParser模块的使用方法

    Python  有两个内建的模块用于处理命令行参数: 一个是  getopt,<Deep in python>一书中也有提到,只能简单处理 命令行参数: 另一个是  optparse,它功 ...

  7. 最完整的合并相交集合的Java代码(查并集)

    这个是自己写的算法,如果有大牛,麻烦帮我并行化.初学者则可以学到不少东西. 产生测试用例 import java.io.*; import java.util.Random; public class ...

  8. eclipse下切换svn用户

    在eclipse中经常用到用svn进行代码版本控制,为了提交或更新代码的时候不反复地提示我们输入用户名和密码,于是我们就习惯把访问SVN的用户名密码自动保存起来.以便下次自动使用,不要再次手工输入,但 ...

  9. [LeetCode#116]Fraction to Recurring Decimal

    Problem: Given two integers representing the numerator and denominator of a fraction, return the fra ...

  10. 【转】VMware 11安装Mac OS X 10.10 及安装Mac Vmware Tools.

    原文网址:http://www.cnblogs.com/Anand/p/4483727.html 先上一张效果图兴奋一下,博主穷屌丝一个,只能通过虚拟黑苹果体验下高富帅的生活,感觉超爽的,废话不多说的 ...