Tomcat的模块结构设计的相当好,而且其Web 容器的性能相当出色。JBoss直接就使用了Tomcat的web容器,WebLogic的早期版本也是使用了Tomcat的代码。

Web容器的工作过程在下面的第二个参考文档中的文档已经说得相当清楚,我也就不再重复说了。如果不清楚调用过程,需要先看这个文档。这里分析一下Connector的处理过程。

1. 一个简单的Web Server示例

这个例子也是从网上找得,不知道原作者,也就不在参考资料中引用了。

这个启动服务的主程序。

public class HttpServer {

 public static void main(String args[]) {

  int port;

  ServerSocket server_socket; // 读取服务器端口号

  try {

   port = Integer.parseInt(args[0]);

  } catch (Exception e) {

   port = 8080;

  }

  try {

   // 监听服务器端口,等待连接请求

   server_socket = new ServerSocket(port);

   System.out.println(”httpServer running on port “

     + server_socket.getLocalPort());

   // 显示启动信息

   while (true) {

    Socket socket = server_socket.accept();

    System.out.println(”New connection accepted “

      + socket.getInetAddress() + “:” + socket.getPort());

    // 创建分线程

    try {

     HttpRequestHandler request = new HttpRequestHandler(socket);

     Thread thread = new Thread(request);

     // 启动线程

     thread.start();

    } catch (Exception e) {

     System.out.println(e);

    }

   }

  } catch (IOException e) {

   System.out.println(e);

  }

 }

}

下面是创建输出的线程

public class HttpRequestHandler implements Runnable {

 final static String CRLF = “/r/n”;

 Socket socket;

 InputStream input;

 OutputStream output;

 BufferedReader br;

 // 构造方法

 public HttpRequestHandler(Socket socket) throws Exception {

  this.socket = socket;

  this.input = socket.getInputStream();

  this.output = socket.getOutputStream();

  this.br = new BufferedReader(new InputStreamReader(socket

    .getInputStream()));

 }

// 实现Runnable 接口的run()方法

