Unsafe系列源码
Unsafe 是 Channel 的内部接口,聚合在 Channel 中协助进行网络读写相关的操作,因为它的设计初衷就是 Channel 的内部辅助类,不应该被 Netty 框架的上层使用者调用,所以被命名为 Unsafe。不能将它与JDK的Unsafe弄混,并不是同一个类。Unsafe 用于处理 Channel 对应网络 IO 的底层操作,相关的网络操作基本上最终也会交给Unsafe完成,所以基本上一个 Channel 的实现中就有一个对应的 Unsafe 类的实现。下面来看看NioMessageUnsafe和NioSocketChannelUnsafe两个类的继承关系:
NioMessageUnsafe
NioSocketChannelUnsafe
Unsafe和NioUnsafe
Unsafe 接口中定义了 socket 相关操作,包括 SocketAddress 获取、selector 注册、网卡 端口绑定、socket 建连与断连、socket 写数据。这些操作都和 jdk 底层 socket 相关。
NioUnsafe 接口主要增加了一些和 nio 操作联系比较紧密的操作。
AbstractUnsafe
在这个类中方法比较多,这里找几个比较重要的来讲解,这些方法可能是之前讲解的方法中调用到的,也可能是下一节内容中会调用的方法,如果有不明白或写错的欢迎在评论区指出,笔者定期会看博客消息。
方法register
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop == null) {
throw new NullPointerException("eventLoop");
} else if (AbstractChannel.this.isRegistered()) {
promise.setFailure(new IllegalStateException("registered to an event loop already"));
} else if (!AbstractChannel.this.isCompatible(eventLoop)) {
promise.setFailure(new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
} else {
//前面一堆的逻辑都是在做校验,如果校验通过了就会来到这个代码块
//这里首先将当前的Channel与参数中的EventLoop建立关系
AbstractChannel.this.eventLoop = eventLoop;
//这个首先进行线程的本地性校验,也就是前面说过得,校验现在执行的线程是不是与Channel关联
//的EventLoop中的线程,如果是则直接执行register0方法,如果不是则调用EventLoop.execute来执行
//最终不管走哪里都是执行register0方法,所以接下来进入register0方法
if (eventLoop.inEventLoop()) {
this.register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
public void run() {
AbstractUnsafe.this.register0(promise);
}
});
} catch (Throwable var4) {
AbstractChannel.logger.warn("Force-closing a channel whose registration task was not accepted by an event loop: {}", AbstractChannel.this, var4);
this.closeForcibly();
AbstractChannel.this.closeFuture.setClosed();
this.safeSetFailure(promise, var4);
}
}
}
}
private void register0(ChannelPromise promise) {
try {
if (!promise.setUncancellable() || !this.ensureOpen(promise)) {
return;
}
boolean firstRegistration = this.neverRegistered;
//调用doRegister方法,这个方法说了很多次了吧,大概就是将Channel注册到EventLoop中的
//Selector选择器中,但是此时还未将关注的事件注册进去。
AbstractChannel.this.doRegister();
this.neverRegistered = false;
AbstractChannel.this.registered = true;
//执行ChannelInitializer.initChannel,以保证handler被添加到pipeline上。
AbstractChannel.this.pipeline.invokeHandlerAddedIfNeeded();
this.safeSetSuccess(promise);
//触发了ChannelRegistered事件,跟ChannelActive作用一样都是在某个特定时间点执行
AbstractChannel.this.pipeline.fireChannelRegistered();
if (AbstractChannel.this.isActive()) {
if (firstRegistration) {
//如果Channel处于活动状态且是第一次注册,则触发ChannelActive事件。
//为什么要判断第一次呢?因为可能是之前register然后deregister了,现在有register的
//防止多次触发ChannelActive事件
AbstractChannel.this.pipeline.fireChannelActive();
} else if (AbstractChannel.this.config().isAutoRead()) {
//如果Channel处于活动状态且不是第一次注册,并设置了AutoRead,则调用beginRead
//beginRead最重要的就是doBeginRead方法,这个之前讲过,就是将Channel关注的事件注册到Selector
this.beginRead();
}
}
} catch (Throwable var3) {
this.closeForcibly();
AbstractChannel.this.closeFuture.setClosed();
this.safeSetFailure(promise, var3);
}
}
方法bind
bind 方法主要用于绑定指定端口。对于服务端,用于绑定监听端口,并设置 backlog 参数;对于客户端,用于指定客户端 Channel 的本地绑定 Socket 地址。
public final void bind(SocketAddress localAddress, ChannelPromise promise) {
this.assertEventLoop();
if (promise.setUncancellable() && this.ensureOpen(promise)) {
if (Boolean.TRUE.equals(AbstractChannel.this.config().getOption(ChannelOption.SO_BROADCAST)) && localAddress instanceof InetSocketAddress && !((InetSocketAddress)localAddress).getAddress().isAnyLocalAddress() && !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
AbstractChannel.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.");
}
//先获取绑定前的Channel状态是否为active激活状态
boolean wasActive = AbstractChannel.this.isActive();
try {
//doBind方法是一个抽象方法,在NioSocketChannel和NioServerSocketChannel中有不同的实现
AbstractChannel.this.doBind(localAddress);
} catch (Throwable var5) {
this.safeSetFailure(promise, var5);
this.closeIfClosed();
return;
}
//这里表示如果绑定前为未激活,绑定后为激活,说明Channel是在此次绑定过程中激活的
//那么就将fireChannelActive交给EventLoop中的线程执行
if (!wasActive && AbstractChannel.this.isActive()) {
this.invokeLater(new Runnable() {
public void run() {
AbstractChannel.this.pipeline.fireChannelActive();
}
});
}
this.safeSetSuccess(promise);
}
}
先来看看两种doBind方法的实现,然后在看看invokeLater方法。
NioServerSocketChannel:
protected void doBind(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
//可以设置backlog参数
this.javaChannel().bind(localAddress, this.config.getBacklog());
} else {
this.javaChannel().socket().bind(localAddress, this.config.getBacklog());
}
}
NioSocketChannel:
private void doBind0(SocketAddress localAddress) throws Exception {
if (PlatformDependent.javaVersion() >= 7) {
SocketUtils.bind(this.javaChannel(), localAddress);
} else {
SocketUtils.bind(this.javaChannel().socket(), localAddress);
}
}
接下来看invokeLater,主要是将fireChannelActive打包成Runnable交给EventLoop执行
private void invokeLater(Runnable task) {
try {
AbstractChannel.this.eventLoop().execute(task);
} catch (RejectedExecutionException var3) {
AbstractChannel.logger.warn("Can't invoke task later as EventLoop rejected it", var3);
}
}
方法disconnect
disconnect主要是用于断开连接,其中最重要的方法是doDisconnect方法。
public final void disconnect(ChannelPromise promise) {
this.assertEventLoop();
if (promise.setUncancellable()) {
//这里的思路跟bind方法一样,记录下disconnect前通道是否激活
boolean wasActive = AbstractChannel.this.isActive();
try {
//最重要的就是这个方法
//doDisconnect是抽象方法,在NioServerSocketChannel和NioSocketChannel中有不同实现
AbstractChannel.this.doDisconnect();
} catch (Throwable var4) {
this.safeSetFailure(promise, var4);
this.closeIfClosed();
return;
}
//表示如果原来是激活的,而现在是未激活状态
if (wasActive && !AbstractChannel.this.isActive()) {
//invokeLaster方法上面讲解bind方法时已经讲过了,就是将fireChannelInactive打包成
//Runnable交给EventLoop去执行。
this.invokeLater(new Runnable() {
public void run() {
AbstractChannel.this.pipeline.fireChannelInactive();
}
});
}
this.safeSetSuccess(promise);
this.closeIfClosed();
}
}
接下来看看doDisconnect两种实现
protected void doDisconnect() throws Exception {
//在NioServerSocketChannel中的实现直接抛出异常
throw new UnsupportedOperationException();
}
protected void doDisconnect() throws Exception {
//接着进入doClose
this.doClose();
}
protected void doClose() throws Exception {
super.doClose();
//最后调用了Channel的close方法来关闭连接
this.javaChannel().close();
}
可以发现在NioServerSocketChannel 是无所谓断开连接一说的,NioSocketChanne 则调用了SocketChannel 关闭连接,然后调用 fireChannelInactive 方法。
方法write、Flush
write 方法实际上将消息添加到发送缓存区 ChannelOutboundBuffer 中,并不是真正的写Channel。当调用 channel 的 write 方法写数据时,这个数据被一系列 ChannelOutboundHandler处理之后,它被放进这个缓冲区中,并没有真正把数据写到socketChannel中。然后再调用channel的flush方法,flush 会把 outboundBuffer中数据真正写到socketChannel。
public final void write(Object msg, ChannelPromise promise) {
this.assertEventLoop();
//获取到发送缓冲区ChannelOutboundBuffer,并对其进行校验
ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
if (outboundBuffer == null) {
this.safeSetFailure(promise, AbstractChannel.WRITE_CLOSED_CHANNEL_EXCEPTION);
ReferenceCountUtil.release(msg);
} else {
//outboundBuffer校验通过
int size;
try {
//调用filterOutboundMessage对msg进行过滤
msg = AbstractChannel.this.filterOutboundMessage(msg);
//计算msg的长度
size = AbstractChannel.this.pipeline.estimatorHandle().size(msg);
if (size < 0) {
size = 0;
}
} catch (Throwable var6) {
this.safeSetFailure(promise, var6);
ReferenceCountUtil.release(msg);
return;
}
//将处理好的msg放入到outboundBuffer中
outboundBuffer.addMessage(msg, size, promise);
}
}
将msg放入到outboundBuffer后,此时并未写到socketChannel中,当调用flush方法时才全部写入到Channel中。
public final void flush() {
this.assertEventLoop();
ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
if (outboundBuffer != null) {
outboundBuffer.addFlush();
//进入flush0看看
this.flush0();
}
}
protected void flush0() {
if (!this.inFlush0) {
ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
if (outboundBuffer != null && !outboundBuffer.isEmpty()) {
this.inFlush0 = true;
if (!AbstractChannel.this.isActive()) {
try {
if (AbstractChannel.this.isOpen()) {
outboundBuffer.failFlushed(AbstractChannel.FLUSH0_NOT_YET_CONNECTED_EXCEPTION, true);
} else {
outboundBuffer.failFlushed(AbstractChannel.FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
}
} finally {
this.inFlush0 = false;
}
} else {
try {
//整个方法最重要的一步就是调用Channel的doWrite方法
//doWrite方法其实就是在NioSocketChannel中讲到的doWrite方法,这里不在赘述。
//doWrite方法就是具体的将数据从outboundBuffer写到Channel的地方
AbstractChannel.this.doWrite(outboundBuffer);
} catch (Throwable var15) {
Throwable t = var15;
if (var15 instanceof IOException && AbstractChannel.this.config().isAutoClose()) {
this.close(this.voidPromise(), var15, AbstractChannel.FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
} else {
try {
this.shutdownOutput(this.voidPromise(), t);
} catch (Throwable var14) {
this.close(this.voidPromise(), var14, AbstractChannel.FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
}
}
} finally {
this.inFlush0 = false;
}
}
}
}
}
AbstractNioUnsafe
AbstractNioUnsafe是 AbstractUnsafe类的NIO实现,它主要实现了 connect ,finishConnect等方法。
方法connect
public final void connect(final SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
if (promise.setUncancellable() && this.ensureOpen(promise)) {
try {
if (AbstractNioChannel.this.connectPromise != null) {
throw new ConnectionPendingException();
}
boolean wasActive = AbstractNioChannel.this.isActive();
//这里调用doConnect,执行连接操作,这个方法主要有三种情况
//1.连接成功返回true
//2.连接正在进行,服务端还没有返回ACK应答,结果不确定,返回false
//3.连接失败,抛出异常
if (AbstractNioChannel.this.doConnect(remoteAddress, localAddress)) {
//第一种,连接成功则执行fulfillConnectPromise方法。
this.fulfillConnectPromise(promise, wasActive);
} else {
//第二种,连接正在进行,结果还无法确定成功失败
AbstractNioChannel.this.connectPromise = promise;
AbstractNioChannel.this.requestedRemoteAddress = remoteAddress;
//获取连接超时时长设置
int connectTimeoutMillis = AbstractNioChannel.this.config().getConnectTimeoutMillis();
//设置定时任务,如果超时时间到了,还没有完成连接,则关闭连接,释放资源
if (connectTimeoutMillis > 0) {
AbstractNioChannel.this.connectTimeoutFuture = AbstractNioChannel.this.eventLoop().schedule(new Runnable() {
public void run() {
ChannelPromise connectPromise = AbstractNioChannel.this.connectPromise;
ConnectTimeoutException cause = new ConnectTimeoutException("connection timed out: " + remoteAddress);
if (connectPromise != null && connectPromise.tryFailure(cause)) {
AbstractNioUnsafe.this.close(AbstractNioUnsafe.this.voidPromise());
}
}
}, (long)connectTimeoutMillis, TimeUnit.MILLISECONDS);
}
//设置监听器,接收连接完成通知,判断连接是否被取消,如果被取消则关闭连接,释放资源
promise.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isCancelled()) {
if (AbstractNioChannel.this.connectTimeoutFuture != null) {
AbstractNioChannel.this.connectTimeoutFuture.cancel(false);
}
AbstractNioChannel.this.connectPromise = null;
AbstractNioUnsafe.this.close(AbstractNioUnsafe.this.voidPromise());
}
}
});
}
} catch (Throwable var6) {
promise.tryFailure(this.annotateConnectException(var6, remoteAddress));
this.closeIfClosed();
}
}
}
接下来看看fulfillConnectPromise方法:
private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
if (promise != null) {
boolean active = AbstractNioChannel.this.isActive();
boolean promiseSet = promise.trySuccess();
if (!wasActive && active) {
//最重要的是这里,调用pipeline的fireChannelActive方法
AbstractNioChannel.this.pipeline().fireChannelActive();
}
if (!promiseSet) {
this.close(this.voidPromise());
}
}
}
channelActive事件是一个入站事件,那么首先就会调用到HeadContext.channelActive方法:
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//向后传递
ctx.fireChannelActive();
//重点来看看这个方法
this.readIfIsAutoRead();
}
private void readIfIsAutoRead() {
if (DefaultChannelPipeline.this.channel.config().isAutoRead()) {
//这里调用了channel.read方法
DefaultChannelPipeline.this.channel.read();
}
}
public void read(ChannelHandlerContext ctx) {
//可以看到这里调用了beginRead方法,这个方法熟悉吧?前面介绍过很多次了。
this.unsafe.beginRead();
}
public final void beginRead() {
this.assertEventLoop();
if (AbstractChannel.this.isActive()) {
try {
//重点在doBeginRead方法
AbstractChannel.this.doBeginRead();
} catch (final Exception var2) {
this.invokeLater(new Runnable() {
public void run() {
AbstractChannel.this.pipeline.fireExceptionCaught(var2);
}
});
this.close(this.voidPromise());
}
}
}
protected void doBeginRead() throws Exception {
SelectionKey selectionKey = this.selectionKey;
if (selectionKey.isValid()) {
this.readPending = true;
int interestOps = selectionKey.interestOps();
if ((interestOps & this.readInterestOp) == 0) {
//这里是真正的将channel关注的事件注册到EventLoop的selector选择器中
selectionKey.interestOps(interestOps | this.readInterestOp);
}
}
}
方法finishConnect
public final void finishConnect() {
assert AbstractNioChannel.this.eventLoop().inEventLoop();
try {
boolean wasActive = AbstractNioChannel.this.isActive();
//doFinishConnect方法主要就是判断channel是否完成connect操作,如果没完成则抛出异常
AbstractNioChannel.this.doFinishConnect();
//fulfillConnectPromise方法上面刚讲过,这里不赘述
this.fulfillConnectPromise(AbstractNioChannel.this.connectPromise, wasActive);
} catch (Throwable var5) {
this.fulfillConnectPromise(AbstractNioChannel.this.connectPromise, this.annotateConnectException(var5, AbstractNioChannel.this.requestedRemoteAddress));
} finally {
if (AbstractNioChannel.this.connectTimeoutFuture != null) {
AbstractNioChannel.this.connectTimeoutFuture.cancel(false);
}
AbstractNioChannel.this.connectPromise = null;
}
}
NioMessageUnsafe
这个是专门来处理客户端连接的 unsafe,里面的方法很少,只有一个 read 方法。
public void read() {
assert AbstractNioMessageChannel.this.eventLoop().inEventLoop();
ChannelConfig config = AbstractNioMessageChannel.this.config();
ChannelPipeline pipeline = AbstractNioMessageChannel.this.pipeline();
Handle allocHandle = AbstractNioMessageChannel.this.unsafe().recvBufAllocHandle();
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
int localRead;
try {
do {
//整个read方法最重要的也就是这个doReadMessages方法,
//执行完后生成的子channel也就加入到readBuf中了
localRead = AbstractNioMessageChannel.this.doReadMessages(this.readBuf);
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while(allocHandle.continueReading());
} catch (Throwable var11) {
exception = var11;
}
localRead = this.readBuf.size();
//这里遍历生成的子channel加入到,触发ChannelRead事件,
//ChannelRead事件入站事件,所以进入到ServerBootStrapAccept的channelRead方法看看,
//因为在HeadContext和TailContext中的ChannelRead没有做处理
for(int i = 0; i < localRead; ++i) {
AbstractNioMessageChannel.this.readPending = false;
pipeline.fireChannelRead(this.readBuf.get(i));
}
this.readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
if (exception != null) {
closed = AbstractNioMessageChannel.this.closeOnReadError(exception);
pipeline.fireExceptionCaught(exception);
}
if (closed) {
AbstractNioMessageChannel.this.inputShutdown = true;
if (AbstractNioMessageChannel.this.isOpen()) {
this.close(this.voidPromise());
}
}
} finally {
if (!AbstractNioMessageChannel.this.readPending && !config.isAutoRead()) {
this.removeReadOp();
}
}
}
先进入doReadMessages方法看看,然后在看看ServerBootStrapAccept的channelRead方法(ServerBootStrapAccept这个类后面会提到)
protected int doReadMessages(List<Object> buf) throws Exception {
//借用SocketUtils.accept方法接受连接,生成一个子channel
SocketChannel ch = SocketUtils.accept(this.javaChannel());
try {
if (ch != null) {
//将这个子Channel包装成一个NioSocketChannel,然后加入到buf中
//需要注意的是,在new NioSocketChannel()时,会创建出channel中的pipeline
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (Throwable var6) {
logger.warn("Failed to create a new channel from an accepted socket.", var6);
try {
ch.close();
} catch (Throwable var5) {
logger.warn("Failed to close a socket.", var5);
}
}
return 0;
}
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//这个msg是什么?它是一个子channel,而且包装成NioSocketChannel。
final Channel child = (Channel)msg;
//将提前设置好的handler加入到pipeline中,还有一些其他的ops参数等等
//也就是channelRead这个方法完成了channel的一些配置
child.pipeline().addLast(new ChannelHandler[]{this.childHandler});
AbstractBootstrap.setChannelOptions(child, this.childOptions, ServerBootstrap.logger);
Entry[] var4 = this.childAttrs;
int var5 = var4.length;
for(int var6 = 0; var6 < var5; ++var6) {
Entry<AttributeKey<?>, Object> e = var4[var6];
child.attr((AttributeKey)e.getKey()).set(e.getValue());
}
try {
this.childGroup.register(child).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
ServerBootstrap.ServerBootstrapAcceptor.forceClose(child, future.cause());
}
}
});
} catch (Throwable var8) {
forceClose(child, var8);
}
}
NioByteUnsafe
NioByteUnsafe 负责具体的网络数据的读取,里面的方法不多,closeOnRead 处理读关闭(半关闭),handleReadException 处理读异常,最关键的是 read 方法。
方法read
public final void read() {
//首先,获取 NioSocketChannel 的 SocketChannelConfig,它主要用于设置客户端连接的 TCP 参数
ChannelConfig config = AbstractNioByteChannel.this.config();
if (AbstractNioByteChannel.this.shouldBreakReadReady(config)) {
AbstractNioByteChannel.this.clearReadPending();
} else {
ChannelPipeline pipeline = AbstractNioByteChannel.this.pipeline();
ByteBufAllocator allocator = config.getAllocator();
Handle allocHandle = this.recvBufAllocHandle();
allocHandle.reset(config);
ByteBuf byteBuf = null;
boolean close = false;
try {
do {
//Nio是面向缓冲区的,所以读数据,肯定需要先分配个缓冲区
byteBuf = allocHandle.allocate(allocator);
//读取Channel中的数据到bytebuf中
allocHandle.lastBytesRead(AbstractNioByteChannel.this.doReadBytes(byteBuf));
//如果读取字节数<=0则清空释放缓冲区,终止读取
if (allocHandle.lastBytesRead() <= 0) {
byteBuf.release();
byteBuf = null;
close = allocHandle.lastBytesRead() < 0;
if (close) {
AbstractNioByteChannel.this.readPending = false;
}
break;
}
allocHandle.incMessagesRead(1);
AbstractNioByteChannel.this.readPending = false;
//读取数据冲个,则触发ChannelRead事件,交给Handler处理这些数据
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
} while(allocHandle.continueReading());
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
if (close) {
this.closeOnRead(pipeline);
}
} catch (Throwable var11) {
this.handleReadException(pipeline, byteBuf, var11, close, allocHandle);
} finally {
if (!AbstractNioByteChannel.this.readPending && !config.isAutoRead()) {
this.removeReadOp();
}
}
}
}
看到这里,基本对Netty里面的组件源码有所认识了,但是仅仅是对单一的组件有所了解,放在整个Netty里,估计还是无法联系起来,再讲完下一个小节EventLoop系列源码后就是对Netty整体流程开始讲解,会将前面所学的东西串联起来。