——每天的寥寥几笔,坚持下去,将会是一份沉甸甸 的积累。
今天的笔记是针对《tomcat how works》第四章——tomcat的默认连接器来讲的。
1.在上一篇文章我罗列书第三章的源码目录,如下:
connector: RequestStream,ResponseStream,ResponseWriter
connnectot.http:HttpConnector,HttpProcessor//前者用于创建serverSocket,监听客户端请求;后者是个关键类,后面单独讲
HttpRequest,HttpResponse,HttpRequestFacade,HttpResponseFacade
//request和response实现了HttpRequestServlet和HttpResponseServlet接口,相关填充数据由下面两个对象提供
HttpHeader,HttpRequestLine//HTTP消息的头部,请求行封装出的两个实体类
SocketInputStream//用Socket.InputStream封装出的新的类,实现了读取HTTP消息的头部和请求行,封装到上面两个对象中
Constants//存一些常量,降低耦合度,便于修改
core: servletProcessor,staticResourceProcessor
//这两个类为业务处理类,就servlet而言,会根据connector解析出的处理类类名,用反射机制实例化某servlet类来处理。
其实每章就是在之前简单的基础上进行扩张充实。在第四章里,覆盖了我们自己写的HttpConnector,HttpProcessor,采用了org.apache.catalina.connector.http.HttpConnector和org.apache.catalina.connector.http.HttpProcessor(实现了多处理器线程,可以用来处理不同的客户端请求);并新增了一个处理servlet的容器SimpleContainer。【源码及书籍下载链接:http://pan.baidu.com/s/1ntBhXX3】
2.再强调一下,第四章的突破点在于:“多处理线程”,所以需要多线程的相关知识,不是很熟的,建议先给自己充下电。马上,下一篇我也会提供相关笔记的。
3.接下来把处理请求的执行流程给梳理下。
(1)bootstrap类启动
HttpConnector connector = new HttpConnector();
SimpleContainer container = new SimpleContainer();
connector.setContainer(container);
try {
connector.initialize();
connector.start();
解释:
- 创建一个Httpconnector连接器,set一个container容器(这个后面解释);
- 初始化操作:会调用open()函数,而open()函数会调用DefaultServerFactory来生产serverSocket对象(前三章都是直接new出来的,这里用到了工厂)
- start操作:会new出来5个(默认,等于minProcessors)httpProcessor对象(通过调用newProcessor()函数,这个函数会启动httpProcessor自身的线程,即run函数已经跑起来),然后添加到栈中(源码如下)。
start():
while (curProcessors < minProcessors) {
if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
break;
HttpProcessor processor = newProcessor();
recycle(processor);//加入栈中
}
newProcessor():
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();//httpProcessor实现了runnable接口,调用start函数直接启动线程,具体源码后面提到
} catch (LifecycleException e) {
log("newProcessor", e);
return (null);
}
}
created.addElement(processor);
return (processor);
}
(2)其实(1)中的connector.start()还会进行一个操作,就是启动httpConnector线程(这个类也实现了runnable接口)。于是自然而然,start函数一执行,便进入run函数
//极简模式
public void run() {
while (!stopped) {
//监听客户端请求
//获取socket对象
processor.assign(socket);//将Socket对象传到httpProcessor里面处理
}
}
(3)httpProcessor类。主要功能依旧是封装request,response对象的相关数据,用于触发相关servlet的service方法。封装数据的过程,也就涉及http消息解析,前一篇文章讲到了parseRequest,parseHeaders等,在本文中该类将继续拓展,新增了如parseAcceptLanguage等。
除了上面提到的几个咱们熟悉的方法之外,又新增了assign()和await()方法,下面详细介绍(设计的非常巧妙),主要看看代码里我的文字介绍
public void run() {
while (!stopped) {
Socket socket = await();
//流程从这开始:
//new出这个processor对象的时候已经触发run方法(上面讲过),然后调用await()方法,由于默认available=false,
//因此await方法调用wait()方法,是的线程进入等待状态(等待notify方法唤醒)
//知道httpConnector调用assign方法,是的available=true,然后又notify,使得线程被唤醒,正好available=true,跳出while循环,
//available又被赋成false。此时run中的socket不为null,调用process方法进行处理,最后将封装好的request和response对象作为参数触发serlvet容器的处理程序
if (socket == null)continue;
try {
process(socket);
} catch (Throwable t) {}
connector.recycle(this);
}
}
private void process(Socket socket) {//极简,删掉了细节部分
connector.getContainer().invoke(request, response);
}
synchronized void assign(Socket socket) {
while (available) {
try {
wait();
} catch (InterruptedException e) {}
}
this.socket = socket;
available = true;
notifyAll();
}
private synchronized Socket await() {
while (!available) {
try {
wait();
} catch (InterruptedException e) {}
}
Socket socket = this.socket;
available = false;
notifyAll();
return (socket);
}
(4)还有一个SimpleContainer类,这个就是servlet容器,用来接收request,response对象,解析request得到相应的servlet,根据反射机制,触发改serlvet的service方法
public void invoke(Request request, Response response)throws IOException, ServletException {
servlet.service((HttpServletRequest) request, (HttpServletResponse) response);//主要就是这个invoke方法,和上的process方法挂钩
}
由于涉及到多线程相关知识,理解起来可能有些难度。下一篇我会整理一份多线程相关的笔记以供大家参考。
又周一了,可能接下来几天更新频率不会那么高(学生党要上课),不过依旧会尽量保持进度(各种学长阿里,腾讯的offer纷纷到手,学弟甚是欣羡,必须加倍努力)。。。。