初识Netty——服务端、客户端创建

 Netty服务端创建

先来看server bootstrap:是socket服务端的启动辅助类,用户通过它可以方便的创建netty的服务端

 Netty服务端创建的步骤:

  1. 创建server bootstrap实例
  2. 设置并绑定reactor线程池。Netty 的 Reactor 线程池是 EventLoopGroup,它实际就是 EventLoop 的数组。EventLoop 的职责是处理所有注册到本线程多路复用器Selector 上的Channel,Selector 的轮询操作由绑定的 EventLoop 线程 run 方法驱动,在一,个循环体内循环执行。
  3. 设置并绑定服务端channel,作为NIO服务端,需要创建serversocketchannel,对应netty实现为Nioserver socket channel。Serverbootstrap也提供了channel方法用于指定服务端channel的类型,netty通过工厂类,利用反射创建Nioserver socket channel对象

  1. 创建channelpipeline,本质是一个负责处理网络事件的职责链,负责执行channel handler
  2. 往Channelpipeline中添加channel handler
  3. 绑定并监听启动端口
  4. Selector轮询。由Reactor 线程NioEventLoop 负责调度和执行Selector 轮询操作,选择准备就绪的Channel集合
  5. 当轮询到就绪的channel之后,就由reactor线程NioEventloop执行channelpipeline的相应方法,最终执行channelhandler
  6. 执行channelhandler和用户添加的handler

Netty服务端创建源码分析

首先通过构造函数创建server bootstrap实例,创建两个Event loop group

NioEventLoopGroup 实际就是 Reactor 线程池,负责调度和执行客户端的接入、网络读写事件的处理、用户自定义任务和定时任务的执行。通过ServerBootstrap的 group方法将两个EventLoopGroup实例传入

创建Nioserversocketchannel,根据channel类型通过反射创建channel实例

服务端启动的最后一步就是绑定本地端口,启动服务

Nioserversocketchannel创建成功后,对他进行初始化

第一步设置socket参数和channel的附加属性

第二步将handler加入到channelpipeline中

之后最后一步就是注册Nioserversocketchannel到reactor线程的多路复用器上

先看看目前channelpipeline的组成

到这里服务端启动部分源码已经分析完成

 客户端接入源码

当多路复用器检测到新的准备就绪的Channel时,默认执行 processSelectedKeysOptimized 方法

由于监听的是客户端那边的连接操作,所以执行Unsafe.read()方法

分析domessage方法,实际上就是接受新的客户端连接并创建Niosocketchannel

接受新的客户端连接后,触发channelpipeline的channelread方法

Niosocketchannel的注册方法与server socket channel的一致,也是将channel注册到reactor线程的多路复用器上

执行完注册操作之后,紧接着会触发ChanncelReadComplete 事件。我们继续分析ChannelReadComplete 在ChannelPipeline 中的处理流程:Netty 的Header 和Tail 本身不关注 ChannelReadComplete 事件就直接透传,执行完ChannelIReadComplete 后,接着执行,PipeLine 的 read()方法,最终执行HeadHandler的 read()方法HeadHandlerread()方法的代码已经在之前的小节介绍过,用来将网络操作位修改为操作。创建 NioSocketChannel 的时候已经将AbstractNioChanncl 的 readInterestOp 设置为OP_READ,这样,执行 selectionKey.interestOps(interestOpslreadInterestOp)操作时就会把操作位设置为OP_READ

至此,新接入的客户端连接处理完成,可以进行读写

 客户端创建

Bootstrap是客户端创建工具类

Netty客户端创建流程:

步骤1:用户线程创建Bootstrap实例,通过API设置创建客户端相关的参数,异步发起客户端连接

步骤2:创建处理客户端连接、I/O读写的Reactor 线程组NioEventLoopGroup。可以通过构造函数指定I/O线程的个数,默认为CPU 内核数的2倍;

步骤3:通过 Bootstrap的ChanneIFactory 和用户指定的Channel类型创建用于客户端连接的NioSocketChannel,它的功能类似于JDKNIO类库提供的SocketChannel

步骤4:创建默认的Channel HandlerPipeline,用于调度和执行网络事件;

步骤5:异步发起TCP连接,判断连接是否成功。如果成功,则直接将NioSocketChannel注册到多路复用器上,监听读操作位,用于数据报读取和消息发送;如果没有立即连接成功,则注册连接监听位到多路复用器,等待连接结果;

步骤6:注册对应的网络监听状态位到多路复用器;

