【Tomcat9源码分析】NIO连接器实现

转载请注明出处:http://blog.csdn.net/linxdcn/article/details/73527465


1 概述

如果你对Tomcat的整个框架、组件、请求流程不熟悉,建议你先阅读以下3篇Tomcat概述性的文章,再来看以下文章:

【Tomcat9源码分析】组件与框架概述
【Tomcat9源码分析】生命周期、启动、停止概述
【Tomcat9源码分析】请求过程概述

老版本Tomcat的Connector有三种运行模式bio、nio、apr,但在Tomcat9中只找到了nio、apr,bio模式因为效率太低已经被淘汰了。

  • nio模式是基于Java nio包实现的,能提供非阻塞I/O操作,拥有比传统I/O操作(bio)更好的并发运行性能。在Tomcat9中是默认模式。
  • apr模式(Apache Portable Runtime/Apache可移植运行时),是Apache HTTP服务器的支持库。可以简单地理解为Tomcat将以JNI的形式调用Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作,从而大大地提高Tomcat对静态文件的处理性能

重温一下Connector组件的工作方式,下面对分析每一步骤。




2 Connector初始化与启动

public Connector(String protocol) {

    if ("HTTP/1.1".equals(protocol) || protocol == null) {
        if (aprConnector) {
            protocolHandlerClassName = "org.apache.coyote.http11.Http11AprProtocol";
        } else {
            // 默认class name
            protocolHandlerClassName = "org.apache.coyote.http11.Http11NioProtocol";
        }
    } 

    ProtocolHandler p = null;
    try {
        // 采用反射机制创建Http11NioProtocol对象
        Class<?> clazz = Class.forName(protocolHandlerClassName);
        p = (ProtocolHandler) clazz.getConstructor().newInstance();
    }
}
  1. Connector构造函数,默认采用org.apache.coyote.http11.Http11NioProtocol
  2. 新建CoyoteAdapter,并调用setAdapter()(未给出代码)
  3. 启动Http11NioProtocol对象

3 Http11NioProtocol启动

// 新建NioEndPoint
public Http11NioProtocol() {
    super(new NioEndpoint());

    // 父类的构造函数大致如下
    // this.endpoint = endpoint;
    // ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
    // setHandler(cHandler);
    // getEndpoint().setHandler(cHandler);
}

// 启动NioEndPoint
public void start() throws Exception {
    endpoint.start();
}

主要创建了两个对象:

  1. 新建NioEndPoint:负责接收请求
  2. 新建ConnectionHandler:负责处理请求
  3. 启动NioEndPoint

4 NioEndPoint

NioEndPoint包含了三个组件

  • Acceptor:负责监听请求
  • Poller:接收监听到的请求socket
  • SocketProcessor(Worker):处理socket,本质上委托ConnectionHandler处理
public class NioEndpoint {

    public void startInternal() throws Exception {
        // NioEndPoint最核心之处,启动Acceptor
        startAcceptorThreads();
    }

    protected final void startAcceptorThreads() {
        int count = getAcceptorThreadCount();
        acceptors = new ArrayList<>(count);

        // 创建了Acceptor线程池
        for (int i = 0; i < count; i++) {
            Acceptor<U> acceptor = new Acceptor<>(this);
            String threadName = getName() + "-Acceptor-" + i;
            acceptor.setThreadName(threadName);
            acceptors.add(acceptor);
            Thread t = new Thread(acceptor, threadName);
            t.setPriority(getAcceptorThreadPriority());
            t.setDaemon(getDaemon());
            t.start();
        }
    }
}

NioEndPoint的启动,最主要是创建Acceptor线程池,同时监听新请求。

4.1 Acceptor
public class Acceptor<U> implements Runnable {

    @Override
    public void run() {

        // 循环遍历直到接收到关闭命令 
        while (endpoint.isRunning()) {

            try {
                U socket = null;
                try {
                    // 1 接收新的请求,注意!这里采用的阻塞模式,多个Acceptor线程同时阻塞在此
                    socket = endpoint.serverSocketAccept();
                } catch (Exception ioe) {
                    // ...省略
                }

                // 2 设置socket,即调用NioEndPoint的setSocketOptions
                // 将socket添加到Poller池中某个poller
                if (endpoint.isRunning() && !endpoint.isPaused()) {
                    if (!endpoint.setSocketOptions(socket)) {
                        endpoint.closeSocket(socket);
                    }
                } else {
                    endpoint.destroySocket(socket);
                }
            } catch (Throwable t) {
                // ...省略
            }
        }
    }
}

