Tomcat4默认连接器
Tomcat的连接器(Connector)采用模块化思想设计,作为一个独立的模块与Servlet Container相连接。目前已经有多种连接器,像Coyote、mod_jk、mod_jk2、mod_webapp等,Tomcat4中的默认连接器目前已经被速度更快的Coyote代替。但是Tomcat4的默认连接器仍然是一个很好的学习工具,有助于理解Tomcat连接器的原理,所有的Tomcat连接器只需满足一下三大:
1、 实现org.apache.catalina.Connector接口;
2、 创建实现了org.apache.catalina.Request接口的request对象;
3、 创建实现了org.apache.catalina.Response接口的response对象。
默认连接器会调用侦听HTTP请求,创建request和response并通过调用org.apache.catalina.Container接口的invoke方法把request和response传给Container:
public void invoke(org.apache.catalina.Request request, org.apache.catalina.Response response);
同时默认连接器提供了一个池用来缓存变量,并把很多String改成了char数组。
默认连接器全面支持HTTP1.1,同时兼任HTTP0.9和HTTP1.0.
HTTP1.1的新特性
1. 支持持久化TCP连接。在以前版本中一旦服务端收到请求相关资源,马上就被关闭,但是一个请求页面中可能会引用很多其它信息,如图片等,如果每个资源都用一个连接会比较耗时,完全可以在一个连接里面处理。HTTP1.1中增加了支持化连接,解决了上述问题。通过request header中指定connection:keep-alive实现
2. 支持块编码,分块传输。
3. 传输一个继续状态(100)。
Connector接口
所有的Tomcat连接器都必须实现org.apache.catalina.Connector接口,其中提供了很多需要实现的方法,最重要的有getContainer、setContainer、createRequest和createResponse方法。
setContainer用来为连接器分配一个servlet容器,getContainer获取分配给连接器的容器,createRequest为来自客户端的请求创建一个request对象,createResponse为请求创建响应(Response)。Connector的类图如下:
其中Connector与Container的关系式一对一的,而HttpConnector与HttpProcessor对象是一对多的。
HttpConnector类
HttpConnector是Connector的实现类,即Tomcat默认的连接器类,作为组件(Component)必须实现org.apache.catalina.Lifecycle,具有生命周期,要在Tomcat中创建一个连接器必须先调用它的init方法进行初始化,然后调用start方法启动。
创建ServerSocket
在调用HttpConnector的启动方法的时候,会创建一个ServerSocket对象,用来侦听客户端的请求。在创建ServerSocket对象的时候使用了工厂模式,提供了一个工厂接口ServerSocketFactory与工厂实现类DefaultServerSocketFactory,我先在这里应该是为了简化ServerSocket创建的过程,便于处理多个端口的情况。
缓存HttpProcessor实例
为了并发处理Request,提高服务器的处理效率,这里用Stack提供了对HttpProcessor进行缓冲,同时通过minProcessors和maxProcessors来控制并发数,一旦请求超过maxProcessors将会被忽略。
为HTTP Request服务
对于侦听到的每一个HTTP请求(Socket),HttpConnector都会去从缓冲栈中获取一个处理器HttpProcessor,并把获取到的Socket分配刚刚获取到的HttpProcessor。
HttpProcessor对象
Http 请求处理器是一个线程,不断调用await方法获取socket,一旦取到以后就调用process方法进行处理,处理完成以后会把当前处理器放入缓冲栈里面。
run方法循环调用await获取socket进行处理,处理完成以后把处理器放回缓冲栈:
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);
}
// Tell threadStop() we have shut ourselves down successfully
synchronized (threadSync) {
threadSync.notifyAll();
}
}
await方法获取socket,如果不能获取调用wait方法把当前线程挂起:
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);
}
assign方法为处理器分配需要处理的socket,并唤醒处理器中的线程处理socket:
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");
}
Request对象
在默认连接器中Http请求用org.apache.catalina.Request接口表示具体类关系见下图:
Response对象
Response接口及其相关类如下图:
处理请求
一旦HttpProcessor组件中线程获取到socket以后,会调用process方法对request请求进行处理,主要做下面三步:
1、 解析connection
2、 解析request
3、 解析request header
简单servlet容器
要测试连接器还需要提供两个类Bootstray和SimpleContainer,Bootstray用来启动应用,SimpleContainer是servlet容器,实现org.ahache.catalina.Container接口,其中有一个关键方法inveke回去加载servlet并调用其的service方法:
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) {
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());
}
}
Bootstray会构造连接器和容器,把容器分配给连接器,先调用连接器的init方法进行初始化,然后调用连接器的start方法启动连接器,如下:
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();
// make the application wait until we press any key.
System.in.read();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
运行应用
要运行应用,需要导入servlet-api.jar(servlet2.4以后)或servlet.jar(servlet2.3),同时在需要把PrimitiveServlet.java编译后放在webroot下,在页面浏览器中输入
http://localhost:8080/servlet/PrimitiveServlet
将会在页面输出:
Hello.Roses are red. Violets are blue.