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

Java 网络编程

早期的 Java API 只支持由本地系统套接字库提供的所谓的阻塞函数,下面的代码展示了一个使用传统 Java API 的服务器代码的普通示例

// 创建一个 ServerSocket 用以监听指定端口上的连接请求
ServerSocket serverSocket = new ServerSocket(5000);
// 对 accept 方法的调用将被阻塞,直到一个连接建立
Socket clientSocket = serverSocket.accept();
// 这些流对象都派生于该套接字的流对象
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
String request, response;
// 客户端发送了 "Done" 则退出循环
while ((request = in.readLine()) != null) {
if ("Done".equals(request)) {
break;
}
// 请求被传递给服务器的处理方法
response = processRequest(request);
// 服务器的响应被发送给客户端
out.println(response);
}

这段代码只能同时处理一个连接,要管理多个客户端,就要为每个新的客户端 Socket 创建一个新的 Thread,让我们来考虑一下这种方案的影响:

  • 在任何时候都会有大量线程处于休眠状态,造成资源浪费
  • 需要为每个线程的调用栈都分配内存
  • 线程的上下文切换会带来开销

这种并发方案对于中小数量的客户端还算理想,但不能很好地支持更大的并发,幸运的是,还有另一种解决方案

Java NIO

NIO(Non-blocking I/O,也称 New I/O),是一种同步非阻塞的 I/O 模型,也是 I/O 多路复用的基础。传统的 IO 流是阻塞的,这意味着,当一个线程调用读或写操作时,线程将被阻塞,直至数据被完全读取或写入。NIO 的非阻塞模式,使一个线程进行读或写操作时,如果目前无数据可用时,就不做操作,而不是保持线程阻塞,所以直至数据就绪以前,线程可以继续做其他事情

class java.nio.channels.Selector 是 Java 非阻塞 IO 实现的关键。它使用事件通知 API 以确定在一组非阻塞套接字中有哪些已经就绪并能进行 IO 相关操作。因为可以在任何时间点任意检查读操作或写操作的完成情况,所以单一线程可以处理多个并发的连接

与阻塞 IO 模型相比,这种模型提供了更好的资源管理:

  • 使用较少的线程便可以处理许多连接,减少内存管理和上下文切换所带来的开销
  • 当没有 IO 操作需要处理时,线程也可以用户其他任务

尽管 Java NIO 如此高效,但要做到正确和安全并不容易,在高负载下可靠和高效地处理和调度 IO 操作是一项烦琐且容易出错的任务,所幸,有 Netty 能帮助我们解决问题

Netty

Netty 是一个广泛使用的 Java 网络编程框架,它隐藏了 Java 高级 API 背后的复杂性,提供一个易于使用的 API 的客户端/服务器框架。在这里我们将要讨论 Netty 的主要构件:

1. Channel

Channel 是 Java NIO 的一个基本构造,可以把 Channel 简单看作是传入(入站)或传出(出站)数据的载体。因此,它可以被打开或关闭,连接或断开连接

2. 回调

一个回调其实就是一个方法,Netty 使用回调来处理事件,当一个回调被触发时,相关的事件可以被 ChannelHandler 的实现处理。下面的代码展示了这样一个例子:当一个新的连接已经建立,ChannelHandler 的 channelActive() 的回调方法将会被调用,并打印出一条信息

public class ConnectHandler extends ChannelInboundHandlerAdapter {

    @override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Client " + ctx.channel().remoteAddress() + " connected");
}
}

3. Future

Future 提供了另一种在操作完成时通知应用程序的方式,它将在未来的某个时刻完成,并提供对其结果的访问。虽然 Java 提供了 Future 的一种实现,但需要手动检查对应操作是否已经完成,或一直阻塞直到它完成,十分烦琐。Netty 提供了自己的实现 ChannelFuture,用于执行异步操作的时候使用

ChannelFuture 提供了几种额外的方法,这些方法使得我们能够注册一个或多个 ChannelFutureListener 实例。监听器的回调方法 operationComplete() 将会在对应操作完成时被调用。每个 Netty 的 IO 操作都会返回一个 ChannelFuture,它们都不会被阻塞,可以同时做其他工作,更加有效的利用资源