public class NioEndpoint {

    protected boolean setSocketOptions(SocketChannel socket) {
        // Process the connection
        try {
            // 3 将SocketChannel设置为非阻塞模式
            socket.configureBlocking(false);
            // 3 设置Socket参数值,如Socket发送、接收的缓存大小、心跳检测等
            Socket sock = socket.socket();
            socketProperties.setProperties(sock);

            // NioChannel是SocketChannel的一个的包装类,NioEndPoint维护了一个NioChannel池
            NioChannel channel = nioChannels.pop();
            if (channel == null) {
                // 如果channel为空,则新建一个
            } else {
                channel.setIOChannel(socket);
                channel.reset();
            }

            // 4 从Poller池取一个poller,将NioChannel交给poller
            getPoller0().register(channel);
        } catch (Throwable t) {
            // ...省略
        }
        return true;
    }
}
  1. 调用NioEndPointserverSocketAccept接收新的请求,注意!这里采用的阻塞模式,多个Acceptor线程同时阻塞在此
  2. 设置接收到的socket,即调用NioEndPoint的setSocketOptions
  3. 将SocketChannel设置为非阻塞模式,并且设置Socket参数值,如Socket发送、接收的缓存大小、心跳检测等
  4. 将SocketChannel包装成NioChannel,并调用Poller池中的某个poller的register()方法,提交给poller
4.2 Poller
public class Poller implements Runnable {

    public void register(final NioChannel socket) {

        // 绑定socket跟poller
        socket.setPoller(this);

        // 获取一个空闲的KeyAttachment对象
        KeyAttachment key = keyCache.poll();   
        final KeyAttachment ka = key!=null?key:new KeyAttachment(socket); 

        // 从Poller的事件对象缓存中取出一个PollerEvent,并用socket初始化事件对象
        PollerEvent r = eventCache.pop();

        // 设置读操作为感兴趣的操作
        ka.interestOps(SelectionKey.OP_READ);
        if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
        else r.reset(socket,ka,OP_REGISTER);

        //加入pollerevent中
        addEvent(r);
    }   
}

register()方法比较简单,把socket与该poller关联,并为socket注册感兴趣的读操作,包装成PollerEvent,添加到PollerEvent池中。

Poller本身是继承Runnable的可执行线程,如下:

public class Poller implements Runnable {

    // 这就是NIO中用到的选择器,可以看出每一个Poller都会关联一个Selector  
    protected Selector selector;  
    // 待处理的事件队列,通过register添加
    protected ConcurrentLinkedQueue<Runnable> events = 
            new ConcurrentLinkedQueue<Runnable>(); 

    @Override
    public void run() {

        while (true) {

            boolean hasEvents = false;

            try {
                if (!close) {
                    hasEvents = events();
                    if (wakeupCounter.getAndSet(-1) > 0) {
                        // 1 wakeupCounter指event的数量,即有event
                        keyCount = selector.selectNow();
                    } else {
                        // 1 无event则调用select阻塞
                        keyCount = selector.select(selectorTimeout);
                    }
                    wakeupCounter.set(0);
                }

            } catch (Throwable x) {
                // ...省略
            }

            //either we timed out or we woke up, process events first
            if ( keyCount == 0 ) hasEvents = (hasEvents | events());

            Iterator<SelectionKey> iterator =
                keyCount > 0 ? selector.selectedKeys().iterator() : null;


            // 2 根据向selector中注册的key遍历channel中已经就绪的keys
            while (iterator != null && iterator.hasNext()) {
                SelectionKey sk = iterator.next();
                NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();

                if (attachment == null) {
                    iterator.remove();
                } else {
                    iterator.remove();
                    // 3 处理就绪key
                    processKey(sk, attachment);
                }
            }
        }
    }

    protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {
        if (sk.isReadable() || sk.isWritable() ) {
            // 在通道上注销对已经发生事件的关注
            unreg(sk, attachment, sk.readyOps());
            boolean closeSocket = false;

            // 读事件
            if (sk.isReadable()) {
                // 3 具体的通道处理逻辑
                if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {
                    closeSocket = true;
                }
            }

            // 写事件
            if (!closeSocket && sk.isWritable()) {
                if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {
                    closeSocket = true;
                }
            }
        }
    }
}

public class NioEndPoint {

