`

Tomcat的Socket实现:org.apache.tomcat.util.net(一)

    博客分类:
  • J2SE
阅读更多
org.apache.tomcat.util.net包的内容都与网络连接和socket有关,比较主要和常见的是JIOEndpoint这个类,前面提到Coyote连接器的时候,就有涉及到JIOEndpoint,它用于监听某个socket端口、将socket对象交给coyote,并提供基本的线程池功能。除了JIOEndpoint,还有AprEndpoint、NioEndpoint等。由于对apr和nio不熟悉,所以只研究了一下JIOEndpoint

org.apache.tomcat.util.net.JIoEndpoint
JIOEndpoint其实和我们本科时上计算机网络或者分布式系统,做实验写的socket服务器差不多,结构也是经典的“Listen-Accept-Handle”,这里简单描述一下:JIOEndpoint使用JDK的ServerSocket类监听某个端口,有socket连接进来的时候便返回一个socket对象,交给专门的处理器。当然,具体的实现没那么简单,下面会按照socket的处理过程,详细说明其中的机理。

初始化
public void init()
        throws Exception {

        if (initialized)
            return;
        // Initialize thread count defaults for acceptor
        if (acceptorThreadCount == 0) {
            acceptorThreadCount = 1;
        }
        if (serverSocketFactory == null) {
            serverSocketFactory = ServerSocketFactory.getDefault();
        }
        if (serverSocket == null) {
            try {
                if (address == null) {
                    serverSocket = serverSocketFactory.createSocket(port, backlog);
                } else {
                    serverSocket = serverSocketFactory.createSocket(port, backlog, address);
                }
            } catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + port);
            }
        }
        //if( serverTimeout >= 0 )
        //    serverSocket.setSoTimeout( serverTimeout );
        initialized = true;
    }

在这里,利用serverSocketFactory新建了一个serverSocket对象,用于监听特定的端口

启动JIOEndpoint
// Create worker collection
            if (executor == null) {
                workers = new WorkerStack(maxThreads);
            }

            // Start acceptor threads
            for (int i = 0; i < acceptorThreadCount; i++) {
                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
                acceptorThread.setPriority(threadPriority);
                acceptorThread.setDaemon(daemon);
                acceptorThread.start();
            }

这里有几个类:worker,workerStack,Acceptor。这些都是JIOEndpoint的一些内部类,下面按照处理顺序依次讲述

Acceptor内部类
Acceptor实现了Runnable接口,只有一个方法run,做的事情就是通过ServerSocket.accept方法,得到socket,然后调用JIOEndpoint的processSocket方法

protected boolean processSocket(Socket socket) {
        try {
            if (executor == null) {
                getWorkerThread().assign(socket);
            } else {
                executor.execute(new SocketProcessor(socket));
            }
        } catch (Throwable t) {
            // This means we got an OOM or similar creating a thread, or that
            // the pool and its queue are full
            log.error(sm.getString("endpoint.process.fail"), t);
            return false;
        }
        return true;
    }

在这里,JIOEndpoint有两种处理socket的方式:使用JDK5的executor,或者内部的worker类。executor怎么用大家可以直接翻书了,我们继续讨论第二种方法。

首先我们要通过getWorkerThread()方法,得到一个Worker对象。具体逻辑是,看看WorkerStack(存放所有worker的一个堆栈)里面有没有空余的worker,有则直接拿来用,无则看看能不能新建一个worker线程,假如不能(比如超出了最大线程数限制),则返回null,那样就只能委屈一下这个acceptor,稍微等一下了(wait()方法),直到有新的worker可用时,通过notify方法唤醒等待的acceptor

     /**
     * Return a new worker thread, and block while to worker is available.
     */
    protected Worker getWorkerThread() {
        // Allocate a new worker thread
        Worker workerThread = createWorkerThread();
        while (workerThread == null) {
            try {
                synchronized (workers) {
                    workers.wait();
                }
            } catch (InterruptedException e) {
                // Ignore
            }
            workerThread = createWorkerThread();
        }
        return workerThread;
    }

如下,当有worker被回收后,通知等待的acceptor

protected void recycleWorkerThread(Worker workerThread) {
        synchronized (workers) {
            workers.push(workerThread);
            curThreadsBusy--;
            workers.notify();
        }
    }



ok,回到前面的processSocket方法,得到worker后,通过worker.assign方法,将socket对象传递给worker

Worker内部类
Worker也实现了runnable接口,有三个方法:assign、await、start和run

所谓的start方法,就是new一个Thread对象,把worker自己传进去,我们知道这个thread就开始执行run方法了。

public void run() {

            // Process requests until we receive a shutdown signal
            while (running) {

                // Wait for the next socket to be assigned
                Socket socket = await();
                if (socket == null)
                    continue;

                // Process the request from this socket
                if (!setSocketOptions(socket) || !handler.process(socket)) {
                    // Close socket
                    try {
                        socket.close();
                    } catch (IOException e) {
                    }
                }

                // Finish up this request
                socket = null;
                recycleWorkerThread(this);

            }

        }

run方法首先调用await方法

private synchronized Socket await() {

            // Wait for the Connector to provide a new Socket
            while (!available) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }

            // Notify the Connector that we have received this Socket
            Socket socket = this.socket;
            available = false;
           notifyAll();

            return (socket);

        }

通过标记位available,如果当前的worker是“非available”的,则线程会开始等待。直到我们调用的assign方法,把一个可用的socket给worker后,才会notifyall,唤醒一个线程,进而取得assign过来的socekt

synchronized void assign(Socket socket) {

            // Wait for the Processor to get the previous Socket
            while (available) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }

            // Store the newly available Socket and notify our thread
            this.socket = socket;
            available = true;
            notifyAll();

        }

所以,assign和await方法相当于生产者和消费者方法,两者通过available进行互斥,而this.socket则相当于被竞争的资源

现在,又回到Worker.run方法。在取得socket后,通过setSocketOptions(socket)方法设置socket的相关选项(例如超时值),最后通过handler.process(socket),终于把socket这个接力棒交给coyote了!

handler是什么?就是个简单的接口,如下:

/**
     * Bare bones interface used for socket processing. Per thread data is to be
     * stored in the ThreadWithAttributes extra folders, or alternately in
     * thread local fields.
     */
    public interface Handler {
        public boolean process(Socket socket);
    }

回顾一下org.apache.coyote.http11.Http11Protocol 的Http11ConnectionHandler内部类,实现的正是这个接口

分享到:
评论

相关推荐

    Apache服务器错误问题

    Apache服务器错误问题 Apache服务器错误问题 Apache服务器错误问题

    java head space.txt

    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent....

    struts2驱动包

    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053) at org.apache.catalina.core....

    解决struts2下载异常的jar包 struts2-sunspoter-stream-1.0.jar

    588) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489) at java.lang.Thread.run(Thread.java:662) 网络解决办法: (虽然该办法可行,但是本人并不提倡。具体原因在之后解释。...

    SocketInputStream.java

    * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/util/StringManager.java,v 1.2 2001/07/22 20:25:14 pier Exp $ * $Revision: 1.2 $ * $Date: 2001/07/22 20:25:14 $ * * =...

    tomcat8开启apr需要相关的组件,包含jdk,apr,apr,util,tomcat8,openssl

    Apache Tomcat 8 是一个广泛使用的开源Java应用服务器,它主要负责部署和运行Java Servlets和JavaServer Pages(JSP)。在某些情况下,为了提高性能,开发者可能会选择启用Apache Portable Runtime (APR) 模块。APR...

    apr-1.7.0.tar.gz

    总的来说,Tomcat Native 1.2.21是一个为了提升Apache Tomcat性能而设计的重要组件,通过结合APR库,它能够实现更高效的网络通信、内存管理和多线程处理。通过深入理解和使用Tomcat Native的源码,开发者可以更好地...

    apr aprutil libtool

    例如,他们会将编译后的APR和apr-util库与Tomcat的JNI(Java Native Interface)部分结合,通过JK2连接器实现Apache和Tomcat的高性能连接。这个过程中可能涉及到修改Tomcat的配置文件,以及正确设置系统环境变量。...

    centos 5下 LAMP 的安装配置

    在开始之前,我们先来了解一下 LAMP 架构的基本概念:LAMP 是一个开源软件组合,由 Linux(操作系统)、Apache(Web 服务器)、MySQL(数据库管理系统)和 PHP(脚本语言)四部分组成,常用于搭建 Web 应用服务。...

    常用的的jar

    Java Archive (JAR) 文件是Java平台上的一个标准文件格式,用于封装多个类文件、资源文件和其他相关数据,便于分发和执行。"常用的jar"这个主题涉及到的是在开发Java应用时经常使用的JAR库,这些库提供了各种功能,...

    [JAVA网络编程 (第3版)].哈诺德.扫描版.pdf

    - **并发工具类**:如java.util.concurrent包下的Semaphore、CountDownLatch、CyclicBarrier等,用于协调多个线程间的同步。 4. **Java NIO(非阻塞I/O)**: - **通道(Channel)和缓冲区(Buffer)**:NIO引入...

    Java入门级demo

    - 日志框架:如Log4j或java.util.logging,用于记录程序运行日志,便于调试和问题排查。 3. Apache相关: - Tomcat:这是一个流行的开源Java应用服务器,用于部署和运行Java Web应用。 - Maven:项目管理和构建...

    精选_毕业设计_基于java的多人聊天程序课程设计_完整源码

    Java中的`java.net`包提供了Socket和ServerSocket类,用于实现客户端和服务器之间的通信。 3. **并发处理**:为了支持多人同时在线聊天,程序需要处理多个并发连接。Java提供了线程(Thread)和并发工具类(如`java...

    基于java的一个支持servlet的web服务器.zip

    本项目“基于Java的一个支持Servlet的Web服务器”是一个实现此类功能的自定义服务器,允许开发者在不依赖大型Web容器(如Apache Tomcat或Jetty)的情况下运行Servlet应用。 1. **Java基础知识**:Java是一种跨平台...

    JAVA笔试题目及JAVA常考知识点总结.pdf

    - 线程的调度由线程池管理,Java的java.util.concurrent包提供了强大的线程池支持。 - Java提供了synchronized和volatile关键字用于处理线程同步问题。 7. Java网络编程 - Java通过***包提供了对网络编程的支持...

    apr-1.4.6.tar.bz2

    安装Apache的时候,为什么要安装apr和apr-util呢 要测APR给tomcat带来的好处最好的方法是在慢速网络上(模拟Internet),将Tomcat线程数开到300以上的水平,然后模拟一大堆并发请求。如果不配APR,基本上300个线程狠...

    dist:我们在fhnw的dist解决方案

    2. **Java Network Programming**: Java的`java.net`包提供了Socket编程接口,可以创建客户端和服务器应用程序,这对于构建分布式系统至关重要。 3. **Java多线程**: Java内置对多线程的支持,允许并发执行任务,这...

    JAVA上百实例源码以及开源项目源代码

    简单聊天软件CS模式 2个目标文件 一个简单的CS模式的聊天软件,用socket实现,比较简单。 凯撒加密解密程序 1个目标文件 1、程序结构化,用函数分别实现 2、对文件的加密,解密输出到文件 利用随机函数抽取幸运数字 ...

    DistributedSystems

    Java的负载均衡策略可以通过Servlet容器(如Tomcat)的配置实现,也可以使用开源库如Apache Ignite或Netflix Ribbon。 4. **容错与复制**:为了保证高可用性,分布式系统通常需要具备故障恢复和数据复制的能力。...

Global site tag (gtag.js) - Google Analytics