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与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的默认连接器的更多相关文章
- how tomcat works 读书笔记(二)----------一个简单的servlet容器
app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/arti ...
- How tomcat works 读书笔记十四 服务器组件和服务组件
之前的项目还是有些问题的,例如 1 只能有一个连接器,只能处理http请求,无法添加另外一个连接器用来处理https. 2 对容器的关闭只能是粗暴的关闭Bootstrap. 服务器组件 org.apa ...
- How Tomcat Works读书笔记三-------连接器
几个概念 HttpServlet,Servlet Servlet是一个接口,定义了一种网络服务,我们所有的servlet都要实现它(或它的子类) HttpServlet是一个抽象类,它针对的就是htt ...
- How tomcat works 读书笔记十五 Digester库 上
Digester库 在前面的几个章节里,我们对tomcat里各个组件的配置完全是使用写硬编码的形式完成的. 如 Context context = new StandardContext(); Loa ...
- how tomcat works 读书笔记 十一 StandWrapper 上
方法调用序列 下图展示了方法调用的协作图: 这个是前面第五章里,我画的图: 我们再回顾一下自从连接器里 connector.getContainer().invoke(request, resp ...
- how tomcat works 读书笔记(一)----------一个简单的webserver
http协议 若是两个人能正常的说话交流,那么他们间必然有一套统一的语言规则<在网络上server与client能交流也依赖与一套规则,它就是我们说的http规则(超文本传输协议Hypertex ...
- how tomcat works 读书笔记(一)----------一个简单的web服务器
http协议 若是两个人能正常的说话交流,那么他们间必定有一套统一的语言规则<在网络上服务器与客户端能交流也依赖与一套规则,它就是我们说的http规则(超文本传输协议Hypertext tran ...
- how tomcat works读书笔记 七 日志记录器
大家可以松一口气了,这个组件比较简单,这一节和前面几节想比,也简单的多. Logger接口 Tomcat中的日志记录器都必须实现org.apache.catalina.Logger接口. packag ...
- How Tomcat Works 读书笔记 八 载入器 上
Java的类载入器 详细资料见 http://blog.csdn.net/dlf123321/article/details/39957175 http://blog.csdn.net/dlf1233 ...
随机推荐
- GIF文件转换为头文件工具
目的: GIF文件转为头文件 举例: 用UE打开GIF文件,如下图所示:图1 test.gif文件将上面文件内容转化为头文件,放到一个数组里面,内容如下:图2 test.h文件 思路: 从上面可知,将 ...
- Django设置TIME_ZONE和LANGUAGE_CODE为中国区域
Django默认的timezone是 TIME_ZONE = 'America/Chicago' LANGUAGE_CODE = 'en-us' 设置为中国区域: TIME_ZONE = 'Asia/ ...
- Newtonsoft.Json工具类
这个类用于序列化和反序列化类. 效果是当前最好的.微软都推荐使用.在建立MVC的里面已经引用了这个dll. 上面一篇文章要用到 SerializeHelper工具类 public class Seri ...
- JAVA NIO 结合多线程
NIO 的选择器采用了多路复用(Multiplexing)技术,可在一个选择器上处理多个套接字, 通过获取读写通道来进行 IO 操作.由于网络带宽等原因,在通道的读.写操作中是容易出现等待的, 所以在 ...
- C#反射(一) 【转】
在还不太熟悉反射的昨天,以为反射很神秘,在网上到处找答案.今天找了段代码敲了一下,茅塞顿开!其实反射也就那么简单的一回事! 反射是一种机制,通过这种机制我们可以知道一个未知类型的类型信息.比如, ...
- Hdu5510 Bazinga
Description Ladies and gentlemen, please sit up straight. Don't tilt your head. I'm serious. For \(n ...
- 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 ...
- 2016 年开发者应该掌握的十个 Postgres 技巧
[编者按]作为一款开源的对象-关系数据库,Postgres 一直得到许多开发者喜爱.近日,Postgres 正式发布了9.5版本,该版本进行了大量的修复和功能改进.而本文将分享10个 Postgres ...
- ASP.NET中Cookie的使用
学习web开发,使用Cookie是不可避免的,在这就浅 显的总结一下,供新手参阅.个人感觉Cookie的使用和ASP.NET中的Session非常像,只不过Cookie是保存在客户端,而 Sessio ...
- C#编程技术层次
不谈具体领域(比如搜索,视频,网络等),单就编程语言这个垂直方向,大体上对它有一个如下的层次划分. 1. 基本运用C#语法,在各种工具和示例代码的支持下,完成一些基本程序任务 2. 熟练掌握面向对象与 ...