how tomcat works 读书笔记四 tomcat的默认连接器

时间:2022-09-28 17:43:02

事实上在第三章,就已经有了连接器的样子了,只是那仅仅是一个学习工具,在这一章我们会開始分析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总的结构框图

how tomcat works 读书笔记四  tomcat的默认连接器

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

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

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

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

Connector接口

类图例如以下

how tomcat works 读书笔记四  tomcat的默认连接器

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

接口情况例如以下

how tomcat works 读书笔记四  tomcat的默认连接器

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

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类图例如以下

how tomcat works 读书笔记四  tomcat的默认连接器

Response对象

uml类图例如以下

how tomcat works 读书笔记四  tomcat的默认连接器

处理请求

这部分确实比較麻烦,能够理解为总体就是一个大循环,当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一书