Netty进阶:手把手教你如何编写一个NIO服务端

简介: Netty进阶:手把手教你如何编写一个NIO服务端

Netty是一款非常优秀的网络编程框架,是对NIO的二次封装,本文将重点剖析Netty服务端的启动流程,深入底层了解如何使用NIO编程服务端。


本文是笔者基于问题的启发式源码阅读技巧的展示,建议带着如下问题开始本文的阅读:


  • ServerBootstrap 的 option 与 childOption 分别有什么作用
  • 服务端IO通道如何绑定事件链。
  • ServerBootstrap 的 handler 方法与 childHandler 方法的区别又是什么?
  • childHandler中的方法在服务端bind方法时会被调用吗?


1、Netty服务端启动示例


基于Netty的使用示例如下:

1ae10a84d30c8d2b73acdd002ff98835.png


代码@1:创建主从多Reactor线程模型的Boss线程组,通常只需要设置一个线程,用于监听客户端的连接请求(OP_ACCEPT)。

代码@2:创建主从多Reactor线程模型的Work线程组,即IO线程组,默认为CPU核数的两倍。

代码@3:创建Netty服务端启动工具类ServerBootstrap。

代码@4:调用group方法设置主从线程组。

代码@5:设置通道的类型,服务端NIO通道类型 NioServerSocketChannel。

代码@6:通过option方法为通道服务端通道选项。

代码@7:通过chiildOption方法为IO通道设置选项。

代码@8:通过ChannelInitializer添加自定义的ChannelHandler,通常包括编码解码器、业务Handler。

代码@9:调用管道的addLast添加自定义编码解码器。

代码@10:调用bind方法绑定到服务端指定接口,绑定完成后则在指点端口上监听客户端的连接。


服务端的核心流程入口为bind方法,接下来我们将详细分析其实现原理,继续体会NIO编程技巧。


2、Netty服务端启动流程


通过跟踪其bind方法,最终将进入到AbstractBootstrap的doBind方法。

98f4e41afbfe1d9e8e5d8c70d29522b8.png

其关键实现点:


代码@1:通过调用initAndRegister方法完成底层网络初始化与通道注册工作。

代码@2:如果初始化与注册工作已完成,则直接调用doBind0方法完成绑定操作。


代码@3:如果初始化与注册工作未完成,则通过regFuture(注册凭证)中添加监听器,等注册完成后再执行doBind0方法。


技巧提示:基于Future异步编程,在主线程中通过调用future.isDone方法判断异步方法是否已完成,如果未完成,通过在该凭证上添加监听器(事件回调),操作完成后执行回调逻辑。

从上面的方法来看服务端的绑定流程包含初始化与绑定两个子流程,接下来将分别深入探讨。


2.1 通道初始化


基于NIO编程,需要先创建通道,然后将其注册到事件选择器,这个过程由 AbstractBootstrap 的 initAndRegister 方法实现。


992c640d3916451f1e0e358d2438e7f9.png

实现的关键点如下:


代码@1:创建NIO服务端通道实现类NioServerSocketChannel的实例。

代码@2:调用 init 方法初始化通道。

代码@3:将通道注册到事件轮询器EventLoopGroup

代码@4::如果通道已注册,但发生了错误则调用通道close方法回收相关资源,如果未


注册成功,则强制清除通道占用的资源,特别是文件占用符。


关于通道的注册逻辑已经在手把手教你如何编写一个NIO客户端 中已详细介绍,故接下来重点关注一下服务端通道的注册流程。


2.1.1 服务端通道初始化流程


AbstractBootstrap 的 init 方法是一个抽象方法,具体有其子类实现:


6f775e8a1901620de6a4de538b41119e.png

服务端通道的初始化代码由ServerBootstrap的init方法。

3b4f21a946245e95c5cbd5fa164296f1.png

Step1:首先将通过ServerBootstrap设置的选项与附加选项初始化到通道中。

a01727c77ca72f1a9079d485c4910712.png

Step2:init 方法的关键点:将 handler 方法设置的事件链,同时新增  ServerBootstrapAcceptor  事件处理方法加入到 NioServerSocketChannel 的事件链,但并没有把 childHandler 中添加的事件链添加到NioServerSocketChannel。


读者朋友们,请停下来思考一下,为什么会这样?从现在可以肯定的是 handler 方法定义的事件处理方法将在与 NioServerSocketChannel 相关的事件发生时其作用。


要解开这个谜题,我们有必要来看看 ServerBootstrapAcceptor 是如何工作的。


2.1.2 ServerBootstrapAcceptor 详解


ServerBootstrapAcceptor 类图如下所示:

f8073314c2e0fe21a8673cd43a8564a9.png

