Tomcat等容器解释汇总
2010年06月10日
清晰的tomcat解释:
Tomcat是一个免费的开源的Serlvet容器,它是Apache基金会的Jakarta项目中的一个核心项目,由Apache,Sun和其它一些公司及个人共同开发而成。由于有了Sun的参与和支持,最新的Servlet和Jsp规范总能在Tomcat中得到体现。
与传统的桌面应用程序不同,Tomcat中的应用程序是一个WAR(Web Archive)文件。WAR是Sun提出的一种Web应用程序格式,与JAR类似,也是许多文件的一个压缩包。这个包中的文件按一定目录结构来组织:通常其根目录下包含有Html和Jsp文件或者包含这两种文件的目录,另外还会有一个WEB-INF目录,这个目录很重要。通常在WEB-INF目录下有一个web.xml文件和一个classes目录,web.xml是这个应用的配置文件,而classes目录下则包含编译好的Servlet类和Jsp或Servlet所依赖的其它类(如JavaBean)。通常这些所依赖的类也可以打包成JAR放到WEB-INF下的lib目录下,当然也可以放到系统的CLASSPATH中,但那样移植和管理起来不方便
在Tomcat中,应用程序的部署很简单,你只需将你的WAR放到Tomcat的webapp目录下,Tomcat会自动检测到这个文件,并将其解压。你在浏览器中访问这个应用的Jsp时,通常第一次会很慢,因为Tomcat要将Jsp转化为Servlet文件,然后编译。编译以后,访问将会很快。另外Tomcat也提供了一个应用:manager,访问这个应用需要用户名和密码,用户名和密码存储在一个xml文件中。通过这个应用,辅助于Ftp,你可以在远程通过Web部署和撤销应用。当然本地也可以。
Tomcat不仅仅是一个Servlet容器,它也具有传统的Web服务器的功能:处理Html页面。但是与Apache相比,它的处理静态Html的能力就不如Apache。我们可以将Tomcat和Apache集成到一块,让Apache处理静态Html,而Tomcat处理Jsp和Servlet。这种集成只需要修改一下Apache和Tomcat的配置文件即可。
基于Tomcat的开发其实主要是Jsp和Servlet的开发,开发Jsp和Servlet非常简单,你可以用普通的文本编辑器或者IDE,然后将其打包成WAR即可。我们这里要提到另外一个工具Ant,Ant也是Jakarta中的一个子项目,它所实现的功能类似于Unix中的make。你需要写一个build.xml文件,然后运行Ant就可以完成xml文件中定义的工作,这个工具对于一个大的应用来说非常好,我们只需在xml中写很少的东西就可以将其编译并打包成WAR。事实上,在很多应用服务器的发布中都包含了Ant。另外,在Jsp1.2中,可以利用标签库实现Java代码与Html文件的分离,使Jsp的维护更方便。
************************************************** **************
Tomcat原理解说:
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/"+fileNam e);
} 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 = ""
+ "404 Not Found" + "404 Not Found" + "
usage:http://yourHostName:port/" + "fileName.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 返回,然后将此元素设成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、weblogic、jboss的区别,容器的作用: 1 名字不一样
2 供应商不一样
3 价格不一样
4 tomcat 是不支持Java EE的
5 别的我看不出啥实质性区别
容器的作用? 就是给你一个运行的环境,它来替你装载,初始化,运行,传递参数,返回结果,并最后销毁,也就是控制着你编写内容的整个生命周期,并提供之间的协调工作。 当然也负责和各种外设打交道。比如网络,磁盘,内存等。