how tomcat works 4 Tomcat Default Connector

一个tomcat的connector必须满足以下条件:

  • 必须实现 org.apache.catalina.Connector 接口。
  • 必须创建实现org.apache.catalina.Request接口的请求对象
  • 必须创建实现org.apache.catalina.Response接口的响应对象

Tomcat 默认的连接器基本上和前面的简单连接器工作方式差不多。

它等待HTTP请求,创建请求和响应对象,传递这两个对象给容器。传递是通过or.apache.catalina.Container接口的invoke方法

public void invoke( 
  org.apache.catalina.Request request, 
  org.apache.catalina.Response response);
在invoke方法中,容器加载servlet类,调用它的service方法,管理会话,记录错误信息。

默认的连接器做了一些优化,首先就是提供了一个可变对象的池来避免对象创建的开销。其次,许多地方使用字符数组来代替字符串对象。

HTTP 协议的持久连接特性

当一个浏览器连接到一个web服务器,连接会在请求的资源发送后关闭。然而,一个网页可能还包含图片,applets小程序等资源。因此,当一个页面被请求,浏览器也需要下载被这个页面引用的资源。如果这些资源通过不同的连接去下载,处理就会变得非常慢。持久连接的引入解决了这个问题,当一个页面下载下来,服务器不会直接关闭连接。而是等待客户端请求所有的应用资源。使用相同的连接来下载所有的资源节省了大量的时间。

Connector 接口

默认连接器接口类图

连接器和容器是一对一的关系。

HttpConnector 类

它实现了org.apache.catalina.Connnector, java.lang.Runnable,org.apache.catalina.LifeCycle.生命周期接口用来维护所有实现它的Catalina组件。

创建一个服务器套接字

初始化方法中调用开放的私有方法来返回java.net.ServerSocket实例,赋值给serverSocket.

获得一个ServerSocket实例通过它的工厂。可以通过org.pache.catalina.net.ServerSocketfactory了解。

维护HttpProcessor的实例们

在第三章中,HttpConnector 实例一次只有一个HttpProcessor实例,它可以处理Http 请求一次。在默认的连接器中,HttpConnector有一个HttpProcessor对象池,每一个HttpProcessor实例有一个独有的线程。因此HttpConnector可以同时处理多个http 请求。

避免每次创建HttpProcessor对象,HttpProcessor实例都存放于java.io.Stack 中:

private Stack processors = new Stack();

通过两个变量来控制HttpProcessor的数量:

protected int minProcessors = 5;

private int maxProcessors = 20;

初始化时,HttpConnector创建minProcessors 个HttpProcessor实例,然后根据请求不停的创建,直到达到maxProcessors。

处理HTTP请求

HttpConnector类把主要的的逻辑放在run方法中,有一个while循环来等待socket

while (!stopped) { 
    Socket socket = null; 
    try { 
      socket = serverSocket.accept(); 
    ...

每一个HTTP 请求,它都会通过调用私有的createProcessor方法来获得HttpProcessor实例。

HttpProcessor processor = createProcessor();

if (processor == null) { 
     try { 
       log(sm.getString("httpConnector.noProcessor")); 
       socket.close(); 
     } 
     ... 
     continue;

如果processor实例超过上限,那么socket只是简单关闭,并不处理请求。

如果createProcessor方法不返回空,客户端套接字就被传递给processor。

    processor.assign(socket);

现在就是HttpProcessor实例的任务了,它读取输入流和解析Http请求。 

HttpProcessor 类

Listing 4.1: The HttpProcessor class's run method.  
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(); 
  } 
}
run方法中的while循环按照这个顺序运行: 获得套接字,处理,调用连接器的recycle方法把当前的HttpProcessor实例推送回栈。

void recycle(HttpProcessor processor){

    processors.push(processor);

}


这里有HttpProcessor类的assign 和 await方法:

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(); 
  ... 
} 
 
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); 
}
程序遵循两种方法综合 
Table 4.1: Summary of the await and assign method  
The processor thread (the await method) 
while (!available) {  
wait();  
}  
Socket socket = this.socket;  
available = false;  
notifyAll();  
return socket; // to the run  
// method  
The connector thread (the assign method) 
while (available) {  
wait();  
}  
this.socket = socket;  
available = true;  
notifyAll();  
...  
 

   Request and Response 类;