 public void run() {

  try {

   processRequest();

  } catch (Exception e) {

   System.out.println(e);

  }

 }

private void processRequest() throws Exception {

  while (true) {

   // 读取并显示Web 浏览器提交的请求信息

   String headerLine = br.readLine();

   System.out.println(”The client request is ” + headerLine);

   if (headerLine.equals(CRLF) || headerLine.equals(”"))

    break;

   StringTokenizer s = new StringTokenizer(headerLine);

   String temp = s.nextToken();

   if (temp.equals(”GET”)) {

    String fileName = s.nextToken();

    fileName = “.” + fileName;

    // 打开所请求的文件

    FileInputStream fis = null;

    boolean fileExists = true;

    try {

     fis = new FileInputStream(”D:/workspace/tomcat/bin/”+fileName);

    } catch (FileNotFoundException e) {

     fileExists = false;

    }

    // 完成回应消息

    String serverLine = “Server: a simple java httpServer”;

    String statusLine = null;

    String contentTypeLine = null;

    String entityBody = null;

    String contentLengthLine = “error”;

    if (fileExists) {

     statusLine = “HTTP/1.0 200 OK” + CRLF;

     contentTypeLine = “Content-type: ” + contentType(fileName) + CRLF;

     contentLengthLine = “Content-Length: “

       + (new Integer(fis.available())).toString() + CRLF;

    } else {

     statusLine = “HTTP/1.0 404 Not Found” + CRLF;

     contentTypeLine = “text/html”;

     entityBody = “<HTML>”

       + “<HEAD><TITLE>404 Not Found</TITLE></HEAD>” + “<BODY>404 Not Found” + “<br>usage:http://yourHostName:port/” + “fileName.html</BODY></HTML>”;

    }

    // 发送到服务器信息

    output.write(statusLine.getBytes());

    output.write(serverLine.getBytes());

    output.write(contentTypeLine.getBytes());

    output.write(contentLengthLine.getBytes());

    output.write(CRLF.getBytes());

    // 发送信息内容

    if (fileExists) {

     sendBytes(fis, output);

     fis.close();

    } else {

     output.write(entityBody.getBytes());

    }

   }

  }

  // 关闭套接字和流

  try {

   output.close();

   br.close();

   socket.close();

  } catch (Exception e) {

  }

 }

private static void sendBytes(FileInputStream fis, OutputStream os)

   throws Exception {

  // 创建一个 1K buffer

  byte[] buffer = new byte[1024];

  int bytes = 0;

  // 将文件输出到套接字输出流中

  while ((bytes = fis.read(buffer)) != -1) {

   os.write(buffer, 0, bytes);

  }

 }

private static String contentType(String fileName) {

  if (fileName.endsWith(”.htm”) || fileName.endsWith(”.html”)) {

   return “text/html”;

  }

  return “fileName”;

 }



这个简单的例子说明的web服务的基本实现。Tomcat在此之上模块化出线程池,网络连接和WebHttp协议3个包。线程池可独立使用,网络连接使用池化,WebHttp直接从网络连接池中获取即可。

2. 线程池的实现

这个功能的实现在包 org.apache.tomcat.util.thread 中。

ThreadPool是线程池,是这个功能实现的核心。它使用了所有的其他类进行工作。在类图中,所有的其他类都是被此类的使用关系。

我们来看此类是如何工作得。

启动连接池的方法:

 public synchronized void start() {

  stopThePool = false;

  currentThreadCount = 0;

  currentThreadsBusy = 0;

adjustLimits();

  pool = new ControlRunnable[maxThreads];

  openThreads(minSpareThreads);

  if (maxSpareThreads < maxThreads) {

   monitor = new MonitorRunnable(this);

  }

 } 

方法中,根据配置情况,初始化所有线程进入备用状态。

首先定义maxThreads数目的数组,但是仅仅初始化其中minSpareThreads个。MonitorRunnable用于检查,是否空闲数目超过 maxSpareThreads个。

currentThreadCount 是当前初始化可以使用的线程数目,而currentThreadsBusy 是当前正在使用的线程数目。

使用连接池的方法:

 public void runIt(ThreadPoolRunnable r) {

  if (null == r) {

   throw new NullPointerException();

  }

  ControlRunnable c = findControlRunnable();

  c.runIt(r);

 } 

该方法中,先寻找可用的线程,找到后在其中运行即可。

找可用线程的方法也很简单,就是将线程数组中第 currentThreadCount - currentThreadsBusy - 1 个元素取出返回,然后将此元素设成null。

线程运行完毕后,设置currentThreadsBusy– ,然后将 currentThreadCount - currentThreadsBusy - 1 的线程放回就可以了。

线程不够用就等待,等待失败就抛出异常。

说明一下上面未提到的类的功能:

ThreadPoolRunnable 这是一个接口,规定了一个线程运行时需要运行的一些动作。这里需要写一些业务逻辑的代码了。

ThreadWithAttributes 这个类从上面的代码中没有看到,这个类标识当前运行线程的一些特征,比如记录当前运行线程的一些状态。

ThreadPoolListener 用于监控ThreadPool中新增线程的情况。

ControlRunnable 这个类是ThreadPool的内部类,用于运行ThreadPoolRunnable 。当ThreadPoolRunnable 运行完毕后,通知ThreadPool回收线程。它时刻处于备用状态。此对象实例化后,就一直在死循环检查是否有它需要运行的东西。

3. 网络连接功能的实现

这个功能的实现在包 org.apache.tomcat.util.net 中。

网络连接功能构建于线程池之上,实现了一个连接服务模型。服务器打开端口,池化进入连接,为进入的连接创建工作线程。

Tomcat的网络连接两个主要的应用是1. 自己提供的web应用。2. 给Apache提供的web应用。这两个过程的解析过程都是一样的。仅仅在于网络连接协议有差别而已。两个应用都使用此包的功能实现。

PoolTcpEndpoint是核心,它使用了ThreadPool。TcpWorkerThread通过调用接口TcpConnectionHandler来完成一次连接需要完成的工作。TcpConnection标识了一个连接对象。

PoolTcpEndpoint的初始化方法代码很简单,在构建器中创建或引用ThreadPool,在初始化时创建ServerSocket,用于侦听客户端连接。

下面是初始化方法

 public void initEndpoint() throws IOException, InstantiationException {

  try {

   if (factory == null)

    factory = ServerSocketFactory.getDefault();

   if (serverSocket == null) {

    try {

     if (inet == null) {

      serverSocket = factory.createSocket(port, backlog);

     } else {

      serverSocket = factory

        .createSocket(port, backlog, inet);

     }

    } catch (BindException be) {

     throw new BindException(be.getMessage() + “:” + port);

    }

   }

   if (serverTimeout >= 0)

    serverSocket.setSoTimeout(serverTimeout);

  } catch (IOException ex) {

   // log(”couldn’t start endpoint”, ex, Logger.DEBUG);

   throw ex;

  } catch (InstantiationException ex1) {

   // log(”couldn’t start endpoint”, ex1, Logger.DEBUG);

   throw ex1;

  }

  initialized = true;

 }

 

启动的方法同样简单,仅仅将TcpWorkerThread作为线程池的工作线程,启动连接池,就大功告成了。

 public void startEndpoint() throws IOException, InstantiationException {

  if (!initialized) {

   initEndpoint();

  }

  if (isPool) {

   tp.start();

  }

  running = true;

  paused = false;

  if (isPool) {

   listener = new TcpWorkerThread(this);

   tp.runIt(listener);

  } else {

   log.error(”XXX Error - need pool !”);

  }

 }

 

侦听的细节包装在TcpWorkerThread类中。运行时,它在ServerSocket端口侦听。当发现有连接进入后,立刻开启一个新线程继续侦听,本线程开始处理连接。下面是代码:

 public void runIt(Object perThrData[]) {

  // Create per-thread cache

  if (endpoint.isRunning()) {

   // Loop if endpoint is paused

   while (endpoint.isPaused()) {

    try {

     Thread.sleep(1000);

    } catch (InterruptedException e) {

     // Ignore

    }

   }

   // Accept a new connection

   Socket s = null;

   try {

    s = endpoint.acceptSocket();

   } finally {

    // Continue accepting on another thread…

    if (endpoint.isRunning()) {

     endpoint.tp.runIt(this);

    }

   }

   // Process the connection

   if (null != s) {

    TcpConnection con = null;

    int step = 1;

    try {

     // 1: Set socket options: timeout, linger, etc

     endpoint.setSocketOptions(s);

     // 2: SSL handshake

     step = 2;

     if (endpoint.getServerSocketFactory() != null) {

 endpoint.getServerSocketFactory().handshake(s);

     }

     // 3: Process the connection

     step = 3;

     con = (TcpConnection) perThrData[0];

     con.setEndpoint(endpoint);

     con.setSocket(s);

 endpoint.getConnectionHandler().processConnection(con,

       (Object[]) perThrData[1]);

    } catch (SocketException se) {

…… 

4. 协议 web http的实现

这个功能的实现在包 org.apache.coyote.http11 中。

对于Http协议的实现核心类是Http11Protocol。具体功能的实现类有MXPoolListener(实现ThreadPoolListener),Http11ConnectionHander(实现TcpConnectionHandler)。

Http11Protocol的初始化方法比较简单,就是设置一下让网络连接开始运行。

Http11ConnectionHander则初始化类Http11Processor,由它解析请求的字符串,交给生成此Connection的Connector的Container,也就是Engine完成。Engine通过递归,解析应返回用户的数据。这个过程在参考文档中有介绍了。

Tomcat原理 分类: 原理 2015-06-28 19:26 5人阅读 评论(0) 收藏的更多相关文章

  1. CSS_Spirte实现原理 分类: HTML+CSS 2015-04-28 22:58 531人阅读 评论(0) 收藏

    CSS Spirte就是所谓的把很多的小图标合并成一张大的图片,然后使用CSS的background-position属性,来动态的定位自己需要图标的位置.这样做的目的主要是减少HTTP请求,加快网页 ...

  2. Network Saboteur 分类: 搜索 POJ 2015-08-09 19:48 7人阅读 评论(0) 收藏

    Network Saboteur Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 10147 Accepted: 4849 Des ...

  3. 欧拉通路-Play on Words 分类: POJ 图论 2015-08-06 19:13 4人阅读 评论(0) 收藏

    Play on Words Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 10620 Accepted: 3602 Descri ...

  4. Power Strings 分类: POJ 串 2015-07-31 19:05 8人阅读 评论(0) 收藏

    Time Limit:3000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u Submit Status Practice POJ ...

  5. 迷之节约 分类: sdutOJ 最小生成树 2015-06-24 19:10 10人阅读 评论(0) 收藏

    迷之节约 Time Limit: 1000ms Memory limit: 65536K 有疑问?点这里^_^ 题目描述 FF超级有钱,最近又买了n个(1 <= n <= 300)小岛,为 ...

  6. Hdu 1506 Largest Rectangle in a Histogram 分类: Brush Mode 2014-10-28 19:16 93人阅读 评论(0) 收藏

    Largest Rectangle in a Histogram Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 ...

  7. 二分图匹配 分类: ACM TYPE 2014-10-01 19:57 94人阅读 评论(0) 收藏

    #include<cstdio> #include<cstring> using namespace std; bool map[505][505]; int n, k; bo ...

  8. Can you find it? 分类: 二分查找 2015-06-10 19:55 5人阅读 评论(0) 收藏

    Description Give you three sequences of numbers A, B, C, then we give you a number X. Now you need t ...

  9. 菊花加载第三方--MBprogressHUD 分类: ios技术 2015-02-05 19:21 120人阅读 评论(0) 收藏

    上次说到了网络请求AFN,那么我们在网络请求的时候,等待期间,为了让用户不认为是卡死或程序出错,一般都会放一个菊花加载,系统有一个菊花加载类叫UIProgressHUD.但是我今天要说的是一个替代它的 ...

随机推荐

  1. MySQL 多实例启动和关闭脚本

    DB: 5.5.14 OS:CentOS 6.3 在MySQL多实例中,一个一个启动灰常麻烦,因此写个简单的启动和关闭脚本 启动脚本:start.sh #!/bin/bash for port in ...

  2. CentOS安装 pure-ftpd

    yum -y install pam-devel cd /usr/local .tar.gz cd pure-ftpd- ./configure --prefix=/usr/local/pure-ft ...

  3. backbone前端基础框架搭建

    前端站点名为:site: 前端框架分为:css.js和img,框架的核心在js文件夹下: js中包括collections.models.views.lib和一个app入口js

  4. 【quartz】 理论知识

    属性的介绍 1.调度器属性:分别设置调度器的实例名(instanceName) 和实例 ID (instanceId).属性 org.quartz.scheduler.instanceName 可以是 ...

  5. z470 装黑苹果 10.92

    1.分两个区,一个是mac安装区,一个是镜像拷贝区. 2.把镜像压进去. 3.安装好系统. 4.把镜像区的 extent拷贝到安装好的系统盘里去. 5.安装驱动,网盘里有.还有系统也在网盘里. 6.声 ...

  6. .NET序员的成长之路

  7. android studio 突然出现Gradle project sync failed 错误

    出现: 之前还是好好的,突然就出现Gradle project sync failed  错误,网上原因可能是工具的问题. 解决办法: 重新打开android studio就好了.不知道大家还有其他的 ...

  8. 《C++Primer》复习——with C++11 [4]

    考虑到STL的掌握主要靠的是练习,所以对于STL这部分,我把书中的练习都做一遍,加深印象.这些练习是第9.10.11.17章的,分别是顺序容器.泛型算法和关联容器等. ——10月22日 /*----- ...

  9. 【POJ】【1061】/【BZOJ】【1477】青蛙的约会

    扩展欧几里德 根据题意列出不定方程: (x+m*T)-(y+n*T)=k*L; //T表示跳了T次,由于是环,可能追了多圈,所以结果应为k*L 化简得  T(m-n)-kL=y-x; 这就成了我们熟悉 ...

  10. 【BZOJ】【2844】albus就是要第一个出场

    高斯消元解XOR方程组 srO  ZYF  Orz 膜拜ZYF…… http://www.cnblogs.com/zyfzyf/p/4232100.html /******************** ...