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 ...
随机推荐
- predis如何实现phpredis的pconnect方法
predis和phpredis都是redis的php客户端,区别可以看这里,这里不赘述. phpredis是php扩展,由C语言编写,诞生较早,很多PHPer都熟悉. predis是用PHP语言编写, ...
- swift官方文档中的switch中case let x where x.hasSuffix("pepper")是什么意思?
在官方文档中,看到这句.但不明白什么意思. let vegetable = "red pepper" switch vegetable { case "celery&qu ...
- QNetworkRequest 请求类
QNetworkRequest Class Header: #include <QNetworkRequest>qmake: QT += networkSince: ...
- EasyPHP的Apache报错
今天安装了最新版本的软件:EasyPHP-DevServer-14.1VC11-install.exe 启动报错: 打开Cport软件: 可见80端口被系统占用,导致Apache不能启动. (1)手动 ...
- C#反射(二) 【转】
如果没有看<C#反射(一)>.建议先看<C#反射(一)>再看这一篇.上一篇文章发表,有人评论我所写的东西比较基础.其实我也知道我也只不过是在写最基础的语法而已,之所以写它是因为 ...
- Count The Pairs
hdu4750:http://acm.hdu.edu.cn/showproblem.php?pid=4750 题意:给你一个带权无向图,然后让你求这样的点对s,t,使得s--t的所有路径上的最大的边的 ...
- 51,PIC,AVR单片机它们的优点缺点都有哪些?
我有幸接触了几款单片机,并用它们做了一些项目.现在想做个小总结,谈一下自己用各种单片机的感受.仅是个人意见,仁者见仁智者见智. 传统51,我想我就不多说了,适合菜鸟入门,容易上手,价格一般(从性价比方 ...
- android 自定义命名空间
一.统一的用户界面是可以使得应用程序更友好.要做到用户界面的统一,我们就必须用到风格(style)和主题(theme).自定义一个View的方法步骤如下:1.首先,在values文件夹下定义一个att ...
- 根据rowid回表
select rowid from T_PM_DEPOSIT_HIS partition(DEPOSIT_HIS_20120104) ; SQL> set linesize 200 SQL> ...
- bzoj1706
倍增好题,f[p,i,j]表示i到j经过了2^p条边走过的最短路径显然f[p+1]可以由f[p]转移来然后对n二进制拆分累加即可 ; ..,..] of int64; f,pf:..] of int6 ...