Netty 2.请求处理线

2.1 核心包速览:

  • io.netty.transport (Java nio实现) channel selector bootstrap
    • io.netty.transport.epoll/kqueue/unix.common/sctp
  • io.netty.codec (编解码,支持各种协议)
    • io.netty.codec.dns/haproxy/http/http2/memcache/mqtt/redis/smtp/socks/stomp/xml
  • io.netty.handler (Reactor 模型 Handler)
    • io.netty.handler.proxy
  • io.netty.buffer (多路复用 ->缓冲)
  • io.netty.common
  • io.netty.resolver (解析)
    • io.netty.resolver.dns

2.2 启动服务:

  • 主线:
    • our thread:
      创建selector
      创建server socket channel
      初始化server socket channel
      给server socket channel 从boss group中选择一个NioEventLoop
    • boss thread:
      将server socket channel 注册到选择的NioEventLoop的selector
      绑定地址启动
      注册接受连接事件(OP_ACCEPT)到selector上
    • 启动服务的本质: 初始化selector 启动ServerSocketChannel 绑定端口
      Selector selector = sun.nio.ch.SelectorProviderImpl.openSelector()
      ServerSocketChannel serverSocketChannel = provider.openServerSocketChannel()
      selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this)
      javaChannel().bind(localAddress, config.getBacklog())
      selectionKey.interestOps(OP_ACCEPT)
  • 知识点:
    • Selector实在new NioEventLoopGroup()(创建一批NioEventLoop)的创建
    • 第一次Register并不是监听OP_ACCEPT,而是0:
      selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this)
    • 最终监听 OP_ACCEPT 是通过 bind 完成后的fireChannelActive()来触发的
    • NioEventLoop是通过Register操作的执行来完成启动的
    • 类似ChannelInitializer,一些Handler可以设计成一次性的,用完就移除,例如授权