    public boolean processSocket(SocketWrapperBase<S> socketWrapper,
            SocketEvent event, boolean dispatch) {
        try {

            // 4 从SocketProcessor池中取出空闲的SocketProcessor,关联socketWrapper
            SocketProcessorBase<S> sc = processorCache.pop();
            if (sc == null) {
                sc = createSocketProcessor(socketWrapper, event);
            } else {
                sc.reset(socketWrapper, event);
            }

            // 4 提交运行SocketProcessor
            Executor executor = getExecutor();
            if (dispatch && executor != null) {
                executor.execute(sc);
            } else {
                sc.run();
            }
        } 
        return true;
    }
}
  1. 调用selectorselect()函数,监听就绪事件
  2. 根据向selector中注册的key遍历channel中已经就绪的keys,并处理key
  3. 处理key对应的channel,调用NioEndPointprocessSocket()
  4. 从SocketProcessor池中取出空闲的SocketProcessor,关联socketWrapper,提交运行SocketProcessor
4.3 SocketProcessor
protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
    @Override
    protected void doRun() {

        // ...省略打断代码
        NioChannel socket = socketWrapper.getSocket();
        SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());

        if (event == null) {
            // 最核心的是调用了ConnectionHandler的process方法
            state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
        } else {
            state = getHandler().process(socketWrapper, event);
        }
    }
}

其实SocketProcessor的代码比较简单,本质上是调用了ConnectionHandlerprocess方法处理socket。


5 ConnectionHandler

protected static class ConnectionHandler<S> implements AbstractEndpoint.Handler<S> {
    @Override
    public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {

        S socket = wrapper.getSocket();

        // 1 获取socket对应的Http11NioProcessor对象,用于http协议的解析
        Processor processor = connections.get(socket);

        // 2 循环解析socket的内容,直到读完
        do {
            state = processor.process(wrapper, status);

            // ...省略超级大一段代码
        } while ( state == SocketState.UPGRADING);
    }
}
  1. 获取socket对应的Http11NioProcessor对象,用于http协议的解析
  2. 循环解析socket的内容,直到读完
  3. 后续就是包装成request和response交给CoyoteAdapter

6 总结

NIO连接器是基于Reactor模式进行设计和开发,Reactor模式基于事件驱动,特别适合处理海量的I/O事件。Reactor模型主要可以分为(详细可参考Netty系列之Netty线程模型):

  • 单线程模型(所有IO操作都在同一个NIO线程上面完成)
  • 多线程模型(有一组NIO线程处理IO操作)
  • 主从多线程模型(服务端用于接收客户端连接的不再是个1个单独的NIO线程,而是一个独立的NIO线程池)

Tomcat属于主从多线程模型,如下图所示:




转载请注明出处:http://blog.csdn.net/linxdcn/article/details/73527465

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
作为一个AI机器人,我并没有能力进行源码分析。但是我可以提供一些关于Tomcat8.5的相关信息。 Tomcat8.5是Apache Tomcat的一个版本,是一个开源的Web容器,使用Java编写,可用于部署Java Web应用程序。Tomcat8.5增加了对Java Servlet 3.1、JavaServer Pages 2.3、Java Unified Expression Language 3.0以及WebSocket 1.1的支持。Tomcat8.5还增强了对SSL/TLS的支持,包括支持ALPN协议和OCSP Stapling。Tomcat8.5还提供了一些新的特性,如异步I/O和NIO2。 在Tomcat8.5源码中,主要涉及到的模块有:Catalina、Coyote、Jasper、WebSocket和Tomcat Native。Catalina模块是Tomcat的核心模块,负责处理HTTP请求和响应。Coyote模块负责处理HTTP请求的接收和响应。Jasper模块负责处理JSP页面的编译和执行。WebSocket模块提供了基于WebSocket协议的通信。Tomcat Native模块提供了对APR(Apache Portable Runtime)库的支持,可以提高Tomcat的性能和可靠性。 在Tomcat8.5源码中,还有一些其他的模块,如:Cluster、Manager、Realm、Valve等。Cluster模块提供了集群支持,可以让多个Tomcat实例共享会话信息。Manager模块提供了Web应用程序管理的功能。Realm模块提供了认证和授权的支持。Valve模块提供了一些过滤器和拦截器,可以对HTTP请求和响应进行处理。 总体来说,Tomcat8.5源码是一个庞大而复杂的项目,需要对JavaWeb开发有一定的了解才能进行源码分析。如果有兴趣了解Tomcat8.5的源码,可以先从官方文档和源码注释入手,逐步深入了解各个模块的实现原理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值