netty理论部分(一)

netty理论部分(一)

工作原理

缓冲区

ByteBuf

Netty 根据 reference-counting(引用计数)来确定何时可以释放 ByteBuf 或 ByteBufHolder 和其他相关资源

Netty 缓冲 API 提供了几个优势:

  • 可以自定义缓冲类型
  • 通过一个内置的复合缓冲类型实现零拷贝
  • 扩展性好,比如 StringBuilder
  • 不需要调用 flip() 来切换读/写模式
  • 读取和写入索引分开
  • 方法链
  • 引用计数
  • Pooling(池)

索引

分为读写索引,read和write都会改变,readindex要小于writeindex。

类型

分为普通缓存区直接缓冲区(direct buff)。正常的io过程分为2个阶段,第二个阶段就是就绪数据处理。正常的数据都会从内核态拷贝到用户态,这也是正常的缓冲区。而实际上我们jvm使用的是虚拟地址,如果直接使用native方法,将该虚拟地址直接映射到规定的内核地址,那么该内核地址将和对用户态和核心态都可以见,那就可以少一个地址传递过程,也就是所谓的direct buff。

与NIO 的缓冲区联系

NIO缓冲区和这里的缓冲区是有区别的,NIO缓冲区就3个控制,location,limit,capity。通过前面2个变量交替,也就是flip来决定读还是写。netty在外层封装了一层,write会将内容写到ByteBuf,然后flush的时候会将内容刷到nio的ByteBuffer 里面。所有内容只有在flush的时候才可以是可见的。而netty的ByteBuf或者DirectBuf都是2个,一个readindex和writeindx。这部分内容在handler在详细解释.

channel

如果对什么是channel不懂得,可以先去看看上一篇文章java nio.

public interface Channel extends AttributeMap, ChannelOutboundInvoker, Comparable<Channel> {
    ChannelId id();
    EventLoop eventLoop();
    Channel parent();
    ChannelConfig config();
    boolean isOpen();
    boolean isRegistered();
    boolean isActive();
    ChannelMetadata metadata();
    SocketAddress localAddress();
    SocketAddress remoteAddress();
    ChannelFuture closeFuture();
    boolean isWritable();
    long bytesBeforeUnwritable();
    long bytesBeforeWritable();
    Channel.Unsafe unsafe();
    ChannelPipeline pipeline();
    ByteBufAllocator alloc();
    Channel read();
    Channel flush();
}

  • 第一个比较重要的方法是eventLoop,Channel需要注册到EventLoop的多路复用器上,用于处理IO事件,通过eventLoop方法可以获取到Channel注册的EventLoop。EventLoop本质上就是处理网络读写事件的Reactor线程。在Netty中,它不仅仅用来处理网络事件,也可以用来执行定时任务和用户自定义NioTask等任务。

  • 第二个比较常用的方法是metadata方法,熟悉TCP协议的同学可能知道,当创建Socket的时候需要指定TCP参数,例如接收和发送的TCP缓冲区大小,TCP的超时时间,是否重用地址等等。在Netty中,每个Channel对应一个物理连接,每个连接都有自己的TCP参数配置。所以,Channel会聚合一个ChannelMetadata用来对TCP参数提供元数据描述信息,通过metadata方法就可以获取当前Channel的TCP参数配置。

  • 第三个方法是parent,对于服务端Channel而言,它的父Channel为空,对于客户端Channel,它的父Channel就是创建它的ServerSocketChannel。

  • 第四个方法是用户获取Channel标识的id,它返回ChannelId对象,ChannelId是Channel的唯一标识,它的可能生成策略如下:
    (1) 机器的MAC地址(EUI-48或者EUI-64)等可以代表全局唯一的信息。
    (2) 当前进程的ID。
    (3) 当前系统时间的毫秒——System.currentTimeMillis
    (4) 当前系统时间的纳秒——System.nanoTime
    (5) 32位的随机整型数
    (6) 32位自增的序列数

  • channel config,里面都是设置的option等.

  • ChannelFuture也就是future类的东西,netty中都是异步io,或者说执行都是新的线程,想要放回结果就需要查看状态.ChannelFuture有两种状态:未完成(uncompleted)完成(completed).可以设置监听.

 @Override
 public void channelRead(ChannelHandlerContext ctx, Object msg) {
     ChannelFuture future = ctx.channel().close();
     future.addListener(new ChannelFutureListener() {
         public void operationComplete(ChannelFuture future) {
             // Perform post-closure operation
             // ...
         }
     });
 }
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {

派生图

channelunsafe的关系图.png

AbstractNioChannel
public abstract class AbstractNioChannel extends AbstractChannel {
    private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractNioChannel.class);
    private static final ClosedChannelException DO_CLOSE_CLOSED_CHANNEL_EXCEPTION = (ClosedChannelException)ThrowableUtil.unknownStackTrace(new ClosedChannelException(), AbstractNioChannel.class, "doClose()");
    private final SelectableChannel ch;
    protected final int readInterestOp;
    volatile SelectionKey selectionKey;
    boolean readPending;
    private final Runnable clearReadPendingRunnable = new Runnable() {
        public void run() {
            AbstractNioChannel.this.clearReadPending0();
        }
    };
    private ChannelPromise connectPromise;
    private ScheduledFuture<?> connectTimeoutFuture;
    private SocketAddress requestedRemoteAddress;

    protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;
        this.readInterestOp = readInterestOp;

        try {
            ch.configureBlocking(false);
        } catch (IOException var7) {
            try {
                ch.close();
            } catch (IOException var6) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Failed to close a partially initialized socket.", var6);
                }
            }

            throw new ChannelException("Failed to enter non-blocking mode.", var7);
        }
    }
    // 省略
}

