tomcat 并发 随笔 3-Acceptor

0. Acceptor

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.apache.tomcat.util.net;

import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jni.Error;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;

public class Acceptor<U> implements Runnable {

    private static final StringManager sm = StringManager.getManager(Acceptor.class);

    private static final int INITIAL_ERROR_DELAY = 50;
    private static final int MAX_ERROR_DELAY = 1600;

    private final AbstractEndpoint<?,U> endpoint;
    private String threadName;
	
	// Acceptor的状态
    protected volatile AcceptorState state = AcceptorState.NEW;
	public enum AcceptorState {
        NEW, RUNNING, PAUSED, ENDED
    }


    public Acceptor(AbstractEndpoint<?,U> endpoint) {
        this.endpoint = endpoint;
    }


    @Override
    public void run() {

        int errorDelay = 0;

        // Loop until we receive a shutdown command
        while (endpoint.isRunning()) {

			// 自旋挂起
            // Loop if endpoint is paused
            while (endpoint.isPaused() && endpoint.isRunning()) {
                state = AcceptorState.PAUSED;
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    // Ignore
                }
            }

            if (!endpoint.isRunning()) {
                break;
            }
            state = AcceptorState.RUNNING;

            try {
				// 这个栅栏在之前初始化的时候说到了
				// 控制最大连接数的
				// 栅栏使用tomcat封装(基于 AtomicLong)后的类型 LimitLatch
				//	org.apache.tomcat.util.net.AbstractEndpoint#countUpOrAwaitConnection
				//		org.apache.tomcat.util.threads.LimitLatch#countUpOrAwait
				// 			java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireSharedInterruptibly
				//			AQS的acquireSharedInterruptibly会尝试直到获取后返回(可以被中断的)
				// 从该方法命名可以看出,连接超出将等待
                //if we have reached max connections, wait
                endpoint.countUpOrAwaitConnection();

                // Endpoint might have been paused while waiting for latch
                // If that is the case, don't accept new connections
                if (endpoint.isPaused()) {
                    continue;
                }

                U socket = null;
                try {
					// 接收1个socket连接
					// 初始化的时候,我们设置了阻塞
					// 内部就 serverSocketChannel.accept()
                    // Accept the next incoming connection from the server
                    // socket
                    socket = endpoint.serverSocketAccept();
                } catch (Exception ioe) {
                    // We didn't get a socket
                    endpoint.countDownConnection();
                    if (endpoint.isRunning()) {
                        // Introduce delay if necessary
                        errorDelay = handleExceptionWithDelay(errorDelay);
                        // re-throw
                        throw ioe;
                    } else {
                        break;
                    }
                }
				// 到这里,表示serverSocket已经成功接收了连接
                // Successful accept, reset the error delay
                errorDelay = 0;

                // Configure the socket
                if (endpoint.isRunning() && !endpoint.isPaused()) {
					// step into ...
					// 根据方法命名,这里应该只是简单设置了socket连接状态,并无业务处理的调用
					// acceptor也因而可以尽快的接收下一个连接的到来
                    // setSocketOptions() will hand the socket off to
                    // an appropriate processor if successful
                    if (!endpoint.setSocketOptions(socket)) {
                        endpoint.closeSocket(socket);
                    }
                } else {
                    endpoint.destroySocket(socket);
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                String msg = sm.getString("endpoint.accept.fail");
                // APR specific.
                // Could push this down but not sure it is worth the trouble.
                if (t instanceof Error) {
                    Error e = (Error) t;
                    if (e.getError() == 233) {
                        // Not an error on HP-UX so log as a warning
                        // so it can be filtered out on that platform
                        // See bug 50273
                        log.warn(msg, t);
                    } else {
                        log.error(msg, t);
                    }
                } else {
                        log.error(msg, t);
                }
            }
        }
        state = AcceptorState.ENDED;
    }
}

1. LimitLatch

	// org.apache.tomcat.util.threads.LimitLatch
	public class LimitLatch {

		private class Sync extends AbstractQueuedSynchronizer {
			private static final long serialVersionUID = 1L;

			public Sync() {
			}

			@Override
			protected int tryAcquireShared(int ignored) {
				// private final AtomicLong count;
				long newCount = count.incrementAndGet();
				if (!released && newCount > limit) {
					// Limit exceeded
					count.decrementAndGet();
					return -1;
				} else {
					return 1;
				}
			}

	}

2. NioEndpoint#setSocketOptions

	// org.apache.tomcat.util.net.NioEndpoint#setSocketOptions
    @Override
    protected boolean setSocketOptions(SocketChannel socket) {
        // Process the connection
        try {
			// clientSocketChannel设置非阻塞调用
            //disable blocking, APR style, we are gonna be polling it
            socket.configureBlocking(false);
			
			// 得到 clientSocket
            Socket sock = socket.socket();
            socketProperties.setProperties(sock);

			// private SynchronizedStack<NioChannel> nioChannels;
			// NioChannel是tomcat封装的类型,内部维护了1个SocketChannel
			// 这个nioChannel也是提前初始化好的channel实例
			// 弹出1个channel来使用
			// 这里弹出的处理:stack[index--] = null;
            NioChannel channel = nioChannels.pop();
            if (channel == null) {
				// 不存在则直接实例化1个channel
                SocketBufferHandler bufhandler = new SocketBufferHandler(
						// 这个 handler 还维护了读、写的能力
                        socketProperties.getAppReadBufSize(),
                        socketProperties.getAppWriteBufSize(),
                        socketProperties.getDirectBuffer());
                if (isSSLEnabled()) {
                    channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
                } else {
                    channel = new NioChannel(socket, bufhandler);
                }
            } else {
				// 将clientSocketChannel放到NioChannel中被委托
                channel.setIOChannel(socket);
                channel.reset();
            }
			
			// 将channel注册到其中的1个poller上
			// Math.abs(pollerRotater.incrementAndGet()) % pollers.length;
            getPoller0().register(channel);
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            try {
                log.error("",t);
            } catch (Throwable tt) {
                ExceptionUtils.handleThrowable(tt);
            }
            // Tell to close the socket
            return false;
        }
        return true;
    }
  • Acceptor 在不间断的循环中控制连接数量
  • 并将可处理的Channel添加到Poller的事件队列中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肯尼思布赖恩埃德蒙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值