分析I/O操作首先我们从NioSocketAcceptor.java这个类为入口进行一步一步的跟踪分析。
首先我们了解NioSocketAcceptor.java:
AbstractPollingIoAcceptor,AbstractIoAcceptor,AbstractIoService是它的基类。
在NioSocketAcceptor中我们先看下DefaultSocketSessionConfig类,DefaultSocketSessionConfig的基类示AbstractIoSessionConfig,
AbstractIoSessionConfig是一些基本参数的设置,如:数据的缓冲区大小 ,超时等:
private int minReadBufferSize = 64;
private int readBufferSize = 2048;
private int maxReadBufferSize = 65536;
private int idleTimeForRead;
private int idleTimeForWrite;
private int idleTimeForBoth;
private int writeTimeout = 60;
private boolean useReadOperation;
private int throughputCalculationInterval = 3;
protected AbstractIoSessionConfig() {
// Do nothing
}
/**
* {@inheritDoc}
*/
public final void setAll(IoSessionConfig config) {
if (config == null) {
throw new NullPointerException("config");
}
setReadBufferSize(config.getReadBufferSize());
setMinReadBufferSize(config.getMinReadBufferSize());
setMaxReadBufferSize(config.getMaxReadBufferSize());
setIdleTime(IdleStatus.BOTH_IDLE, config.getIdleTime(IdleStatus.BOTH_IDLE));
setIdleTime(IdleStatus.READER_IDLE, config.getIdleTime(IdleStatus.READER_IDLE));
setIdleTime(IdleStatus.WRITER_IDLE, config.getIdleTime(IdleStatus.WRITER_IDLE));
setWriteTimeout(config.getWriteTimeout());
setUseReadOperation(config.isUseReadOperation());
setThroughputCalculationInterval(config.getThroughputCalculationInterval());
doSetAll(config);
}
再回到NioSocketAcceptor的基类AbstractPollingIoAcceptor,AbstractPollingIoAcceptor将NIO底层选择器打开:
private AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
Executor executor, IoProcessor<T> processor,
boolean createdProcessor) {
super(sessionConfig, executor);
if (processor == null) {
throw new NullPointerException("processor");
}
this.processor = processor;
this.createdProcessor = createdProcessor;
try {
// Initialize the selector
init();
// The selector is now ready, we can switch the
// flag to true so that incoming connection can be accepted
selectable = true;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeIoException("Failed to initialize.", e);
} finally {
if (!selectable) {
try {
destroy();
} catch (Exception e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
}
}
}
}
然后我们跟踪AbstractIoAcceptor类,我们查看它的bind(SocketAddress localAddress)
/**
* {@inheritDoc}
*/
public final void bind(SocketAddress localAddress) throws IOException {
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
List<SocketAddress> localAddresses = new ArrayList<SocketAddress>(1);
localAddresses.add(localAddress);
bind(localAddresses);
}
/**
* {@inheritDoc}
*/
public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException {
if (isDisposing()) {
throw new IllegalStateException("Already disposed.");
}
if (localAddresses == null) {
throw new NullPointerException("localAddresses");
}
List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
for (SocketAddress a: localAddresses) {
checkAddressType(a);
localAddressesCopy.add(a);
}
if (localAddressesCopy.isEmpty()) {
throw new IllegalArgumentException("localAddresses is empty.");
}
boolean activate = false;
synchronized (bindLock) {
if (boundAddresses.isEmpty()) {
activate = true;
}
if (getHandler() == null) {
throw new IllegalStateException("handler is not set.");
}
try {
boundAddresses.addAll(bindInternal(localAddressesCopy));
} catch (IOException e) {
throw e;
} catch (RuntimeException e) {
throw e;
} catch (Throwable e) {
throw new RuntimeIoException(
"Failed to bind to: " + getLocalAddresses(), e);
}
}
if (activate) {
getListeners().fireServiceActivated();
}
}
我们重点分析下bindInternal这个方法,他是I/O核心地方:
这里面有3个需要队列:
//注册队列
private final Queue<AcceptorOperationFuture> registerQueue =
new ConcurrentLinkedQueue<AcceptorOperationFuture>();
//取消注册队列
private final Queue<AcceptorOperationFuture> cancelQueue =
new ConcurrentLinkedQueue<AcceptorOperationFuture>();
//本地地址到服务器socket的映射表
private final Map<SocketAddress, H> boundHandles =
Collections.synchronizedMap(new HashMap<SocketAddress, H>());
/**
* {@inheritDoc}
*/
@Override
protected final Set<SocketAddress> bindInternal(
List<? extends SocketAddress> localAddresses) throws Exception {
// Create a bind request as a Future operation. When the selector
// have handled the registration, it will signal this future.
//注册请求
AcceptorOperationFuture request = new AcceptorOperationFuture(localAddresses);
// adds the Registration request to the queue for the Workers
// to handle
//加入注册队列中,等待Acceptor处理
registerQueue.add(request);
// creates the Acceptor instance and has the local
// executor kick it off.
startupAcceptor();
// As we just started the acceptor, we have to unblock the select()
// in order to process the bind request we just have added to the
// registerQueue.
//中断select方法的,当注册队列或取消注册队列发生变化时需要调用
wakeup();
// Now, we wait until this request is completed.
request.awaitUninterruptibly();
if (request.getException() != null) {
throw request.getException();
}
// Update the local addresses.
// setLocalAddresses() shouldn't be called from the worker thread
// because of deadlock.
// 更新本地绑定地址
Set<SocketAddress> newLocalAddresses = new HashSet<SocketAddress>();
for (H handle:boundHandles.values()) {
newLocalAddresses.add(localAddress(handle));
}
return newLocalAddresses;
}
/**
* Starts the inner Acceptor thread.
*/
private void startupAcceptor() {
if (!selectable) {
registerQueue.clear();
cancelQueue.clear();
flushingSessions.clear();
}
synchronized (lock) {
if (acceptor == null) {
//开启执行线程
acceptor = new Acceptor();
executeWorker(acceptor);
}
}
}
/**
* This private class is used to accept incoming connection from
* clients. It's an infinite loop, which can be stopped when all
* the registered handles have been removed (unbound).
*/
private class Acceptor implements Runnable {
public void run() {
int nHandles = 0;
lastIdleCheckTime = System.currentTimeMillis();
while (selectable) {
try {
//检测是否有SelectionKey已经可以被处理了
int selected = select();
//注册服务器sockets句柄,这样做的目的是将Selector的状态置于
//OP_ACCEPT,并绑定到所监听的端口上,表明接受了可以接收的来自客户端的连接请求
nHandles += registerHandles();
if (selected > 0) {
//处理可以被处理的SelectionKey状态为OP_ACCEPT的服务器socket句柄集(即真正处理来自客户端的连接请求)
processReadySessions(selectedHandles());
}
long currentTime = System.currentTimeMillis();
flushSessions(currentTime);
//检查是否有取消连接的客户端请求
nHandles -= unregisterHandles();
notifyIdleSessions(currentTime);
if (nHandles == 0) {
synchronized (lock) {
if (registerQueue.isEmpty() && cancelQueue.isEmpty()) {
//完成工作
acceptor = null;
break;
}
}
}
} catch (Exception e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
try {
//线程休眠一秒
Thread.sleep(1000);
} catch (InterruptedException e1) {
}
}
}
if (selectable && isDisposing()) {
//释放资源
selectable = false;
try {
destroy();
} catch (Exception e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
} finally {
disposalFuture.setValue(true);
}
}
}
}
@SuppressWarnings("unchecked")
private void processReadySessions(Iterator<H> handles) {
//处理来自客户端的连接请求
while (handles.hasNext()) {
H h = handles.next();
handles.remove();
try {
if (isReadable(h)) {
//selector读操作
readHandle(h);
}
if (isWritable(h)) {
//selector写操作
for (IoSession session : getManagedSessions().values()) {
scheduleFlush((T) session);
}
}
} catch (Throwable t) {
ExceptionMonitor.getInstance().exceptionCaught(t);
}
}
}