步骤7:由多路复用器在I/O现场中轮询各Channel,处理连接结果;

步骤8:如果连接成功,设置Future 结果,发送连接成功事件,触发 ChanneIPipcline执行;

步骤9:由ChanneIPipeline 调度执行系统和用户的ChanneIHandler,执行业务逻辑

Boostrap提供了设置IO线程组的接口:

TCP参数设置接口:

Channel接口:用于指定客户端使用的channel接口(反射机制)

设置handler接口:

发起客户端连接:

 客户端连接操作

首先要创建和初始化Niosocketchannel

从NioEventLoopGroup中 获 取 NioEventLoop,然后使用其作为参数创建NioSocketChannel

初始化channel后,将其注册到selector上

发起异步TCP连接

Doconnect0最终调用head handler的connect方法

需要注意的是,SocketChannel 执行 connect()操作后有以下三种结果:(1)连接成功,返回True(2)暂时没有连接上,服务端没有返回ACK 应答,连接结果不确定,返回False;(3)连接失败,直接抛出I/O异常。如 果 是 第 二 种 结 果,需 要 将NioSocketChannel 中 的 selectionKey 设 置 为OP_ CONNECT,监听连接结果。异步连接返回之后,需要判断连接结果,如果连接成功,则触发 ChannelActive事件

ChanelActive 事件最终会将 NioSocketChannel 中的 selectionKey 设置为SelectionKey.OP_READ,用于监听网络读操作。如果没有立即连接上服务端,则注册 SelectionKey.OP_CONNECT 到多路复用器

如果过程发生异常,则关闭链路

 异步连接结果通知

当服务端返回握手应答之后,对连接结果进行判断

进一步看finishconnect方法:

Dofinishconnect方法用于判断JDK的socketChannel的连接结果

连接成功之后调用fulfillconnectPromiose方法,触发链路激活事件

Fire Channel Active方法主要修改网络监听位为读操作

 客户端连接超时机制

首先,用户在创建Netty客户端的时候,可以通过ChannelOption.CONNECT TIMEOUT_MILIS配置项设置连接超时时间

发起连接的同时,启动连接超时检测定时器

如果在超时之前获取到结果,就删除定时器

无论是否连接成功,只要获取到结果,就删除连接超时定时器

### Netty 客户端使用教程 Netty 是一个高性能、异步事件驱动的网络应用框架,适用于开发可维护的高性能协议服务端客户端[^1]。下面介绍如何创建并运行一个基本的 Netty 客户端。 #### 创建 Netty 客户端实例 要建立一个简单的 Netty 客户端程序,首先需要引入必要的依赖项到项目的构建工具中(Maven 或 Gradle),接着编写如下所示的基础代码: ```java import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; public class SimpleClient { public static void main(String[] args) throws Exception { String host = "localhost"; int port = 8080; EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); // (1) b.group(group) // (2) .channel(NioSocketChannel.class) // (3) .handler(new ClientInitializer()); // (4) ChannelFuture f = b.connect(host, port).sync(); // (5) f.channel().closeFuture().sync(); // (6) } finally { group.shutdownGracefully(); // (7) } } } ``` 上述代码片段解释了启动过程的关键部分: - `Bootstrap` 对象用于设置客户端参数; - 设置线程池为 `NioEventLoopGroup` 类型; - 指定通道类型为基于 NIO 的套接字; - 添加初始化处理器链; - 尝试连接至指定地址的服务端; - 阻塞当前线程直到连接关闭; - 清理资源。 #### 处理异常与错误 对于可能出现的各种异常状况,比如 I/O 错误或是内存不足等问题,在 Netty 中可以通过覆盖特定的方法来进行捕获和响应。例如,当检测到远程主机已断开时,将会触发 `channelInactive()` 方法;而遇到未预见的问题,则会进入 `exceptionCaught()` 函数体内处理逻辑[^3]。 为了避免重复尝试重新连接的情况发生——即两个回调函数内均存在相同的重连操作——建议只在一个地方实施此行为,并确保其他位置不会再次发起不必要的请求[^4]。 #### 端口冲突排查指南 如果应用程序无法绑定预期使用的端口,可能是由于该端口已被另一个进程占用所致。此时应按照以下步骤进行诊断: 1. 参考官方文档确认默认监听端口; 2. 利用操作系统命令行工具查询是否存在竞争情况; 3. 调整配置文件里的设定值或终止干扰性的后台任务以解决问题[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值