2.3 构建连接:

  • boss thread
    NioEventLoop中的selector轮询创建连接事件(OP_ACCEPT)
    创建socket channel
    初始化 socket channel 并从 worker group中选择一个NioEventLoop
  • worker thread
    将socket channel 注册到选择的NioEventLoop的selector
    注册读事件(OP_READ)到selector上
  • 知识点:
    * 接收连接本质:
    selector.select()/selectNow()/select(timeoutMillis)发现OP_ACCEPT事件,处理:
    SocketChannel socketChannel = serverSocketChannel.accepot()
    selectionKey = javaChannel().register(eventLoop().unwrappedSelector(),0,this);
    selectionKey.interestOps(OP_READ)
    * 创建连接的初始化和注册是通过pipeline.fireChannelRead在ServerBootstrapAcceptor中完成
    * 第一次Register并不是监听OP_READ 而是0:
    selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this)
    * 最终监听 OP_READ 是通过 Register 完成后的fireChannelActive来触发的
    * Worker‘s NioEventLoop是通过Register操作执行来启动
    * 接收连接的读操作,不会尝试读取更多次((16次)

2.4 接收数据

  • 读数据技巧
    1.自适应数据大小的分配器(AdaptiveRecvByteBufAllocator)
    2.连续读(defaultMaxMessagesPerRead)
  • 主线:
    多路复用器Selector接收到OP_READ事件
    处理OP_READ事件:NioSocketChannel.NioSocketChannelUnsafe.read()
    分配一个初始的1024字节的byte buffer来接受数据
    从Channel接受数据到byte buffer
    记录实际接受数据大小,调整下次分配byte buffer大小
    触发pipeline.fireChannelRead(byteBuf)把读取到的数据传播出去
    判断接受byte buffer 是否满载而归:是,尝试继续读取直到没有数据或满16次
    否,结束本轮读取,等待下次OP_READ事件
  • 知识点:
    * 读取数据本质:sun.nio.ch.SocketChannelImpl#read(java.nio.ByteBuffer)
    * NioSocketChannel read() 是读数据 NioServerSocketChannel read() 是创建连接
    * pipeline.fireChannelReadComplete() 一次读事件处理完成
    * 为什么最多只尝试读取16次:雨露均沾
    * AdaptiveRecvByteBufAllocator 对 bytebuf的猜测:放大果断 缩小谨慎 (需要连续2次判断)

2.5 业务处理:

  • 触发pipeline.fireChannelRead(byteBuf) 把读取到的数据传播出去
  • 知识点:
    * 处理业务本质:数据在pipeline中所有的handler的channelRead()执行过程
    * Handler要实现 io.netty.channel.ChannelInboundHandler#channelRead(ChannelHandlerContext ctx,Object msg)且不能加注解@Skip才能被执行到
    * 中途可退出,不保证执行到TailHandler
    * 默认处理线程就是Channel绑定的NioEventLoop线程,也可以设置其他:
    pipeline.addLast(new UnorderedThreadPoolEventExecutor(10), serverHandler)

2.6 发送数据:

  • 写数据的三种方式
    * write 写到一个buffer
    * flush 把buffer里的数据发送出去
    * writeAndFlush 写到buffer,立马发送
    * write和Flush之间有个ChannelOutboundBuffer
  • 写数据要点:
    * 1. 对方仓库爆仓时,送不了的时候,会停止送,协商等电话通知什么时候好了,再送。
    Netty写数据,写不进去时,会停止写,然后注册一个OP_WRITE事件,来通知什么时候可以写进去了再写
    * 2. 发送数据时,对方仓库都直接收下,这个时候再发送快递时,可以尝试发送更多的快递
    Netty批量写数据时,如果想写的都写进去了,接下来的尝试写更能多 调整 maxBytesPerGatherWrite
    * 3. 发送快递时,发到某个地方的快递特别多,会连续发,但快递车有限,考虑其他地方
    Netty只要有数据要写,且能写的出去,则一直尝试,直到写不出去或者满16次 writeSpinCount
    * 4. 揽收太多,发送来不及,爆仓,这时出个告示牌:收不下了,最好过2天再来邮寄
    Netty待写数据太多,超过一定的水位线writeBufferWaterMark.high() 会将可写的标志位改成false,让应用端自己做决定要不要发送数据
    • 主线:
      1.Write ———写数据到buffer
      ChannelOutboundBuffer#addMessage
      2.Flush ——发送buffer里面的数据:
      AbstractChannel.AbstractUnsafe#flush
      准备数据:ChannelOutboundBuffer#addFlush
      发送:NioSocketChannel#doWrite
      3. 写本质:
      Single write: sun.nio.ch.SocketChannelImol#write(java.nio.ByteBuffer)
      gathering write: sun.nio.ch.SocketChannelImpl#write(java.nio.ByteBuffre[],int,int)
      4.写数据写不进去时,会停止写,注册一个OP_WRITE事件,来通知什么时候可以写进去
      5.OP_WRIET不是说有数据可写,而是说可以写进去,所以正常情况,不能注册,否则一直触发
      6.批量写数据时,如果尝试写的都写进去了,接下来会尝试写更多(maxBytesPerGatheringWrite)
      7.只要有数据要写,且能写,则一直尝试,直到16次writeSpinCount 写16次还没有写完,就直接schedule一个task来继续写,而不是注册写事件来触发
      8.待写数据太多,超过一定的水位线writeBufferWaterMark.high() 会将可写的标志改为false,让应用端自己做决定要不要继续写
      9.channelHandlerContext.channel().write() 从TailContext开始执行
      channelHandlerContext.write() 从当前的Context开始

2.7 断开连接:

  • 主线:
    多路复用器Selector 接收到OP_READ事件:
    处理OP_READ事件:NioSocketChannel.NioSocketChannelUnsafe.read()
    接收数据
    判断接受的数据大小是否<0 如果是,说明是关闭,开始执行关闭
    关闭channel 包含cancel多路复用器的key
    清理消息:不接受新信息,fail掉所有queue中消息
    触发fireChannelnactive和fireChannelUnregistered
  • 知识点:
    关闭连接本质:
    java.nio.channels.spi.AbstractInterruptibleChannel#close
    java.nio.channels.SelectionKey#cancel
    要点:
    关闭连接,会触发OP_READ方法。读取字节数是-1代表关闭
    数据读取进行时,强行关闭,触发IO Exception 进而执行关闭
    Channel的关闭包含了SelectionKey的cancel

2.8 关闭服务:

  • bossGroup.shutdownGracefully(); workGroup.shutdownGracefully() 关闭所有Group中的NioEventLoop:
    修改NioEventLoop的State标志位
    NioEventLoop判断State执行退出
  • 知识点:
    * 关闭服务本质:
    * 关闭所有连接及Selector:
    java.nio.channels.Selectors#keys
    java.nio.channels.spi.AbstractInterruptibleChannel#close
    java.nio.channels.SelectionKey#cancel
    selector.close()
    * 关闭所有线程:退出循环体 for(;😉
    * 关闭服务要点:
    优雅:DEFAULT_SHUTDOWN_QUIET_PERIOD
    可控:DEFAULT_SHUTDOWN_TIMEOUT
    先不接活 后尽量干完手头的活 (先关boss 后关worker: 不是100% 保证)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值