selectionkey在NIO中相当于channel的唯一标识

SelectableChannel ch原生nio对象.

private final SelectableChannel ch;

readInterestOp,nio事件.

EventLoop中消耗selectedkeys,准备就绪的key会被消耗.

package io.netty.channel.nio;
注册channel
 @Override
protected void doRegister() throws Exception {
    boolean selected = false;
    for (;;) {
        try {
            selectionKey = javaChannel().register(eventLoop().selector, 0, this);
            return;
        } catch (CancelledKeyException e) {
            if (!selected) {
                // Force the Selector to select now as the "canceled" SelectionKey may still be
                // cached and not removed because no Select.select(..) operation was called yet.
                eventLoop().selectNow();
                selected = true;
            } else {
                // We forced a select operation on the selector before but the SelectionKey is still cached
                // for whatever reason. JDK bug ?
                throw e;
            }
        }
    }
}
遍历就绪的selectedkeys
 private void processSelectedKeysOptimized() {
        for(int i = 0; i < this.selectedKeys.size; ++i) {
            SelectionKey k = this.selectedKeys.keys[i];
            this.selectedKeys.keys[i] = null;
            Object a = k.attachment();
            if (a instanceof AbstractNioChannel) {
                this.processSelectedKey(k, (AbstractNioChannel)a);
            } else {
                NioTask<SelectableChannel> task = (NioTask)a;
                processSelectedKey(k, task);
            }

            if (this.needsToSelectAgain) {
                this.selectedKeys.reset(i + 1);
                this.selectAgain();
                i = -1;
            }
        }

    }
       private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
        NioUnsafe unsafe = ch.unsafe();
        if (!k.isValid()) {
            NioEventLoop eventLoop;
            try {
                eventLoop = ch.eventLoop();
            } catch (Throwable var6) {
                return;
            }

            if (eventLoop == this && eventLoop != null) {
                unsafe.close(unsafe.voidPromise());
            }
        } else {
            try {
                int readyOps = k.readyOps();
                if ((readyOps & 8) != 0) {
                    int ops = k.interestOps();
                    ops &= -9;
                    k.interestOps(ops);
                    unsafe.finishConnect();
                }

                if ((readyOps & 4) != 0) {
                    ch.unsafe().forceFlush();
                }

                if ((readyOps & 17) != 0 || readyOps == 0) {
                    unsafe.read();
                }
            } catch (CancelledKeyException var7) {
                unsafe.close(unsafe.voidPromise());
            }

        }
    }
  1. OP_ACCEPT=16
  2. OP_CONNECT=8
  3. OP_READ=1
  4. OP_WRITE=4
  5. ==0是没有绑定,就是注册了下.

selector

在netty中selector基本看不见的,过程被封装的很全。 调度,全在eventLoop中,其实没必要仔细看。这个要是改了,估计直接整体框架会垮掉,这个应该是最不容易修改的部分。想要爽爽这个可以试试NIO还是上面那个链接。

handler

netty修改比较重要的地方,用了很多设计模式。

在这里插入图片描述

public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler
public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelOutboundHandler 
public abstract class ChannelHandlerAdapter implements ChannelHandler

使用了策略设计模式,基本都是handler但是行为的不同,决定了他们是不同的handler,拥有inbound操作的handler和outbound操作的handler。就好比是,你我都是人类,我选择了上车操作,你选择了下车操作,实现了不同的接口。

pipeline

用来储存多个handler的双向链表。逻辑链表,很多人的图都是逻辑处理过的,实际的数据结构就是双向链表。

流向

Netty负责人演讲用的PPT

head->tail会判断是不是inboudinghandler,从tail在到head会判断是不是ouboundinghandler。

channelHandlerContext

