tomcat连接器

        连接器是tomcat核心组件之一,负责处理外部连接响应目前连接器支持HTTP/1.1、HTTP/2、AJP三种协议,支持NIO、NIO2、APR三种IO模型。连接器与另一核心组件容器构成service组件,通过配置多个service可实现不同协议不同端口访问。

        连接器主要工作流程可分为以下几步:

        1.监听端口,接受请求获取网络字节流。

        2.解析字节流,生成tomcat request对象。

        3.将tomcat request对象转为servlet request对象。

        4.调用servlet容器。

        5.将返回的servlet response转为tomcat response。

        6.tomcat response 转为网络字节流

        7.返回客户端。

        在tomcat中这些请求由三个组件endpoint、processor 和 adapter实现。其中endpoint负责监听端口,对客户端请求/响应进行处理。processor网络字节流与tomcat对象之间转换。adapter是典型的适配器模式负责将不同协议的tomcat对象与servlet对象间转换,然后调用容器进行处理。tomcat使用这三个组件进行解耦,并达到IO模型与通讯协议自由组合的效果。

        下面通过源码验证以上推论以HTTP/1.1协议,NIO模型为例。

Connector类为入口,创建不同ProtocolHandler

  public Connector(String protocol) {
        boolean apr = AprStatus.getUseAprConnector() && AprStatus.isInstanceCreated()
                && AprLifecycleListener.isAprAvailable();
        ProtocolHandler p = null;
        try {
            p = ProtocolHandler.create(protocol, apr);
        } catch (Exception e) {
            log.error(sm.getString(
                    "coyoteConnector.protocolHandlerInstantiationFailed"), e);
        }
        if (p != null) {
            protocolHandler = p;
            protocolHandlerClassName = protocolHandler.getClass().getName();
        } else {
            protocolHandler = null;
            protocolHandlerClassName = protocol;
        }
        // Default for Connector depends on this system property
        setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
    }

ProtocolHandler.create(protocol, apr)通过指定的protocol创建不同协议与io模型组合处理器

 public static ProtocolHandler create(String protocol, boolean apr)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
        if (protocol == null || "HTTP/1.1".equals(protocol)
                || (!apr && org.apache.coyote.http11.Http11NioProtocol.class.getName().equals(protocol))
                || (apr && org.apache.coyote.http11.Http11AprProtocol.class.getName().equals(protocol))) {
            if (apr) {
                return new org.apache.coyote.http11.Http11AprProtocol();
            } else {
                return new org.apache.coyote.http11.Http11NioProtocol();
            }
        } else if ("AJP/1.3".equals(protocol)
                || (!apr && org.apache.coyote.ajp.AjpNioProtocol.class.getName().equals(protocol))
                || (apr && org.apache.coyote.ajp.AjpAprProtocol.class.getName().equals(protocol))) {
            if (apr) {
                return new org.apache.coyote.ajp.AjpAprProtocol();
            } else {
                return new org.apache.coyote.ajp.AjpNioProtocol();
            }
        } else {
            // Instantiate protocol handler
            Class<?> clazz = Class.forName(protocol);
            return (ProtocolHandler) clazz.getConstructor().newInstance();
        }
    }

进入Http11NioProtocol,Http11NioProtocol在初始化是创建NioEndpoint()对象

   public Http11NioProtocol() {
        super(new NioEndpoint());
    }

首先关注NioEndpoint中bind方法


    /**
     * Initialize the endpoint.
     */
    @Override
    public void bind() throws Exception {
        // 该方法创建完成端口监听
        initServerSocket();

        setStopLatch(new CountDownLatch(1));

        // Initialize SSL if needed
        initialiseSsl();
    }




protected void initServerSocket() throws Exception {
// 截取部分代码
   serverSock = ServerSocketChannel.open();
            socketProperties.setProperties(serverSock.socket());
            InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
// ServerSocketChannel绑定对应端口,通过getAcceptCount设置队列长度,当应用程序处理不过来时
// 请求会先放入队列中
            serverSock.bind(addr, getAcceptCount());
        }
// 设置为阻塞式通过accept()接受请求,并将请求翻入Poller的队列中
        serverSock.configureBlocking(true); //mimic APR behavior
    }

Poller实现Runnable,会不断的查询是否有请求进入队列。当获取到队列中最新请求会调用processor组件进行处理

 
    @Override
        public void run() {
            // Loop until destroy() is called
            while (true) {
                    if (!close) {
                        hasEvents = events();
                        if (wakeupCounter.getAndSet(-1) > 0) {
                            // If we are here, means we have other stuff to do
                            // Do a non blocking select
                            keyCount = selector.selectNow();
                        } else {
                            keyCount = selector.select(selectorTimeout);
                        }


                while (iterator != null && iterator.hasNext()) {
                    SelectionKey sk = iterator.next();
                    iterator.remove();
                    NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
                    // Attachment may be null if another thread has called
                    // cancelledKey()
                    if (socketWrapper != null) {
                    // 执行处理
                        processKey(sk, socketWrapper);
                    }
                }
                    }

processor将请求放入线程池中

protected void processKey(SelectionKey sk, NioSocketWrapper socketWrapper) {
                            if (sk.isReadable()) {
                                if (socketWrapper.readOperation != null) {
                                    if (!socketWrapper.readOperation.process()) {
                                        closeSocket = true;
                                    }
                                } else if (socketWrapper.readBlocking) {
                                    synchronized (socketWrapper.readLock) {
                                        socketWrapper.readBlocking = false;
                                        socketWrapper.readLock.notify();
                                    }
                                } else if (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) {
                                    closeSocket = true;
                                }
                            }
                            if (!closeSocket && sk.isWritable()) {
                                if (socketWrapper.writeOperation != null) {
                                    if (!socketWrapper.writeOperation.process()) {
                                        closeSocket = true;
                                    }
                                } else if (socketWrapper.writeBlocking) {
                                    synchronized (socketWrapper.writeLock) {
                                        socketWrapper.writeBlocking = false;
                                        socketWrapper.writeLock.notify();
                                    }
                                } else if (!processSocket(socketWrapper, SocketEvent.OPEN_WRITE, true)) {
                                    closeSocket = true;
                                }
                            }
                     
}

线程池调用NioEndpoint中doRun执行任务,在执行任务时使用适配器将不同tomcat request转为servlet request并调用容器进行处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值