Channel channel = ...;
// 异步地连接到远程结点
ChannelFuture future = channel.connect(new InetSocketAddress("192.168.0.1", 25));
// 注册一个 ChannelFutureListener
future.addListener(new ChannelFutureListener() { @Override
public void operationComplete(ChannelFuture future) {
// 如果操作成功
if(future.isSuccess()) {
...
} else {
// 发生异常
...
}
}
});

4. 事件和 ChannelHandler

Netty 使用不同的事件来触发合适的动作,事件是按照与入站或出站数据流的相关性进行分类的,可能由入站数据或相关状态更改而触发的事件包括:

  • 连接已被激活或失效
  • 数据读取
  • 用户事件
  • 错误事件

出站事件是未来将会触发的某个动作的操作结果,包括:

  • 打开或关闭到远程结点的连接
  • 将数据写到或冲刷到套接字

每个事件都可分发给 ChannelHandler 类中的某个用户实现的方法,下图展示了一个事件如何被一个 ChannelHandler 链处理

Netty 提供了大量预定义的 ChannelHandler 实现,供开发者使用

5. 总结

Netty 的异步编程模型是建立在 Future 和回调的概念之上的,将事件派发到 ChannelHandler 拦截并高速地转换入站数据和出站数据,开发者只需要提供回调或者利用返回的 Future 即可。Netty 通过触发事件将 Selector 从应用程序中抽象出来,消除了本需手写的派发代码。在内部,将会为每个 Channel 分配一个 EventLoop,用于处理所有事件,包括:

  • 注册感兴趣的时间
  • 将事件派发给 ChannelHandler
  • 安排进一步的动作

EventLoop 本身只有一个线程驱动,处理了一个 Channel 的所有 IO 事件,这个简单而强大的设计消除了可能在 ChannelHandler 实现中需要进行同步的任何顾虑,因此我们只需专注于提供正确的逻辑即可