处理请求

  • 解析连接
  • 解析请求
  • 解析头


Listing 4.2: The parseConnection method  
 
private void parseConnection(Socket socket) 
  throws IOException, ServletException { 
  if (debug >= 2) 
    log("    parseConnection: address=" + socket.getInetAddress() + 
      ", port=" + connector.getPort()); 
  ((HttpRequestImpl) request).setInet(socket.getInetAddress()); 
  if (proxyPort != 0) 
    request.setServerPort(proxyPort); 
  else 
    request.setServerPort(serverPort); 
  request.setSocket(socket); 
}

简单容器程序

Listing 4.3: The SimpleContainer class  
 
package ex04.pyrmont.core; 
 
import java.beans.PropertyChangeListener; 
import java.net.URL; 
import java.net.URLClassLoader; 
import java.net.URLStreamHandler; 
import java.io.File; 
import java.io.IOException; 
import javax.naming.directory.DirContext; 
import javax.servlet.Servlet; 
import javax.servlet.ServletException; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import org.apache.catalina.Cluster; 
import org.apache.catalina.Container; 
import org.apache.catalina.ContainerListener; 
import org.apache.catalina.Loader; 
import org.apache.catalina.Logger; 
import org.apache.catalina.Manager; 
import org.apache.catalina.Mapper; 
import org.apache.catalina.Realm; 
import org.apache.catalina.Request; 
import org.apache.catalina.Response; 
 
public class SimpleContainer implements Container { 
 
  public static final String WEB_ROOT = 
    System.getProperty("user.dir") + File.separator + "webroot"; 
 
  public SimpleContainer() {    } 
  public String getInfo() { 
    return null; 
  } 
  public Loader getLoader() { 
    return null; 
  } 
  public void setLoader(Loader loader) {    } 
  public Logger getLogger() { 
    return null; 
  } 
 public void setLogger(Logger logger) {    } 
  public Manager getManager() { 
    return null; 
  } 
  public void setManager(Manager manager) {    } 
  public Cluster getCluster() { 
    return null; 
  } 
  public void setCluster(Cluster cluster) {    } 
 
  public String getName() { 
    return null; 
  } 
  public void setName(String name) {    } 
  public Container getParent() { 
    return null; 
  } 
  public void setParent(Container container) {    } 
 public ClassLoader getParentClassLoader() { 
    return null; 
  } 
  public void setParentClassLoader(ClassLoader parent) {    } 
  public Realm getRealm() { 
    return null; 
  } 
  public void setRealm(Realm realm) {    } 
  public DirContext getResources() { 
    return null; 
  } 
  public void setResources(DirContext resources) {    } 
  public void addChild(Container child) {    } 
  public void addContainerListener(ContainerListener listener) {    } 
  public void addMapper(Mapper mapper) {    } 
  public void addPropertyChangeListener( 
    PropertyChangeListener listener) {    } 
  public Container findchild(String name) { 
    return null; 
  } 
  public Container[] findChildren() { 
    return null; 
  } 
  public ContainerListener[] findContainerList
  public ContainerListener[] findContainerListeners() { 
    return null; 
  } 
  public Mapper findMapper(String protocol) { 
    return null; 
  } 
  public Mapper[] findMappers() { 
    return null; 
  } 
  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)).toStrin
      urls[0] = new URL(null, repository, streamHand
      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()); 
    } 
  } 
 
  public Container map(Request request, boolean update) { 
    return null; 
  } 
  public void removeChild(Container child) {    } 
  public void removeContainerListener(ContainerListener listener) {    } 
  public void removeMapper(Mapper mapper) {    } 
  public void removoPropertyChangeListener( 
    PropertyChangeListener listener) { 
  } 
}

Bootstrap 类

The Bootstrap class is given in Listing 4.4. 
Listing 4.4: The ex04.pyrmont.startup.Bootstrap class  
 
package ex04.pyrmont.startup; 
import ex04.pyrmont.core.simplecontainer; 
import org.apache.catalina.connector.http.HttpConnector; 
 
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(); 
    } 
  } 
}

Running 这个程序。

java -classpath ./lib/servlet.jar;./ ex04.pyrmont.startup.Bootstrap 



转载于:https://my.oschina.net/sunxuetao/blog/128255

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值