事实上在第三章,就已经有了连接器的样子了,只是那仅仅是一个学习工具,在这一章我们会開始分析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. GIF文件转换为头文件工具

    目的: GIF文件转为头文件 举例: 用UE打开GIF文件,如下图所示:图1 test.gif文件将上面文件内容转化为头文件,放到一个数组里面,内容如下:图2 test.h文件 思路: 从上面可知,将 ...

  2. Django设置TIME_ZONE和LANGUAGE_CODE为中国区域

    Django默认的timezone是 TIME_ZONE = 'America/Chicago' LANGUAGE_CODE = 'en-us' 设置为中国区域: TIME_ZONE = 'Asia/ ...

  3. Newtonsoft.Json工具类

    这个类用于序列化和反序列化类. 效果是当前最好的.微软都推荐使用.在建立MVC的里面已经引用了这个dll. 上面一篇文章要用到 SerializeHelper工具类 public class Seri ...

  4. JAVA NIO 结合多线程

    NIO 的选择器采用了多路复用(Multiplexing)技术,可在一个选择器上处理多个套接字, 通过获取读写通道来进行 IO 操作.由于网络带宽等原因,在通道的读.写操作中是容易出现等待的, 所以在 ...

  5. C#反射(一) 【转】

    在还不太熟悉反射的昨天,以为反射很神秘,在网上到处找答案.今天找了段代码敲了一下,茅塞顿开!其实反射也就那么简单的一回事!    反射是一种机制,通过这种机制我们可以知道一个未知类型的类型信息.比如, ...

  6. Hdu5510 Bazinga

    Description Ladies and gentlemen, please sit up straight. Don't tilt your head. I'm serious. For \(n ...

  7. The type java.lang.String cannot be resolved. It is indirectly referenced from required .class files

    最近在做J2ME开发项目,配置环境一切OK,但是打开项目时某些文件提示: The type java.lang.String cannot be resolved. It is indirectly ...

  8. 2016 年开发者应该掌握的十个 Postgres 技巧

    [编者按]作为一款开源的对象-关系数据库,Postgres 一直得到许多开发者喜爱.近日,Postgres 正式发布了9.5版本,该版本进行了大量的修复和功能改进.而本文将分享10个 Postgres ...

  9. ASP.NET中Cookie的使用

    学习web开发,使用Cookie是不可避免的,在这就浅 显的总结一下,供新手参阅.个人感觉Cookie的使用和ASP.NET中的Session非常像,只不过Cookie是保存在客户端,而 Sessio ...

  10. C#编程技术层次

    不谈具体领域(比如搜索,视频,网络等),单就编程语言这个垂直方向,大体上对它有一个如下的层次划分. 1. 基本运用C#语法,在各种工具和示例代码的支持下,完成一些基本程序任务 2. 熟练掌握面向对象与 ...