Netty 草稿

Reactor开发模式
SocketChannel 在client端监听op_connect,op_write,op_read事件,在server只监听op_write,op_read事件,ServerSocketChannel在server端运行,只监听op_accept事件

netty对Reactor模式的实现
首先ServerSocketChannel是可以创建子的socketchannel,创建是由
BootstrapChannelFactory这个工厂类根据传入的class反射来创建,
所以
serverBootstrap.group().channel(xxx.class) .channel这个时候就是由上面的工厂类利用反射动态的创建一个channel,绑定在对应的group上

serverBootstrap.group(bossGroup,workerGroup) .channel传进来的会创建一个channel绑定在bossGroup上,然后传进来的ServerSocketChannel可以创建子socketchannel绑定在wokergroup上

再和上面的reactor开发模式对应,就是serversocketchannel绑定在bossGroup上来负责监听op_accept事件,socketchannel绑定在workergroup上来负责监听其他read write事件

粘包和半包

TCP为什么会出现粘包和半包(UDP其实没有这个问题):主要是因为TCP是流协议

常用的解决这种问题的方案:要么短链接,要么长链接中采用封装成桢framing技术

netty对使用Framing粘包半包的支持:
固定长度的方式:一定就按固定长度的来传,不够就补空,解码FixedLengthFrameDecoder
分割符的方式:以指定的分割符来分割,但要考虑内容中如果有这个分割符需要转义,解码DelimiterBasedFrameDecoder
固定长度字段存内容的长度信息:要考虑预留多少位来存储这个长度,解码器是LengthFieldBasedFramedDecoder,编码器LengthFieldPrepender

上面的编码器都继承了ByteToMessageDecoder这个抽象类

ByteToMessageDecoder 继承 ChannelInboundHandlerAdapter
ChannelInboundHandlerAdapter 中有一个channelRead方法处理数据,ByteToMessageDecoder 中就具体实现了channelRead方法
ByteToMessageDecoder中维护了一个ByteBuf类型的数据积累器cumulation,如果是第一笔数据直接赋值给cumulation,不是第一笔的就追加在cumulation后面,然后调

  callDecode(ctx, cumulation, out);
  
  //其中callDecode中会调
  decodeRemovalReentryProtection(ctx, in, out);
  //然后这个又会调decode(ctx, in, out);//这个decode是ByteToMessageDecoder中提供的抽象方法,具体由上面的各种Decoder来实现
  

以FixedLengthFrameDecoder为例子

public class FixedLengthFrameDecoder extends ByteToMessageDecoder {
    private final int frameLength;

    public FixedLengthFrameDecoder(int frameLength) {
        if(frameLength <= 0) {
            throw new IllegalArgumentException("frameLength must be a positive integer: " + frameLength);
        } else {
            this.frameLength = frameLength;
        }
    }

    protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
        Object decoded = this.decode(ctx, in);
        if(decoded != null) {
            out.add(decoded);//每一次解出来的数据放在out中
        }

    }

    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        return in.readableBytes() < this.frameLength?null:in.readSlice(this.frameLength).retain();
        //如果当前积累器就是这里的in中的数据小于定义的长度,不做任何操作,这个时候数据也不够,超过定义的长度,取frameLength长度的数据出来,剩下的就还在积累器中
    }
 
 

一次编码器ByteToMessageDecoder,解决粘包半包问题,将原始的数据流(io.netty.buffer.ByteBuf) 变成用户的数据(io.netty.buffer.ByteBuf这里的用户的数据还是字节流,但是没有粘包半包的问题了)
二次编码器MessageToMessageDecoder, 将一次编码处理后的字节数据(io.netty.buffer.ByteBuf)变成java的对象(java object)

KeepAlive和Idle检测

netty源码核心包
io.netty.transport: 实现协议
io.netty.codec: 实现编解码的部分
io.netty.handler: 各种handler的实现
其他工具类:io.netty.buffer,io.netty.common,io.netty.resolver

netty的启动
从线程角度看,启动的时候一个是启动线程要做几件事情:创建selector,创建并初始化serverSocketChannel,给serversocketChannel从boss group中选择一个NioEventLoop
一个是boss thread要做的几件事情:将serverSocketChannel注册到选择的NioEventLoop上的selector(也就是第一步创建的selector),绑定地址启动,然后就可以开始注册OP_acceptor事件到selector上

从源码的角度上看
new NioEventLoopGroup的时候就会new 一个selector
创建完group之后,调bind(port)异步去绑定地址,在这个bind的过程中有一句

  //类AbstractBootstrap中的dobind方法
 private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();
         //初始化一个serverSocketChannel,register到selector上,返回的是一个regFuture,下面会根据isDone判断是否完成了
        final Channel channel = regFuture.channel();
        if (regFuture.cause() != null) {
            return regFuture;
        }

        if (regFuture.isDone()) {  //已经完成
            // At this point we know that the registration was complete and successful.
            ChannelPromise promise = channel.newPromise();
            doBind0(regFuture, channel, localAddress, promise);
            return promise;
        } else {
            // Registration future is almost always fulfilled already, but just in case it's not.
            //没完成,建一个task绑定在listener上
            final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
            regFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    Throwable cause = future.cause();
                    if (cause != null) {
                        // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
                        // IllegalStateException once we try to access the EventLoop of the Channel.
                        promise.setFailure(cause);
                    } else {
                        // Registration was successful, so set the correct executor to use.
                        // See https://github.com/netty/netty/issues/2586
                        promise.registered();

                        doBind0(regFuture, channel, localAddress, promise);
                    }
                }
            });
            return promise;
        }
    }
    

ServerBootstrapAcceptor:负责接收客户端连接创建连接后的连接初始化工作

netty构建连接
构建连接的主线:
对于boss thread:
1 NioEventLoop中的selector轮询创建连接事件(OP_Accept)
2 创建socket Channel
3 初始化socket channel 并从work group中选择一个NioEventLoop

对于worker thread:
1 将socket channel 注册到上面第3步选择的NioEventLoop的selector(这里怎么选择出来一个NioEventLoop有多种,策略模式)
2 注册OP_READ 到selector

netty 读取数据
selector(多路复用器)接收到OP_read事件
处理这个事件,NioSocketChannel.NioSocketChannelUnsafe.read()
1) 分配一个byte buffer来接收数据,初始化这个buffer是1024个字节
2)从channel接收数据到byte buffer
3)记录实际接收数据的大小,调整下次分配byte buffer的大小
4)触发pipeline.fireChannelRead(bytebuf)把读到的数据传播出去
5)判断接收数据的bytebuffer是否满载而归,是的话继续读取直到没有数据或者已经满来16次,否则本轮读取结束,等待下次OP_read事件

读取数据的关键代码
SocketChannelImpl.read(java.nio.ByteBuffer)
NioSocketChannel.read是读取数据,NioServerSocketChannel.read是创建连接
pipeline.fireChannelReadComplete() 一次读事件完成
pipeline.fireChannelRead(bytebuf) 一次读数据完成
一次读事件包括多个读数据

netty处理业务逻辑
inboud和outbound
head - handler1 - handler2 - tail
inbound是从head到tail,outbound是从tail到head
每一个handler都是实现了ChannelInboundHandler的

你可能感兴趣的:(netty)