首页 > 代码库 > how tomcat works 读书笔记四 tomcat的默认连接器
how tomcat works 读书笔记四 tomcat的默认连接器
事实上在第三章,就已经有了连接器的样子了,只是那仅仅是一个学习工具,在这一章我们会開始分析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类
它既实现了上面说的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的默认连接器
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。