abstract class AbstractChannelHandlerContext extends DefaultAttributeMap implements ChannelHandlerContext, ResourceLeakHint {
     volatile AbstractChannelHandlerContext next;
    volatile AbstractChannelHandlerContext prev;
   AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name, boolean inbound, boolean outbound) {
        this.name = (String)ObjectUtil.checkNotNull(name, "name");
        this.pipeline = pipeline;
        this.executor = executor;
        this.inbound = inbound;
        this.outbound = outbound;
        this.ordered = executor == null || executor instanceof OrderedEventExecutor;
    }   
}

我们所使用的ctx大部分是defaultchannelhandercontext。

final class DefaultChannelHandlerContext extends AbstractChannelHandlerContext {
    private final ChannelHandler handler;

    DefaultChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {
        super(pipeline, executor, name, isInbound(handler), isOutbound(handler));
        if (handler == null) {
            throw new NullPointerException("handler");
        } else {
            this.handler = handler;
        }
    }

    public ChannelHandler handler() {
        return this.handler;
    }
 //many   
}

一个每次add一个channelHandler都会创建一个channelHandlerContext与之绑定,channelHandlerContext的作用就是管理它所关联的channelHandler和在同一个pipeline中的其他channelHandler之间的交互,他是什么?他与某个channelHandler绑定,权利有限,只能从当前他所绑定的channelHandler开始搞事情。

ctx的write
//AbstractChannelHandlerContext
private void write(Object msg, boolean flush, ChannelPromise promise) {
    AbstractChannelHandlerContext next = this.findContextOutbound();
    Object m = this.pipeline.touch(msg, next);
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        if (flush) {
            next.invokeWriteAndFlush(m, promise);
        } else {
            next.invokeWrite(m, promise);
        }
    } else {
        Object task;
        if (flush) {
            task = AbstractChannelHandlerContext.WriteAndFlushTask.newInstance(next, m, promise);
        } else {
            task = AbstractChannelHandlerContext.WriteTask.newInstance(next, m, promise);
        }

        safeExecute(executor, (Runnable)task, promise, m);
    }

}
private void invokeWriteAndFlush(Object msg, ChannelPromise promise) {
        if (this.invokeHandler()) {
            this.invokeWrite0(msg, promise);
            this.invokeFlush0();
        } else {
            this.writeAndFlush(msg, promise);
        }
}
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
        if (msg == null) {
            throw new NullPointerException("msg");
        } else if (this.isNotValidPromise(promise, true)) {
            ReferenceCountUtil.release(msg);
            return promise;
        } else {
            this.write(msg, true, promise);
            return promise;
        }
    }

他只会找到当前handler后的第一个outhandler来处理write过程。然后继续把write过程传递下去。重要一点是重写的write方法是属于handler的,而不是handlercontext,而ctx的的write方法如上,就会将这个write传递给下一个outbound。

public Channel channel() {
    return this.pipeline.channel();
}
//而pipeline的write,是从最后的尾部tail进行写
public final ChannelFuture write(Object msg) {
        return this.tail.write(msg);
}

所以ctx和ctx.channel()或者pipeline的write是有区别的,直接使用是当前位置开始寻找,而后者则是直接到尾部开始寻找。

inboud

inboud责任链模式,从名字可以知道适合链表,他会遍历整个链表,找到合适的人来受理事件就会处理事件,不会再往后走,但如果找不到受理,就会一直往后,直到任务结束。我开始写netty demo的时候就遇到过,channelactive执行第一个后不执行其他的。因为覆盖了fire所以,变相的找到了处理人。

//absChannelHandlerContext里面
public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelActive();
    }
public ChannelHandlerContext fireChannelActive() {
    invokeChannelActive(this.findContextInbound());
    return this;
}
private AbstractChannelHandlerContext findContextInbound() {
        AbstractChannelHandlerContext ctx = this;

        do {
            ctx = ctx.next;
        } while(!ctx.inbound);

        return ctx;
}
static void invokeChannelActive(final AbstractChannelHandlerContext next) {
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelActive();
        } else {
            executor.execute(new Runnable() {
                public void run() {
                    next.invokeChannelActive();
                }
            });
        }

    }


private void invokeChannelActive() {
        if (this.invokeHandler()) {
            try {
                ((ChannelInboundHandler)this.handler()).channelActive(this);
            } catch (Throwable var2) {
                this.notifyHandlerException(var2);
            }
        } else {
            this.fireChannelActive();
        }

    }

这个在责任链模式中会认为它无法处理这个,需要设置到下一个处理。如果没有fire任何执行都会到该地方中止,在此链循环中。

}

private void invokeChannelActive() {
if (this.invokeHandler()) {
try {
((ChannelInboundHandler)this.handler()).channelActive(this);
} catch (Throwable var2) {
this.notifyHandlerException(var2);
}
} else {
this.fireChannelActive();
}

}

这个在责任链模式中会认为它无法处理这个,需要设置到下一个处理。如果没有fire任何执行都会到该地方中止,在此链循环中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值