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

作者: admin 分类: Tomcat 发布时间: 2019-11-15 10:58  阅读: 281 views

上一篇文章整理了tomcat4.x/5.x的核心组件catalina连接器的部分实现代码,继续往下看《how tomcat works》/深入剖析tomcat,是对tomcat默认连接器的说明。这一章节的源码量比前面的示例陡然上升,主要是增加了对http多线程请求的处理、设计模式的引用、http1.1新特性的使用及返回响应信息的补充。我这里是看的 catalin-4.1.36.jar包文件。

期间碰到了监控类对象生命周期的LifeCycle接口,关于Lifecycle接口的说明及示例,这个作者说的比较好 https://blog.csdn.net/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/5.x的默认连接器的多线程处理逻辑整理及代码示例、使用方式

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

更多阅读