Netty 框架学习 —— 初识 Netty的更多相关文章

  1. Netty 框架学习 —— 基于 Netty 的 HTTP/HTTPS 应用程序

    通过 SSL/TLS 保护应用程序 SSL 和 TLS 安全协议层叠在其他协议之上,用以实现数据安全.为了支持 SSL/TLS,Java 提供了 javax.net.ssl 包,它的 SSLConte ...

  2. Netty 框架学习 —— 传输

    概述 流经网络的数据总是具有相同的类型:字节,这些字节如何传输主要取决于我们所说的网络传输.用户并不关心传输的细节,只在乎字节是否被可靠地发送和接收 如果使用 Java 网络编程,你会发现,某些时候当 ...

  3. Netty 框架学习 —— 引导

    概述 前面我们学习了 ChannelPipeline.ChannelHandler 和 EventLoop 之后,接下来的问题是:如何将它们组织起来,成为一个可实际运行的应用程序呢?答案是使用引导(B ...

  4. Netty 框架学习 —— 编解码器框架

    编解码器 每个网络应用程序都必须定义如何解析在两个节点之间来回传输的原始字节,以及如何将其和目标应用程序的数据格式做相互转换.这种转换逻辑由编解码器处理,编解码器由编码器和解码器组成,它们每种都可以将 ...

  5. Netty 框架学习 —— 第一个 Netty 应用

    概述 在本文,我们将编写一个基于 Netty 实现的客户端和服务端应用程序,相信通过学习该示例,一定能更全面的理解 Netty API 该图展示的是多个客户端同时连接到一台服务器.客户端建立一个连接后 ...

  6. Netty 框架学习 —— EventLoop 和线程模型

    EventLoop 接口 Netty 是基于 Java NIO 的,因此 Channel 也有其生命周期,处理一个连接在其生命周期内发生的事件是所有网络框架的基本功能.通常来说,我们使用一个线程来处理 ...

  7. Netty 框架学习 —— 预置的 ChannelHandler 和编解码器

    Netty 为许多提供了许多预置的编解码器和处理器,几乎可以开箱即用,减少了在烦琐事务上话费的时间和精力 空闲的连接和超时 检测空闲连接以及超时对于释放资源来说至关重要,Netty 特地为它提供了几个 ...

  8. Netty 框架学习 —— Netty 组件与设计

    Channel.EventLoop 和 ChannelFuture 这一节将对 Channel.EventLoop 和 ChannelFuture 类进行讨论,它们组合在一起,可以被认为是 Netty ...

  9. Netty 框架学习 —— ByteBuf

    概述 网络数据的基本单位总是字节,Java NIO 提供了 ByteBuffer 作为它的字节容器,但这个类的使用过于复杂.Netty 的 ByteBuf 具有卓越的功能性和灵活性,可以作为 Byte ...

  10. Netty 框架学习 —— ChannelHandler 与 ChannelPipeline

    ChannelHandler 1. Channel 生命周期 Channel 的生命周期状态如下: 状态 描述 ChannelUnregistered Channel 已经被创建,但还未注册到 Eve ...

随机推荐

  1. [SWPUCTF 2021 新生赛]非常简单的逻辑题

    一道简单的代码逆向,根据提供的py代码写出逆向代码,没怎么做过这种题开始没什么思路,原来直接暴力就好 直接写出暴力代码

  2. 微前端、single-spa初探

    微前端 微前端,前端这次词就不用多做解释了,这个概念的重点在于这个"微"字, 从字面意义上看,微是小的意思,小是相对于大的一个用于比较的形容词,所以通常是在项目庞大的情况下,才会考 ...

  3. 文心一言 VS 讯飞星火 VS chatgpt (139)-- 算法导论11.4 3题

    三.用go语言,考虑一个采用均匀散列的开放寻址散列表.当装载因子为 3/4 和 7/8 时,试分别给出一次不成功查找和一次成功查找的探查期望数上界. 文心一言: 在开放寻址法中,当散列表的装载因子超过 ...

  4. 手撕Vue-Router-初始化路由信息

    前言 经过上一节课的学习,我们已经完成了提取我们想要的路由信息数据格式,提取完毕了之后,接下来我们该干什么,接下来需要做的步骤就是监听路由的变化,保存当前的路由. 那么就会遇到几个问题,就是怎么监听, ...

  5. React 应用构建(环境)

    可以少去理解一些不必要的概念,而多去思考为什么会有这样的东西,它解决了什么问题,或者它的运行机制是什么? 一. 环境搭建 工作编辑器:Visual Studio Code. Javascript 解析 ...

  6. iOS信号量造成线程优先级反转

    在并发队列使用信号量会可能会造成线程优先级反转 一.在iOS16 & XCode14上遇到 - 使用信号量造成线程优先级反转问题 提醒 经过查询资料,发现是在XCode14上增加了工具,比如 ...

  7. c++算法练习day01【2022年蓝桥杯省赛B组题目】每天做一点、、、

    这个练习目前来说就比较宽松,打算在寒假(基本也就是这一个月每天刷几道题吧) 题目一: 小明决定从下周一开始努力刷题准备蓝桥杯竞赛.他计划周一至周五每天做 a 道题目,周六和周日每天做 b 道题目.请你 ...

  8. 【LOJ NOI Round#2 Day1 T1】单枪匹马(矩阵乘法)

    题目传送门 操作二要求的东西是一个循环迭代的东西,手推相邻两项找下规律,发现相邻两项的分子分母间含有线性关系,考虑用矩阵乘法求解.对于 \([1,n]\)的询问,从后往前倒推, \(x_{n-1}=a ...

  9. 地图选择器datav怎么使用?

    DataV 是一款基于阿里云的数据可视化产品,它提供了丰富的组件和功能,其中包括地图选择器.下面是一个详细的介绍: 1. 了解 DataV: - DataV 是一款强大的数据可视化工具,能够帮助用户将 ...

  10. nginx 反向代理teleport

    nginx 反向代理teleport 普通配置(以Nginx服务与TP服务在同一台主机上为例) # ...其他内容... server { listen 80; server_name www.you ...