netty学习笔记

Netty是一款异步的事件驱动的网络应用程序框架,支持快速地开发可维护的高性能的面向协议的服务器和客户端。

Netty的主要构件块:

  1. Channel是javaNIO的一个基本构造。可以把Channel看作是传入或者传出数据的载体。
  2. 回调其实就是一个方法,一个指向已经被提供给另一个方法的方法的引用,使得接受回调的方法可以在适当时候调用前者。类A的a()方法调用类B的b()方法,类B的b()方法执行完毕主动调用类A的callback()方法。
  3. Future提供了另一种在操作完成时通知应用程序的方式。Future 本身是一种被广泛运用的并发设计模式,可在很大程度上简化需要数据流同步的并发应用开发。是回调的一个更加精细的版本。(主要为了体现异步执行)
  4. 事件和ChannelHandler。Netty使用不同的事件来通知我们状态的改变或者是操作的状态,可通过已经发生的事件来触发适当的动作。Netty的ChannelHandler为处理器提供了基本的抽象,其实例类似于一种为了响应特定事件而被执行的回调。通过ChannelHandler来构建应用程序的逻辑。

所有的Netty服务器都需要两部分:

  1. 至少一个ChannelHandler,该组件实现了服务器对从客户端接收的数据的处理,即它的业务逻辑
  2. 引导,这是配置服务器的启动代码,至少它会将服务器绑定到它要监听连接请求的端口上。

服务器端:

ChannelHandler是一个接口族的父接口,实现负责接收并响应事件通知,需要实现ChannelInboundHandler接口,用来定义响应入站事件的方法。

channelRead()——对于每个传入的消息都要调用;

channelReadComplete()——通知ChannelInboundHandler最后一次对channelRead()的调用是当前批量读取中的最后一条消息

exceptionCaught()——在读取操作期间,有异常抛出时会调用。

客户端:

channelActive()——在到服务器的连接已经建立之后将被调用

channelRead0()——当从服务器收到一条消息时被调用

exceptionCaught()——在处理过程中引发异常时被调用

构造一个 NioSocketChannel 所需要做的工作:

  • 调用 NioSocketChannel.newSocket(DEFAULT_SELECTOR_PROVIDER) 打开一个新的 Java NIO SocketChannel
  • AbstractChannel(Channel parent) 中初始化 AbstractChannel 的属性:
    • parent 属性置为 null
    • unsafe 通过newUnsafe() 实例化一个 unsafe 对象, 它的类型是 AbstractNioByteChannel.NioByteUnsafe 内部类
    • pipeline 是 new DefaultChannelPipeline(this) 新创建的实例. 这里体现了:Each channel has its own pipeline and it is created automatically when a new channel is created.
    • AbstractNioChannel 中的属性:
      • SelectableChannel ch 被设置为 Java SocketChannel, 即 NioSocketChannel#newSocket 返回的 Java NIO SocketChannel.
      • readInterestOp 被设置为 SelectionKey.OP_READ
      • SelectableChannel ch 被配置为非阻塞的 ch.configureBlocking(false)
      • NioSocketChannel 中的属性:
        • SocketChannelConfig config = new NioSocketChannelConfig(this, socket.socket())

NioSocketChannel.newUnsafe 方法会返回一个 NioSocketChannelUnsafe 实例

MultithreadEventExecutorGroup 内部维护了一个 EventExecutor 数组, Netty 的 EventLoopGroup 的实现机制其实就建立在 MultithreadEventExecutorGroup 之上. 每当 Netty 需要一个 EventLoop 时, 会调用 next() 方法获取一个可用的 EventLoop.

  • EventLoopGroup(其实是MultithreadEventExecutorGroup) 内部维护一个类型为 EventExecutor children 数组, 其大小是 nThreads, 这样就构成了一个线程池
  • 如果我们在实例化 NioEventLoopGroup 时, 如果指定线程池大小, 则 nThreads 就是指定的值, 反之是处理器核心数 * 2
  • MultithreadEventExecutorGroup 中会调用 newChild 抽象方法来初始化 children 数组
  • 抽象方法 newChild 是在 NioEventLoopGroup 中实现的, 它返回一个 NioEventLoop 实例.
  • NioEventLoop 属性:
    • SelectorProvider provider 属性: NioEventLoopGroup 构造器中通过 SelectorProvider.provider() 获取一个 SelectorProvider
    • Selector selector 属性: NioEventLoop 构造器中通过调用通过 selector = provider.openSelector() 获取一个 selector 对象
  • 首先在 AbstractBootstrap.initAndRegister中, 通过 group().register(channel), 调用 MultithreadEventLoopGroup.register 方法
  • 在MultithreadEventLoopGroup.register 中, 通过 next() 获取一个可用的 SingleThreadEventLoop, 然后调用它的 register
  • 在 SingleThreadEventLoop.register 中, 通过 channel.unsafe().register(this, promise) 来获取 channel 的 unsafe() 底层操作对象, 然后调用它的 register.
  • 在 AbstractUnsafe.register 方法中, 调用 register0 方法注册 Channel
  • 在 AbstractUnsafe.register0 中, 调用 AbstractNioChannel.doRegister 方法
  • AbstractNioChannel.doRegister 方法通过 javaChannel().register(eventLoop().selector, 0, this) 将 Channel 对应的 Java NIO SockerChannel 注册到一个 eventLoop 的 Selector 中, 并且将当前 Channel 作为 attachment.