ServerBootstrapAcceptor方法只实现了inbound事件的channelRead事件。在详细探究它之前先看看属性:


  • EventLoopGroup childGroup
    事件执行器组,ServerBootstrap设置的从Reactor线程组,即Work线程组。
  • ChannelHandler childHandler
    ServerBootstrap#childHandler 设置的事件处理器,也就是用户定义的事件处理器。


接下来探究其 channelRead 方法的实现逻辑:

816152e2cfd78f65441fe83ac2fb5e71.png

Step1:channelRead竟然传入的是一个Channel,那这个Channel对象是NioSocketChannel吗?


是的,原来当 OP_ACCEPT 事件触发后,Server端会通过调用ServerSocketChannel 的 accept()方法,将返回一个 NioSocketChannel,读写操作的载体,在NIO中负责数据的读写。


Step2:将通过 childHandler 定义的事件处理器绑定到 NioSocketChannel。


最终完成 NioServerSocketChannel 与 NioSocketChannel 的初始化与事件绑定。


关于 NioSocketChannel 详细的初始化流程蕴含在 ChannelInitializer,其机制已经在 手把手教你如何编写一个NIO客户端  中详细介绍。


2.2 NIO绑定机制



在通道完成初始化与注册后,服务端需要进行端口绑定,由 AbstractBootstrap 的 doBind0 方法实现。


ef863bba23bc584d5d2f7090c0af11a8.png

bind 的核心实现最终是调用 Channel 的 bind 方法,最终由 AbstractChannel 类实现:

b9172ca9cf095de49d6088df8d522e74.png

bind事件将传播,根据Netty事件传播机制,bind 属于 ChannelOutbound事件,最终将调用 HeadContext的bind方法,最终将调用Unsafe的bind方法,更加具体是调用 AbstractChannel的内部类AbstractUnsafe的bind方法,其代码如下所示:

1e1f58fdf7d57aa66985d2390f7d3654.png


doBind 方法是一个抽象方法,NIO服务端的实现:

NioServerSocketChannel。

fc721c4a5bce04fe7bb84de65527e587.png


即最终通过调用NIO底层NioServerSocketChannel 的 bind 方法完成服务端通道的绑定操作,即实现服务端在特定端口监听客户客户端的连接请求。

相关文章
|
7月前
|
Java Maven
【Netty 网络通信】启动通信服务端
【1月更文挑战第9天】【Netty 网络通信】启动通信服务端
|
2月前
|
网络协议 前端开发
netty的TCP服务端和客户端实现
本文介绍了使用Netty框架实现TCP服务端和客户端的步骤,包括添加Netty依赖、编写服务端和客户端的代码,涉及NioEventLoopGroup、ServerBootstrap、Bootstrap、ChannelInitializer等核心组件,以及如何启动服务端监听和客户端连接。
223 4
|
4月前
|
设计模式
Lettuce的特性和内部实现问题之Netty NIO的性能优于BIO的问题如何解决
Lettuce的特性和内部实现问题之Netty NIO的性能优于BIO的问题如何解决
|
1月前
|
消息中间件 缓存 Java
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
|
3月前
|
Java
Netty BIO/NIO/AIO介绍
Netty BIO/NIO/AIO介绍
|
3月前
|
存储 机器人 Linux
Netty(二)-服务端网络编程常见网络IO模型讲解
Netty(二)-服务端网络编程常见网络IO模型讲解
|
4月前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
155 0
|
4月前
|
存储 网络协议 Java
【Netty 神奇之旅】Java NIO 基础全解析:从零开始玩转高效网络编程!
【8月更文挑战第24天】本文介绍了Java NIO,一种非阻塞I/O模型,极大提升了Java应用程序在网络通信中的性能。核心组件包括Buffer、Channel、Selector和SocketChannel。通过示例代码展示了如何使用Java NIO进行服务器与客户端通信。此外,还介绍了基于Java NIO的高性能网络框架Netty,以及如何用Netty构建TCP服务器和客户端。熟悉这些技术和概念对于开发高并发网络应用至关重要。
94 0
|
7月前
|
网络协议 Java 物联网
Spring Boot与Netty打造TCP服务端(解决粘包问题)
Spring Boot与Netty打造TCP服务端(解决粘包问题)
1093 2
|
7月前
|
Java 应用服务中间件 API
从零手写实现 tomcat-06-servlet bio/thread/nio/netty 池化处理
该文介绍了逐步改进的网络服务器实现,从最初的 BIO 基础版到使用线程池的 BIO+Thread,再到 NIO 版本和 NIO+Thread,最后展示了一个使用 Netty 框架的简洁实现。文章旨在说明如何解决阻塞问题,并对比不同模型的优劣,最终推荐使用 Netty 以简化 NIO 编程。