上一篇文章整理了tomcat4.x/5.x的核心组件catalina连接器的部分实现代码,继续往下看《how tomcat works》/深入剖析tomcat,是对tomcat默认连接器的说明。这一章节的源码量比前面的示例陡然上升,主要是增加了对http多线程请求的处理、设计模式的引用、http1.1新特性的使用及返回响应信息的补充。我这里是看的 catalin-4.1.36.jar包文件。
期间碰到了监控类对象生命周期的LifeCycle接口,关于Lifecycle接口的说明及示例,这个作者说的比较好 https://blog.youkuaiyun.com/is_zhoufeng/article/details/49593655
我这里简单的整理下,对多个http请求的处理部分逻辑。增加了一个stack结构用来存储一定量的 HttpProcess处理实例。如下
package com.chl.webserver.shortsimple;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.AccessControlException;
import java.util.Stack;
import java.util.Vector;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
/**
* 简单示例 tomcat4默认连接器的 同时处理多个http请求 整理自 catalina-4.1.36.jar
*
* @author chenhailong
*
*/
public class HttpConnector implements Runnable {
// 最小实例个数
protected int minProcessors = 5;
// 最大实例个数
private int maxProcessors = 20;
// 当前实例数量
private int curProcessors = 0;
// httpProcessor实例池
private Stack processors = new Stack();
private Vector created = new Vector();
// serverSocket服务
private ServerSocket serverSocket = null;
//The thread synchronization object.
private Object threadSync = new Object();
private boolean initialized = false;
private boolean started = false;
private boolean stopped = false;
// 超时设置
private int connectionTimeout = 60000;
// 没有延时
private boolean tcpNoDelay = true;
@Override
public void run() {
while(!this.stopped) {
Socket socket = null;
try {
socket = this.serverSocket.accept();
if(this.connectionTimeout > 0) {
socket.setSoTimeout(connectionTimeout);
}
socket.setTcpNoDelay(this.tcpNoDelay);
}catch(AccessControlException ace) {
continue;
}catch (IOException e) {
try {
//异常处理
}catch(Exception ee) {
}
continue;
}
//创建HttpProcessor实例对象进行请求响应
HttpProcessor processor = createProcessor();
if(processor == null) {
try {
socket.close();
}catch(IOException e) {
}
continue;
}else {
processor.assign(socket);
}
}
synchronized (this.threadSync)
{
this.threadSync.notifyAll();
}
}
//判断当前实例数量,进行创建
private HttpProcessor createProcessor() {
synchronized (this.processors) {
if (this.processors.size() > 0) {
return (HttpProcessor) this.processors.pop();
}
if ((this.maxProcessors > 0) && (this.curProcessors < this.maxProcessors)) {
return newProcessor();
}
if (this.maxProcessors < 0) {
return newProcessor();
}
return null;
}
}
//创建新的HttpProcessor实例
private HttpProcessor newProcessor() {
HttpProcessor processor = new HttpProcessor(this, this.curProcessors++);
if ((processor instanceof Lifecycle)) {
try {
processor.start(); //开启线程
} catch (LifecycleException e) {
return null;
}
}
this.created.addElement(processor);
return processor;
}
//将当前实例放到栈顶,(对HttpProcessor资源的回收)
void recycle(HttpProcessor processor)
{
this.processors.push(processor);
}
}
HttpProcessor类中的run()方法会循环依次做如下几件事:获取套接字,进行处理,调用连接器的recycle()方法将当前实例放回栈顶。当执行到await()方法时会阻塞。await()方法会阻塞处理器线程的控制流,直到它从HttpConnector中获取了新的Socket对象。也就是知道HttpConnector对象调用HttpProcessor实例 assign()方法前,会一直阻塞。
await()方法和assign()方法不是运行在同一个线程中。assign()方法是从HttpConnector对象的run()方法中调用的。它们两之间通过使用一个名为available的布尔变量和java.lang.Objet类的wait()方法和notifyAll()方法进行沟通,如下
package com.chl.webserver.shortsimple;
import java.net.Socket;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.util.LifecycleSupport;
public class HttpProcessor implements Runnable, Lifecycle {
private HttpConnector connector = null;
private boolean available = false;
private Socket socket = null;
private Thread thread = null;
private boolean started = false;
private Object threadSync = new Object();
private String threadName = null;
private LifecycleSupport lifecycle = new LifecycleSupport(this);
// 构造函数
public HttpProcessor(HttpConnector connector, int id) {
// this.connector = connector;
// this.debug = connector.getDebug();
// this.id = id;
// this.proxyName = connector.getProxyName();
// this.proxyPort = connector.getProxyPort();
// this.request = ((HttpRequestImpl)connector.createRequest());
// this.response = ((HttpResponseImpl)connector.createResponse());
// this.serverPort = connector.getPort();
// this.threadName = ("HttpProcessor[" + connector.getPort() + "][" + id + "]");
}
//由HttpConnector调用
synchronized void assign(Socket socket) {
while (this.available) {
try {
wait();
} catch (InterruptedException e) {
}
}
this.socket = socket;
this.available = true;
notifyAll();
// if ((this.debug >= 1) && (socket != null)) {
// log(" An incoming request is being assigned");
// }
}
//启动HttpProcessor时调用(与上面的不在同一个线程运行)
private synchronized Socket await() {
while (!this.available) {
try {
wait();
} catch (InterruptedException e) {
}
}
Socket socket = this.socket;
this.available = false;
notifyAll();
// if ((this.debug >= 1) && (socket != null)) {
// log(" The incoming request has been awaited");
// }
return socket;
}
@Override
public void run() {
while(!this.started) {
Socket socket = await();
if(socket != null) {
try {
process(socket);
}catch(Throwable t) {
// ..
}
this.connector.recycle(this);
}
}
synchronized (this.threadSync) {
this.thread.notifyAll();
}
}
//解析请求头等信息
private void process(Socket socket) {
//处理请求流信息..
}
//开始当前线程
private void threadStart() {
// log(this.sm.getString("httpProcessor.starting"));
this.thread = new Thread(this, this.threadName);
this.thread.setDaemon(true);
this.thread.start();
// if (this.debug >= 1) {
// log(" Background thread has been started");
// }
}
// Lifecycle 实现类
@Override
public void start() throws LifecycleException {
this.lifecycle.fireLifecycleEvent("start", null);
this.started = true;
threadStart();
}
@Override
public void addLifecycleListener(LifecycleListener arg0) {
}
@Override
public LifecycleListener[] findLifecycleListeners() {
return null;
}
@Override
public void removeLifecycleListener(LifecycleListener arg0) {
}
@Override
public void stop() throws LifecycleException {
}
}
我这里是理解了好几下才明白这个调用逻辑,之后写多线程调用的时候可以借鉴。上面的所有代码及逻辑,已经封装成了一个jar包,对外提供接口。我们可以可以通过实现接口(org.apache.catalina.Container)来直接使用这个默认连接器。如下代码(书作者示例)
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[] 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)).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());
}
}
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 removePropertyChangeListener(PropertyChangeListener listener) {
}
}
启动类如下:
/* explains Tomcat's default container */
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();
}
}
}
默认连接器就可以使用了。

本文深入剖析Tomcat4.x默认连接器如何同时处理多个HTTP请求,介绍了HttpConnector和HttpProcessor的实现机制,包括实例池、生命周期管理、多线程处理流程和资源回收策略。
555

被折叠的 条评论
为什么被折叠?



