tomcat源码分析(4) Http11NioProtocol分析 二

本篇文章我详细分析一下Http11NioProtocol

关键的代码部分:
public void bind() throws Exception {
        serverSock = ServerSocketChannel.open();
        socketProperties.setProperties(serverSock.socket());
        InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
        serverSock.socket().bind(addr,getAcceptCount());
        serverSock.configureBlocking(true); //mimic APR behavior
        // Initialize thread count defaults for acceptor, poller
        if (acceptorThreadCount == 0) {
            // FIXME: Doesn't seem to work that well with multiple accept threads
            acceptorThreadCount = 1;
        }
        if (pollerThreadCount <= 0) {
            //minimum one poller thread
            pollerThreadCount = 1;
        }
        setStopLatch(new CountDownLatch(pollerThreadCount));
        // Initialize SSL if needed
        initialiseSsl();
        selectorPool.open();
    }
public void startInternal() throws Exception {
        if (!running) {
            running = true;
            paused = false;
            processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                    socketProperties.getProcessorCache());
            eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                            socketProperties.getEventCache());
            nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                    socketProperties.getBufferPool());
            // Create worker collection
            if ( getExecutor() == null ) {
                createExecutor();
            }
            initializeConnectionLatch();
            // Start poller threads
            pollers = new Poller[getPollerThreadCount()];
            for (int i=0; i

Acceptor: 作用接受客户端的请求进行封装,并将类型为OP_REGISTER的PollerEvent添加到Poller(轮训器)的SynchronizedQueue实现Runnable接口,开起线程的数量由acceptorThreadCount控制

1.接受客户端的连接 SocketChannel socket = serverSock.accept();
2.对于连接数进行限流 LimitLatch.countUpOrAwait() and LimitLatch.countDown() 参数设置:setMaxConnections
3.对SocketChannel进行socket设置并包装为NioChannel

protected boolean setSocketOptions(SocketChannel socket) {
    // Process the connection
    try {
        //disable blocking, APR style, we are gonna be polling it
        socket.configureBlocking(false);
        Socket sock = socket.socket();
        socketProperties.setProperties(sock);
        //nioChannels 为NioChannel对象池
        NioChannel channel = nioChannels.pop();
        if (channel == null) {
            SocketBufferHandler bufhandler = new SocketBufferHandler(
                    socketProperties.getAppReadBufSize(),
                    socketProperties.getAppWriteBufSize(),
                    socketProperties.getDirectBuffer());
            if (isSSLEnabled()) {
                channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
            } else {
                channel = new NioChannel(socket, bufhandler);
            }
        } else {
            channel.setIOChannel(socket);
            channel.reset();
        }
        getPoller0().register(channel);
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        // Tell to close the socket
        return false;
    }
    return true;
}

4.按顺序获取轮询器Poller并注册NioChannel

getPoller0().register(channel);
public void register(final NioChannel socket) {
    socket.setPoller(this);
    NioSocketWrapper ka = new NioSocketWrapper(socket, NioEndpoint.this);
    socket.setSocketWrapper(ka);
    ka.setPoller(this);
    ka.setReadTimeout(getSocketProperties().getSoTimeout());
    ka.setWriteTimeout(getSocketProperties().getSoTimeout());
    ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
    ka.setSecure(isSSLEnabled());
    ka.setReadTimeout(getConnectionTimeout());
    ka.setWriteTimeout(getConnectionTimeout());
    PollerEvent r = eventCache.pop();
    ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
    if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
    else r.reset(socket,ka,OP_REGISTER);
    addEvent(r);
}

PollerEvent:作为Poller中SynchronizedQueue的单元,实现Runnable接口,作用是将SocketChannel注册到Poller的Selector上监听socket是否可读

socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper);

Poller(轮训器):实现Runnable接口,开起线程的数量由pollerThreadCount控制,构造函数为this.selector = Selector.open();由一个SynchronizedQueue构成,流程如下:

