tomcat4x5x的默认连接器的多线程处理逻辑整理及代码示例、使用方式

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

上一篇文章整理了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();
    }
  }
}

默认连接器就可以使用了。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值