Channel 注册过程所做的工作就是将 Channel 与对应的 EventLoop 关联, 因此这也体现了, 在 Netty 中, 每个 Channel 都会关联一个特定的 EventLoop, 并且这个 Channel 中的所有 IO 操作都是在这个 EventLoop 中执行的; 当关联好 Channel 和 EventLoop 后, 会继续调用底层的 Java NIO SocketChannel 的 register 方法, 将底层的 Java NIO SocketChannel 注册到指定的 selector 中. 通过这两步, 就完成了 Netty Channel 的注册过程.

 

  • 调用 NioServerSocketChannel.newSocket(DEFAULT_SELECTOR_PROVIDER) 打开一个新的 Java NIO ServerSocketChannel
  • AbstractChannel(Channel parent) 中初始化 AbstractChannel 的属性:
    • parent 属性置为 null
    • unsafe 通过newUnsafe() 实例化一个 unsafe 对象, 它的类型是 AbstractNioMessageChannel#AbstractNioUnsafe 内部类
    • pipeline 是 new DefaultChannelPipeline(this) 新创建的实例.
    • AbstractNioChannel 中的属性:
      • SelectableChannel ch 被设置为 Java ServerSocketChannel, 即 NioServerSocketChannel#newSocket 返回的 Java NIO ServerSocketChannel.
      • readInterestOp 被设置为 SelectionKey.OP_ACCEPT
      • SelectableChannel ch 被配置为非阻塞的 ch.configureBlocking(false)
      • NioServerSocketChannel 中的属性:
        • ServerSocketChannelConfig config = new NioServerSocketChannelConfig(this, javaChannel().socket())

对于 Outbound事件:

  • Outbound 事件是请求事件(由 Connect 发起一个请求, 并最终由 unsafe 处理这个请求)
  • Outbound 事件的发起者是 Channel
  • Outbound 事件的处理者是 unsafe
  • Outbound 事件在 Pipeline 中的传输方向是 tail -> head.
  • 在 ChannelHandler 中处理事件时, 如果这个 Handler 不是最后一个 Hnalder, 则需要调用 ctx.xxx (例如 ctx.connect) 将此事件继续传播下去. 如果不这样做, 那么此事件的传播会提前终止.
  • Outbound 事件流: Context.OUT_EVT -> Connect.findContextOutbound -> nextContext.invokeOUT_EVT -> nextHandler.OUT_EVT -> nextContext.OUT_EVT

对于 Inbound 事件:

  • Inbound 事件是通知事件, 当某件事情已经就绪后, 通知上层.
  • Inbound 事件发起者是 unsafe
  • Inbound 事件的处理者是 Channel, 如果用户没有实现自定义的处理方法, 那么Inbound 事件默认的处理者是 TailContext, 并且其处理方法是空实现.
  • Inbound 事件在 Pipeline 中传输方向是 head -> tail
  • 在 ChannelHandler 中处理事件时, 如果这个 Handler 不是最后一个 Hnalder, 则需要调用 ctx.fireIN_EVT (例如 ctx.fireChannelActive) 将此事件继续传播下去. 如果不这样做, 那么此事件的传播会提前终止.
  • Outbound 事件流: Context.fireIN_EVT -> Connect.findContextInbound -> nextContext.invokeIN_EVT -> nextHandler.IN_EVT -> nextContext.fireIN_EVT

 

posted on 2018-03-12 16:53  Rainrun  阅读(152)  评论(0)    收藏  举报