1.从SynchronizedQueue获取PollerEvent,执行PollerEvent.run方法,监听读事件
2.selector.select()得到可以进行操作的SelectionKeys,遍历进行处理processKey(selectionKey, attachment);
3.判断selectionKey的读写就绪,并封装为SocketProcessorBase,丢进线程池进行处理

processSocket(attachment, SocketEvent.OPEN_READ, true)
public boolean processSocket(SocketWrapperBase socketWrapper,
            SocketEvent event, boolean dispatch) {//dispatch是否需要线程池中执行
        try {
            if (socketWrapper == null) {
                return false;
            }
            SocketProcessorBase sc = processorCache.pop();
            if (sc == null) {
                sc = createSocketProcessor(socketWrapper, event);
            } else {
                sc.reset(socketWrapper, event);
            }
            Executor executor = getExecutor();
            if (dispatch && executor != null) {
                executor.execute(sc);
            } else {
                sc.run();
            }
        } catch (RejectedExecutionException ree) {
            getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
            return false;
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            // This means we got an OOM or similar creating a thread, or that
            // the pool and its queue are full
            getLog().error(sm.getString("endpoint.process.fail"), t);
            return false;
        }
        return true;
    }

SocketProcessor extends SocketProcessorBase implements Runnable

1.判断是否完成TLS握手,我们使用是非https协议,默认返回true
2.state = getHandler().process(socketWrapper, event); 这个getHandler()返回的是ConnectionHandler,ConnectionHandler创建代码:

public abstract class AbstractHttp11Protocol extends AbstractProtocol {
...
public AbstractHttp11Protocol(AbstractEndpoint endpoint) {
    super(endpoint);
    setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
    ConnectionHandler cHandler = new ConnectionHandler<>(this);
    setHandler(cHandler);
    getEndpoint().setHandler(cHandler);
}
...

3.缓存中获取Processor,如果不存在则创建

protected Processor createProcessor() {
    Http11Processor processor = new Http11Processor(getMaxHttpHeaderSize(),
            getAllowHostHeaderMismatch(), getRejectIllegalHeaderName(), getEndpoint(),
            getMaxTrailerSize(), allowedTrailerHeaders, getMaxExtensionSize(),
            getMaxSwallowSize(), httpUpgradeProtocols, getSendReasonPhrase(),
            relaxedPathChars, relaxedQueryChars);
    processor.setAdapter(getAdapter());
    processor.setMaxKeepAliveRequests(getMaxKeepAliveRequests());
    processor.setConnectionUploadTimeout(getConnectionUploadTimeout());
    processor.setDisableUploadTimeout(getDisableUploadTimeout());
    processor.setCompressionMinSize(getCompressionMinSize());
    processor.setCompression(getCompression());
    processor.setNoCompressionUserAgents(getNoCompressionUserAgents());
    processor.setCompressibleMimeTypes(getCompressibleMimeTypes());
    processor.setRestrictedUserAgents(getRestrictedUserAgents());
    processor.setMaxSavePostSize(getMaxSavePostSize());
    processor.setServer(getServer());
    processor.setServerRemoveAppProvidedValues(getServerRemoveAppProvidedValues());
    return processor;
}

4.执行processor.process(wrapper, status);Http11Processor从读取的流拿到数据封装为request,调用Adapter.service就业务数据写入response,调用endRequest就response写入数据流

request:
GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36
Postman-Token: 7e9c95a5-9333-c2c5-6306-96f4b90cab00
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

response:
HTTP/1.1 200 
Transfer-Encoding: chunked
Date: Fri, 31 Aug 2018 06:19:15 GMT
Connection: close

f
hello world !!!
0

.

5.执行完成之后close连接,这里为什么关闭:浏览器默认使用http1.1是keep-alive,通过服务器设置配置中maxKeepAliveRequests=1,设置服务器为不支持keep-alive,生产对于keep-alive还是要根据实际需求使用,默认tomcat有对于keep-alive有策略,当持有的数量大于75%时,会主要关闭连接

你可能感兴趣的:(tomcat源码分析(4) Http11NioProtocol分析 二)