Netty(四):AbstractChannel源码解析
首先我们通过一张继承关系的图来认识下AbstractChannel在Netty中的位置。
除了Comaprable接口来自java自带的包,其他都是Netty包中提供的。
Comparable接口定义了Channel是可以比较的。
AttributeMap接口为Channel提供了绑定其他属性的能力。
这两个接口我们先不去深入了解,主要看ChannelOutboundInvoker。
ChannelOutboundInvoker接口主要提供了与网络链路相关的一些操作以及读写相关的操作,并统一返回了ChannelFuture对象,便于我们可以监听这些操作是否成功。
Channel接口继承了ChannelOutboundInvoker,在接口内部定义了Unsafe接口,增加了一部分方法,一些方法用来判断通道状态,一些方法用来绑定EventLoop,Pipeline和Unsafe对象。
接下来看AbstractChannel的源码:
/*
* Copyright 2012 The Netty Project
*
* The Netty Project 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 io.netty.channel; import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.socket.ChannelOutputShutdownEvent;
import io.netty.channel.socket.ChannelOutputShutdownException;
import io.netty.util.DefaultAttributeMap;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.ThrowableUtil;
import io.netty.util.internal.UnstableApi;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import java.io.IOException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.NoRouteToHostException;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NotYetConnectedException;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException; /**
* A skeletal {@link Channel} implementation.
*/
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel { private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractChannel.class); private static final ClosedChannelException FLUSH0_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace(
new ClosedChannelException(), AbstractUnsafe.class, "flush0()");
private static final ClosedChannelException ENSURE_OPEN_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace(
new ClosedChannelException(), AbstractUnsafe.class, "ensureOpen(...)");
private static final ClosedChannelException CLOSE_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace(
new ClosedChannelException(), AbstractUnsafe.class, "close(...)");
private static final ClosedChannelException WRITE_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace(
new ClosedChannelException(), AbstractUnsafe.class, "write(...)");
private static final NotYetConnectedException FLUSH0_NOT_YET_CONNECTED_EXCEPTION = ThrowableUtil.unknownStackTrace(
new NotYetConnectedException(), AbstractUnsafe.class, "flush0()"); //父Channel
private final Channel parent;
private final ChannelId id;
//Unsafe对象,封装ByteBuf的读写过程
private final Unsafe unsafe;
//关联的Pipeline对象
private final DefaultChannelPipeline pipeline; private final VoidChannelPromise unsafeVoidPromise = new VoidChannelPromise(this, false);
private final CloseFuture closeFuture = new CloseFuture(this); //本地地址和远端地址
private volatile SocketAddress localAddress;
private volatile SocketAddress remoteAddress; //用来处理读写的线程
private volatile EventLoop eventLoop; private volatile boolean registered;
private boolean closeInitiated; /** Cache for the string representation of this channel */
private boolean strValActive;
private String strVal; /**
* Creates a new instance.
*
* @param parent
* the parent of this channel. {@code null} if there's no parent.
*/
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
} /**
* Creates a new instance.
*
* @param parent
* the parent of this channel. {@code null} if there's no parent.
*/
protected AbstractChannel(Channel parent, ChannelId id) {
this.parent = parent;
this.id = id;
unsafe = newUnsafe();
pipeline = newChannelPipeline();
} @Override
public final ChannelId id() {
return id;
} /**
* Returns a new {@link DefaultChannelId} instance. Subclasses may override this method to assign custom
* {@link ChannelId}s to {@link Channel}s that use the {@link AbstractChannel#AbstractChannel(Channel)} constructor.
*/
protected ChannelId newId() {
return DefaultChannelId.newInstance();
} /**
* Returns a new {@link DefaultChannelPipeline} instance.
*/
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
} //通道是否可写
@Override
public boolean isWritable() {
//是否存在输出缓冲区且缓冲区可写
ChannelOutboundBuffer buf = unsafe.outboundBuffer();
return buf != null && buf.isWritable();
} //获取剩余可写空间
@Override
public long bytesBeforeUnwritable() {
ChannelOutboundBuffer buf = unsafe.outboundBuffer();
// isWritable() is currently assuming if there is no outboundBuffer then the channel is not writable.
// We should be consistent with that here.
return buf != null ? buf.bytesBeforeUnwritable() : 0;
} //还需处理多少字节才可写
@Override
public long bytesBeforeWritable() {
ChannelOutboundBuffer buf = unsafe.outboundBuffer();
// isWritable() is currently assuming if there is no outboundBuffer then the channel is not writable.
// We should be consistent with that here.
return buf != null ? buf.bytesBeforeWritable() : Long.MAX_VALUE;
} @Override
public Channel parent() {
return parent;
} @Override
public ChannelPipeline pipeline() {
return pipeline;
} @Override
public ByteBufAllocator alloc() {
return config().getAllocator();
} @Override
public EventLoop eventLoop() {
EventLoop eventLoop = this.eventLoop;
if (eventLoop == null) {
throw new IllegalStateException("channel not registered to an event loop");
}
return eventLoop;
} @Override
public SocketAddress localAddress() {
SocketAddress localAddress = this.localAddress;
if (localAddress == null) {
try {
this.localAddress = localAddress = unsafe().localAddress();
} catch (Throwable t) {
// Sometimes fails on a closed socket in Windows.
return null;
}
}
return localAddress;
} /**
* @deprecated no use-case for this.
*/
@Deprecated
protected void invalidateLocalAddress() {
localAddress = null;
} //获取远端地址
@Override
public SocketAddress remoteAddress() {
SocketAddress remoteAddress = this.remoteAddress;
if (remoteAddress == null) {
try {
this.remoteAddress = remoteAddress = unsafe().remoteAddress();
} catch (Throwable t) {
// Sometimes fails on a closed socket in Windows.
return null;
}
}
return remoteAddress;
} /**
* @deprecated no use-case for this.
*/
@Deprecated
protected void invalidateRemoteAddress() {
remoteAddress = null;
} //是否已经注册
@Override
public boolean isRegistered() {
return registered;
} /******* ChannelOutboundInvoker接口中的方法都交给pipeline对象代理********/
@Override
public ChannelFuture bind(SocketAddress localAddress) {
return pipeline.bind(localAddress);
} @Override
public ChannelFuture connect(SocketAddress remoteAddress) {
return pipeline.connect(remoteAddress);
} @Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return pipeline.connect(remoteAddress, localAddress);
} @Override
public ChannelFuture disconnect() {
return pipeline.disconnect();
} @Override
public ChannelFuture close() {
return pipeline.close();
} @Override
public ChannelFuture deregister() {
return pipeline.deregister();
} @Override
public Channel flush() {
pipeline.flush();
return this;
} @Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
return pipeline.bind(localAddress, promise);
} @Override
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
return pipeline.connect(remoteAddress, promise);
} @Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
return pipeline.connect(remoteAddress, localAddress, promise);
} @Override
public ChannelFuture disconnect(ChannelPromise promise) {
return pipeline.disconnect(promise);
} @Override
public ChannelFuture close(ChannelPromise promise) {
return pipeline.close(promise);
} @Override
public ChannelFuture deregister(ChannelPromise promise) {
return pipeline.deregister(promise);
} @Override
public Channel read() {
pipeline.read();
return this;
} @Override
public ChannelFuture write(Object msg) {
return pipeline.write(msg);
} @Override
public ChannelFuture write(Object msg, ChannelPromise promise) {
return pipeline.write(msg, promise);
} @Override
public ChannelFuture writeAndFlush(Object msg) {
return pipeline.writeAndFlush(msg);
} @Override
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
return pipeline.writeAndFlush(msg, promise);
} @Override
public ChannelPromise newPromise() {
return pipeline.newPromise();
} @Override
public ChannelProgressivePromise newProgressivePromise() {
return pipeline.newProgressivePromise();
} @Override
public ChannelFuture newSucceededFuture() {
return pipeline.newSucceededFuture();
} @Override
public ChannelFuture newFailedFuture(Throwable cause) {
return pipeline.newFailedFuture(cause);
} /**************ChannelOutboundInvoker接口*************/ //返回ChannelFuture
@Override
public ChannelFuture closeFuture() {
return closeFuture;
} //返回UnSafe对象
@Override
public Unsafe unsafe() {
return unsafe;
} /**
* Create a new {@link AbstractUnsafe} instance which will be used for the life-time of the {@link Channel}
*/
//抽象方法 具体获取Unsafe的方式交给子类实现,
protected abstract AbstractUnsafe newUnsafe(); /**
* Returns the ID of this channel.
*/
@Override
public final int hashCode() {
return id.hashCode();
} /**
* Returns {@code true} if and only if the specified object is identical
* with this channel (i.e: {@code this == o}).
*/
@Override
public final boolean equals(Object o) {
return this == o;
} @Override
public final int compareTo(Channel o) {
if (this == o) {
return 0;
} return id().compareTo(o.id());
} /**
* Returns the {@link String} representation of this channel. The returned
* string contains the {@linkplain #hashCode() ID}, {@linkplain #localAddress() local address},
* and {@linkplain #remoteAddress() remote address} of this channel for
* easier identification.
*/
@Override
public String toString() {
boolean active = isActive();
if (strValActive == active && strVal != null) {
return strVal;
} SocketAddress remoteAddr = remoteAddress();
SocketAddress localAddr = localAddress();
if (remoteAddr != null) {
StringBuilder buf = new StringBuilder(96)
.append("[id: 0x")
.append(id.asShortText())
.append(", L:")
.append(localAddr)
.append(active? " - " : " ! ")
.append("R:")
.append(remoteAddr)
.append(']');
strVal = buf.toString();
} else if (localAddr != null) {
StringBuilder buf = new StringBuilder(64)
.append("[id: 0x")
.append(id.asShortText())
.append(", L:")
.append(localAddr)
.append(']');
strVal = buf.toString();
} else {
StringBuilder buf = new StringBuilder(16)
.append("[id: 0x")
.append(id.asShortText())
.append(']');
strVal = buf.toString();
} strValActive = active;
return strVal;
} @Override
public final ChannelPromise voidPromise() {
return pipeline.voidPromise();
} /**
* {@link Unsafe} implementation which sub-classes must extend and use.
*/
//Unsafe的抽象实现,虽然是抽象类,但没有抽象的方法,只是对某些方法提供了空实现;一些方法的关键实现是交给AbstractChannel的特定子类
protected abstract class AbstractUnsafe implements Unsafe { private volatile ChannelOutboundBuffer outboundBuffer = new ChannelOutboundBuffer(AbstractChannel.this);
private RecvByteBufAllocator.Handle recvHandle;
private boolean inFlush0;
/** true if the channel has never been registered, false otherwise */
private boolean neverRegistered = true; private void assertEventLoop() {
assert !registered || eventLoop.inEventLoop();
} @Override
public RecvByteBufAllocator.Handle recvBufAllocHandle() {
if (recvHandle == null) {
recvHandle = config().getRecvByteBufAllocator().newHandle();
}
return recvHandle;
} @Override
public final ChannelOutboundBuffer outboundBuffer() {
return outboundBuffer;
} @Override
public final SocketAddress localAddress() {
return localAddress0();
} @Override
public final SocketAddress remoteAddress() {
return remoteAddress0();
} //注册;将Unsafe与EventLoop绑定
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop == null) {
throw new NullPointerException("eventLoop");
}
if (isRegistered()) {
promise.setFailure(new IllegalStateException("registered to an event loop already"));
return;
}
if (!isCompatible(eventLoop)) {
promise.setFailure(
new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
return;
} AbstractChannel.this.eventLoop = eventLoop; if (eventLoop.inEventLoop()) {
//如果当前线程是处理线程,直接注册
register0(promise);
} else {
//如果当前线程不是处理线程,则包装成任务丢给eventLoop处理
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
logger.warn(
"Force-closing a channel whose registration task was not accepted by an event loop: {}",
AbstractChannel.this, t);
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
} private void register0(ChannelPromise promise) {
try {
// check if the channel is still open as it could be closed in the mean time when the register
// call was outside of the eventLoop
//确认Channel仍然打开
if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
} boolean firstRegistration = neverRegistered;
//注册具体流程
doRegister();
//修改注册状态
neverRegistered = false;
registered = true; // Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
// user may already fire events through the pipeline in the ChannelFutureListener.
//产生HandlerAdded
pipeline.invokeHandlerAddedIfNeeded(); safeSetSuccess(promise);
//注册成功,产生注册事件
pipeline.fireChannelRegistered(); //激活成功
if (isActive()) {
//首次注册,产生active事件
if (firstRegistration) {
pipeline.fireChannelActive();
} else if (config().isAutoRead()) {
//如果重注册且配置了自动读
// This channel was registered before and autoRead() is set. This means we need to begin read
// again so that we process inbound data.
//
// See https://github.com/netty/netty/issues/4805
beginRead();
}
}
} catch (Throwable t) {
// Close the channel directly to avoid FD leak.
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
} //绑定
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
assertEventLoop(); if (!promise.setUncancellable() || !ensureOpen(promise)) {
return;
} // See: https://github.com/netty/netty/issues/576
if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
localAddress instanceof InetSocketAddress &&
!((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
!PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
// Warn a user about the fact that a non-root user can't receive a
// broadcast packet on *nix if the socket is bound on non-wildcard address.
logger.warn(
"A non-root user can't receive a broadcast packet if the socket " +
"is not bound to a wildcard address; binding to a non-wildcard " +
"address (" + localAddress + ") anyway as requested.");
} boolean wasActive = isActive();
try {
//具体绑定流程
doBind(localAddress);
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
}
//绑定成功,产生Active事件
if (!wasActive && isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelActive();
}
});
}
//将Promis设置为成功
safeSetSuccess(promise);
} //断开连接
@Override
public final void disconnect(final ChannelPromise promise) {
assertEventLoop(); if (!promise.setUncancellable()) {
return;
} boolean wasActive = isActive();
try {
//断开具体流程
doDisconnect();
} catch (Throwable t) {
safeSetFailure(promise, t);
closeIfClosed();
return;
} //断开成功,产生Inactive事件
if (wasActive && !isActive()) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireChannelInactive();
}
});
} safeSetSuccess(promise);
closeIfClosed(); // doDisconnect() might have closed the channel
} @Override
public final void close(final ChannelPromise promise) {
assertEventLoop(); close(promise, CLOSE_CLOSED_CHANNEL_EXCEPTION, CLOSE_CLOSED_CHANNEL_EXCEPTION, false);
} /**
* Shutdown the output portion of the corresponding {@link Channel}.
* For example this will clean up the {@link ChannelOutboundBuffer} and not allow any more writes.
*/
@UnstableApi
public final void shutdownOutput(final ChannelPromise promise) {
assertEventLoop();
shutdownOutput(promise, null);
} /**
* Shutdown the output portion of the corresponding {@link Channel}.
* For example this will clean up the {@link ChannelOutboundBuffer} and not allow any more writes.
* @param cause The cause which may provide rational for the shutdown.
*/
private void shutdownOutput(final ChannelPromise promise, Throwable cause) {
if (!promise.setUncancellable()) {
return;
} final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
//如果不存在输出缓冲区,设置Promise为失败,抛出已关闭异常
if ( == null) {
promise.setFailure(CLOSE_CLOSED_CHANNEL_EXCEPTION);
return;
} this.outboundBuffer = null; // Disallow adding any messages and flushes to outboundBuffer. //线程关闭原因
final Throwable shutdownCause = cause == null ?
new ChannelOutputShutdownException("Channel output shutdown") :
new ChannelOutputShutdownException("Channel output shutdown", cause);
Executor closeExecutor = prepareToClose();
if (closeExecutor != null) {
closeExecutor.execute(new Runnable() {
@Override
public void run() {
try {
//关闭流程
doShutdownOutput();
promise.setSuccess();
} catch (Throwable err) {
promise.setFailure(err);
} finally {
// Dispatch to the EventLoop
eventLoop().execute(new Runnable() {
@Override
public void run() {
closeOutboundBufferForShutdown(pipeline, outboundBuffer, shutdownCause);
}
});
}
}
});
} else {
try {
// Execute the shutdown.
doShutdownOutput();
promise.setSuccess();
} catch (Throwable err) {
promise.setFailure(err);
} finally {
closeOutboundBufferForShutdown(pipeline, outboundBuffer, shutdownCause);
}
}
} private void closeOutboundBufferForShutdown(
ChannelPipeline pipeline, ChannelOutboundBuffer buffer, Throwable cause) {
buffer.failFlushed(cause, false);
buffer.close(cause, true);
pipeline.fireUserEventTriggered(ChannelOutputShutdownEvent.INSTANCE);
} private void close(final ChannelPromise promise, final Throwable cause,
final ClosedChannelException closeCause, final boolean notify) {
if (!promise.setUncancellable()) {
return;
} if (closeInitiated) {
if (closeFuture.isDone()) {
// Closed already.
safeSetSuccess(promise);
} else if (!(promise instanceof VoidChannelPromise)) { // Only needed if no VoidChannelPromise.
// This means close() was called before so we just register a listener and return
closeFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
promise.setSuccess();
}
});
}
return;
} closeInitiated = true; final boolean wasActive = isActive();
final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
this.outboundBuffer = null; // Disallow adding any messages and flushes to outboundBuffer.
Executor closeExecutor = prepareToClose();
if (closeExecutor != null) {
closeExecutor.execute(new Runnable() {
@Override
public void run() {
try {
// Execute the close.
doClose0(promise);
} finally {
// Call invokeLater so closeAndDeregister is executed in the EventLoop again!
invokeLater(new Runnable() {
@Override
public void run() {
if (outboundBuffer != null) {
// Fail all the queued messages
outboundBuffer.failFlushed(cause, notify);
outboundBuffer.close(closeCause);
}
fireChannelInactiveAndDeregister(wasActive);
}
});
}
}
});
} else {
try {
// Close the channel and fail the queued messages in all cases.
doClose0(promise);
} finally {
if (outboundBuffer != null) {
// Fail all the queued messages.
outboundBuffer.failFlushed(cause, notify);
outboundBuffer.close(closeCause);
}
}
if (inFlush0) {
invokeLater(new Runnable() {
@Override
public void run() {
fireChannelInactiveAndDeregister(wasActive);
}
});
} else {
fireChannelInactiveAndDeregister(wasActive);
}
}
} private void doClose0(ChannelPromise promise) {
try {
doClose();
closeFuture.setClosed();
safeSetSuccess(promise);
} catch (Throwable t) {
closeFuture.setClosed();
safeSetFailure(promise, t);
}
} private void fireChannelInactiveAndDeregister(final boolean wasActive) {
deregister(voidPromise(), wasActive && !isActive());
} @Override
public final void closeForcibly() {
assertEventLoop(); try {
doClose();
} catch (Exception e) {
logger.warn("Failed to close a channel.", e);
}
} @Override
public final void deregister(final ChannelPromise promise) {
assertEventLoop(); deregister(promise, false);
} private void deregister(final ChannelPromise promise, final boolean fireChannelInactive) {
if (!promise.setUncancellable()) {
return;
} if (!registered) {
safeSetSuccess(promise);
return;
} // As a user may call deregister() from within any method while doing processing in the ChannelPipeline,
// we need to ensure we do the actual deregister operation later. This is needed as for example,
// we may be in the ByteToMessageDecoder.callDecode(...) method and so still try to do processing in
// the old EventLoop while the user already registered the Channel to a new EventLoop. Without delay,
// the deregister operation this could lead to have a handler invoked by different EventLoop and so
// threads.
//
// See:
// https://github.com/netty/netty/issues/4435
invokeLater(new Runnable() {
@Override
public void run() {
try {
doDeregister();
} catch (Throwable t) {
logger.warn("Unexpected exception occurred while deregistering a channel.", t);
} finally {
if (fireChannelInactive) {
pipeline.fireChannelInactive();
}
// Some transports like local and AIO does not allow the deregistration of
// an open channel. Their doDeregister() calls close(). Consequently,
// close() calls deregister() again - no need to fire channelUnregistered, so check
// if it was registered.
if (registered) {
registered = false;
pipeline.fireChannelUnregistered();
}
safeSetSuccess(promise);
}
}
});
} @Override
public final void beginRead() {
assertEventLoop(); if (!isActive()) {
return;
} try {
doBeginRead();
} catch (final Exception e) {
invokeLater(new Runnable() {
@Override
public void run() {
pipeline.fireExceptionCaught(e);
}
});
close(voidPromise());
}
} @Override
public final void write(Object msg, ChannelPromise promise) {
assertEventLoop(); ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
if (outboundBuffer == null) {
// If the outboundBuffer is null we know the channel was closed and so
// need to fail the future right away. If it is not null the handling of the rest
// will be done in flush0()
// See https://github.com/netty/netty/issues/2362
safeSetFailure(promise, WRITE_CLOSED_CHANNEL_EXCEPTION);
// release message now to prevent resource-leak
ReferenceCountUtil.release(msg);
return;
} int size;
try {
msg = filterOutboundMessage(msg);
size = pipeline.estimatorHandle().size(msg);
if (size < 0) {
size = 0;
}
} catch (Throwable t) {
safeSetFailure(promise, t);
ReferenceCountUtil.release(msg);
return;
} outboundBuffer.addMessage(msg, size, promise);
} @Override
public final void flush() {
assertEventLoop(); ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
if (outboundBuffer == null) {
return;
} outboundBuffer.addFlush();
flush0();
} @SuppressWarnings("deprecation")
protected void flush0() {
if (inFlush0) {
// Avoid re-entrance
return;
} final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
if (outboundBuffer == null || outboundBuffer.isEmpty()) {
return;
} inFlush0 = true; // Mark all pending write requests as failure if the channel is inactive.
if (!isActive()) {
try {
if (isOpen()) {
outboundBuffer.failFlushed(FLUSH0_NOT_YET_CONNECTED_EXCEPTION, true);
} else {
// Do not trigger channelWritabilityChanged because the channel is closed already.
outboundBuffer.failFlushed(FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
}
} finally {
inFlush0 = false;
}
return;
} try {
doWrite(outboundBuffer);
} catch (Throwable t) {
if (t instanceof IOException && config().isAutoClose()) {
/**
* Just call {@link #close(ChannelPromise, Throwable, boolean)} here which will take care of
* failing all flushed messages and also ensure the actual close of the underlying transport
* will happen before the promises are notified.
*
* This is needed as otherwise {@link #isActive()} , {@link #isOpen()} and {@link #isWritable()}
* may still return {@code true} even if the channel should be closed as result of the exception.
*/
close(voidPromise(), t, FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
} else {
try {
shutdownOutput(voidPromise(), t);
} catch (Throwable t2) {
close(voidPromise(), t2, FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
}
}
} finally {
inFlush0 = false;
}
} @Override
public final ChannelPromise voidPromise() {
assertEventLoop(); return unsafeVoidPromise;
} protected final boolean ensureOpen(ChannelPromise promise) {
if (isOpen()) {
return true;
} safeSetFailure(promise, ENSURE_OPEN_CLOSED_CHANNEL_EXCEPTION);
return false;
} /**
* Marks the specified {@code promise} as success. If the {@code promise} is done already, log a message.
*/
protected final void safeSetSuccess(ChannelPromise promise) {
if (!(promise instanceof VoidChannelPromise) && !promise.trySuccess()) {
logger.warn("Failed to mark a promise as success because it is done already: {}", promise);
}
} /**
* Marks the specified {@code promise} as failure. If the {@code promise} is done already, log a message.
*/
protected final void safeSetFailure(ChannelPromise promise, Throwable cause) {
if (!(promise instanceof VoidChannelPromise) && !promise.tryFailure(cause)) {
logger.warn("Failed to mark a promise as failure because it's done already: {}", promise, cause);
}
} protected final void closeIfClosed() {
if (isOpen()) {
return;
}
close(voidPromise());
} //将任务提交给EventLoop执行
private void invokeLater(Runnable task) {
try {
// This method is used by outbound operation implementations to trigger an inbound event later.
// They do not trigger an inbound event immediately because an outbound operation might have been
// triggered by another inbound event handler method. If fired immediately, the call stack
// will look like this for example:
//
// handlerA.inboundBufferUpdated() - (1) an inbound handler method closes a connection.
// -> handlerA.ctx.close()
// -> channel.unsafe.close()
// -> handlerA.channelInactive() - (2) another inbound handler method called while in (1) yet
//
// which means the execution of two inbound handler methods of the same handler overlap undesirably.
eventLoop().execute(task);
} catch (RejectedExecutionException e) {
logger.warn("Can't invoke task later as EventLoop rejected it", e);
}
} /**
* Appends the remote address to the message of the exceptions caused by connection attempt failure.
*/
protected final Throwable annotateConnectException(Throwable cause, SocketAddress remoteAddress) {
if (cause instanceof ConnectException) {
return new AnnotatedConnectException((ConnectException) cause, remoteAddress);
}
if (cause instanceof NoRouteToHostException) {
return new AnnotatedNoRouteToHostException((NoRouteToHostException) cause, remoteAddress);
}
if (cause instanceof SocketException) {
return new AnnotatedSocketException((SocketException) cause, remoteAddress);
} return cause;
} /**
* Prepares to close the {@link Channel}. If this method returns an {@link Executor}, the
* caller must call the {@link Executor#execute(Runnable)} method with a task that calls
* {@link #doClose()} on the returned {@link Executor}. If this method returns {@code null},
* {@link #doClose()} must be called from the caller thread. (i.e. {@link EventLoop})
*/
protected Executor prepareToClose() {
return null;
}
} /**
* Return {@code true} if the given {@link EventLoop} is compatible with this instance.
*/
protected abstract boolean isCompatible(EventLoop loop); /**
* Returns the {@link SocketAddress} which is bound locally.
*/
protected abstract SocketAddress localAddress0(); /**
* Return the {@link SocketAddress} which the {@link Channel} is connected to.
*/
protected abstract SocketAddress remoteAddress0(); /**
* Is called after the {@link Channel} is registered with its {@link EventLoop} as part of the register process.
*
* Sub-classes may override this method
*/
protected void doRegister() throws Exception {
// NOOP
} /**
* Bind the {@link Channel} to the {@link SocketAddress}
*/
protected abstract void doBind(SocketAddress localAddress) throws Exception; /**
* Disconnect this {@link Channel} from its remote peer
*/
protected abstract void doDisconnect() throws Exception; /**
* Close the {@link Channel}
*/
protected abstract void doClose() throws Exception; /**
* Called when conditions justify shutting down the output portion of the channel. This may happen if a write
* operation throws an exception.
*/
@UnstableApi
protected void doShutdownOutput() throws Exception {
doClose();
} /**
* Deregister the {@link Channel} from its {@link EventLoop}.
*
* Sub-classes may override this method
*/
protected void doDeregister() throws Exception {
// NOOP
} /**
* Schedule a read operation.
*/
protected abstract void doBeginRead() throws Exception; /**
* Flush the content of the given buffer to the remote peer.
*/
protected abstract void doWrite(ChannelOutboundBuffer in) throws Exception; /**
* Invoked when a new message is added to a {@link ChannelOutboundBuffer} of this {@link AbstractChannel}, so that
* the {@link Channel} implementation converts the message to another. (e.g. heap buffer -> direct buffer)
*/
protected Object filterOutboundMessage(Object msg) throws Exception {
return msg;
} static final class CloseFuture extends DefaultChannelPromise { CloseFuture(AbstractChannel ch) {
super(ch);
} @Override
public ChannelPromise setSuccess() {
throw new IllegalStateException();
} @Override
public ChannelPromise setFailure(Throwable cause) {
throw new IllegalStateException();
} @Override
public boolean trySuccess() {
throw new IllegalStateException();
} @Override
public boolean tryFailure(Throwable cause) {
throw new IllegalStateException();
} boolean setClosed() {
return super.trySuccess();
}
} private static final class AnnotatedConnectException extends ConnectException { private static final long serialVersionUID = 3901958112696433556L; AnnotatedConnectException(ConnectException exception, SocketAddress remoteAddress) {
super(exception.getMessage() + ": " + remoteAddress);
initCause(exception);
setStackTrace(exception.getStackTrace());
} @Override
public Throwable fillInStackTrace() {
return this;
}
} private static final class AnnotatedNoRouteToHostException extends NoRouteToHostException { private static final long serialVersionUID = -6801433937592080623L; AnnotatedNoRouteToHostException(NoRouteToHostException exception, SocketAddress remoteAddress) {
super(exception.getMessage() + ": " + remoteAddress);
initCause(exception);
setStackTrace(exception.getStackTrace());
} @Override
public Throwable fillInStackTrace() {
return this;
}
} private static final class AnnotatedSocketException extends SocketException { private static final long serialVersionUID = 3896743275010454039L; AnnotatedSocketException(SocketException exception, SocketAddress remoteAddress) {
super(exception.getMessage() + ": " + remoteAddress);
initCause(exception);
setStackTrace(exception.getStackTrace());
} @Override
public Throwable fillInStackTrace() {
return this;
}
}
}
我们可以看到ChannelOutboundInvoker接口的方法主要交给Pipeline代理。
并且AbstractChannel留下了很多类似doXXX的抽象方法。这应该是模板模式的应用,由不同的子类实现不同的逻辑。
还注意到一点,AbstractChannel中有个抽象的内部类,实现了Unsafe接口。
Unsafe接口有很多方法与ChannelOutboundInvoker中的方法类似。
看AbstractUnsafe中的实现,与网络链路相关的方法中,具体的业务实现仍然是交给外部接口的方法实现。
比如register方法;关键的业务逻辑还是交给AbstractChannel中的doRegister方法。
并且在doRegister注册完成后,调用pipeline的fireXXX方法,让事件在Pipeline中传递。从而实现Netty的事件驱动。
Netty(四):AbstractChannel源码解析的更多相关文章
- Netty系列之源码解析(一)
本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 当前:Netty 源码解析(一)开始 Netty 源码解析(二): Netty 的 Channel ...
- Android Handler机制(四)---Handler源码解析
Handler的主要用途有两个:(1).在将来的某个时刻执行消息或一个runnable,(2)把消息发送到消息队列. 主要依靠post(Runnable).postAtTime(Runnable, l ...
- Netty 核心组件 EventLoop 源码解析
前言 在前文 Netty 启动过程源码分析 (本文超长慎读)(基于4.1.23) 中,我们分析了整个服务器端的启动过程.在那篇文章中,我们重点关注了启动过程,而在启动过程中对核心组件并没有进行详细介绍 ...
- Android事件总线(四)源码解析otto
前言 上一篇文章中讲到了otto的用法,这一篇我们来讲一下otto的源码.可能有人觉得otto过时了,但是通过源码我们学习的是高手设计otto时的设计理念,这种设计理念是不过时的. otto各个类的作 ...
- 四.jQuery源码解析之jQuery.fn.init()的参数解析
从return new jQuery.fn.init( selector, context, rootjQuery )中可以看出 参数selector和context是来自我们在调用jQuery方法时 ...
- spring cloud深入学习(四)-----eureka源码解析、ribbon解析、声明式调用feign
基本概念 1.Registe 一一服务注册当eureka Client向Eureka Server注册时,Eureka Client提供自身的元数据,比如IP地址.端口.运行状况指标的Uri.主页地址 ...
- 基于Netty的RPC架构学习笔记(五):netty线程模型源码分析(二)
文章目录 小技巧(如何看开源框架的源码) 源码解析 阅读源码技巧 打印查看 通过打断点调试 查看调用栈 小技巧(如何看开源框架的源码) 一断点 二打印 三看调用栈 四搜索 源码解析 //设置nioso ...
- redux的源码解析
一. redux出现的动机 1. Javascript 需要管理比任何时候都要多的state2. state 在什么时候,由于什么原因,如何变化已然不受控制.3. 来自前端开发领域的新需求4. 我们总 ...
- Springboot打包执行源码解析
一.打包 Springboot打包的时候,需要配置一个maven插件[spring-boot-maven-plugin] <build> <plugins> <plugi ...
随机推荐
- qt creator源码全方面分析(3-9)
依赖分析图 我们对库和插件的依赖性进行分析,并画图如下,稍微省略了一些插件,画出来太乱了,核心的都在图中了. 原创造福大家,共享改变世界 献出一片爱心,温暖作者心灵
- Zabbix监控平台
Zabbix监控平台 案例1:常用系统监控命令 案例2:部署Zabbi ...
- 解决:docker-compose端口绑定
docker-compose 进程绑定 Bind for 0.0.0.0:3825 failed: port is already allocated 查看进程发现有进程在关闭后继续进行 docker ...
- Flask 入门(九)
外键数据库 我们想想,所有的数据不可能这么简单,万一建的数据库有了外键呢?如何增加,如何查询? 承接上文: 先登录mysql数据库,把里面的表和数据都删了 执行语句: use data select ...
- 切片-list、字符串
切片-list.字符串 1.字符串,切片顾头不顾尾 s="123455" print(s[0:3]) 结果:123 2.list d=[12,34,45] print(d[: ...
- 页面存在多个url,使用jmeter进行遍历操作
有一次遇见一个问题:进入网站后,有多个相同的url,但是仅url后面的路径中id有区别,如下图:这时我想要遍历点击查看url详情内容:那么就可以使用一个“逻辑控制器---foreach控制器” 如下: ...
- Linux C++ 网络编程学习系列(5)——多路IO之epoll边沿触发
多路IO之epoll边沿触发+非阻塞 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll_ET_LT_NOBLOCK_example 源码说 ...
- 数据结构和算法(Golang实现)(9)基础知识-算法复杂度及渐进符号
算法复杂度及渐进符号 一.算法复杂度 首先每个程序运行过程中,都要占用一定的计算机资源,比如内存,磁盘等,这些是空间,计算过程中需要判断,循环执行某些逻辑,周而反复,这些是时间. 那么一个算法有多好, ...
- 端口扫描工具nmap的常用参数讲解
转载请注明出处:https://www.cnblogs.com/wangyanzhong123/p/12576406.html nmap下载与安装 这个没什么好说的.很简单官网上下载就ok了,需要注意 ...
- 使用malloc和free函数进行内存动态分配
一.在学习c语言里面,内存分配这个话题非常有意思,因为我们平时在开发的时候,如果一不小心没注意内存释放的话,写的的程序很容易出错,所以今天就来回顾一下c语言里面的内存动态分配,下面我们先来看一个实例来 ...