netty学习笔记
Netty是一款异步的事件驱动的网络应用程序框架,支持快速地开发可维护的高性能的面向协议的服务器和客户端。
Netty的主要构件块:
- Channel是javaNIO的一个基本构造。可以把Channel看作是传入或者传出数据的载体。
- 回调其实就是一个方法,一个指向已经被提供给另一个方法的方法的引用,使得接受回调的方法可以在适当时候调用前者。类A的a()方法调用类B的b()方法,类B的b()方法执行完毕主动调用类A的callback()方法。
- Future提供了另一种在操作完成时通知应用程序的方式。Future 本身是一种被广泛运用的并发设计模式,可在很大程度上简化需要数据流同步的并发应用开发。是回调的一个更加精细的版本。(主要为了体现异步执行)
- 事件和ChannelHandler。Netty使用不同的事件来通知我们状态的改变或者是操作的状态,可通过已经发生的事件来触发适当的动作。Netty的ChannelHandler为处理器提供了基本的抽象,其实例类似于一种为了响应特定事件而被执行的回调。通过ChannelHandler来构建应用程序的逻辑。
所有的Netty服务器都需要两部分:
- 至少一个ChannelHandler,该组件实现了服务器对从客户端接收的数据的处理,即它的业务逻辑
- 引导,这是配置服务器的启动代码,至少它会将服务器绑定到它要监听连接请求的端口上。
服务器端:
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
浙公网安备 33010602011771号