Netty 框架学习 —— 初识 Netty
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的更多相关文章
- Netty 框架学习 —— 基于 Netty 的 HTTP/HTTPS 应用程序
通过 SSL/TLS 保护应用程序 SSL 和 TLS 安全协议层叠在其他协议之上,用以实现数据安全.为了支持 SSL/TLS,Java 提供了 javax.net.ssl 包,它的 SSLConte ...
- Netty 框架学习 —— 传输
概述 流经网络的数据总是具有相同的类型:字节,这些字节如何传输主要取决于我们所说的网络传输.用户并不关心传输的细节,只在乎字节是否被可靠地发送和接收 如果使用 Java 网络编程,你会发现,某些时候当 ...
- Netty 框架学习 —— 引导
概述 前面我们学习了 ChannelPipeline.ChannelHandler 和 EventLoop 之后,接下来的问题是:如何将它们组织起来,成为一个可实际运行的应用程序呢?答案是使用引导(B ...
- Netty 框架学习 —— 编解码器框架
编解码器 每个网络应用程序都必须定义如何解析在两个节点之间来回传输的原始字节,以及如何将其和目标应用程序的数据格式做相互转换.这种转换逻辑由编解码器处理,编解码器由编码器和解码器组成,它们每种都可以将 ...
- Netty 框架学习 —— 第一个 Netty 应用
概述 在本文,我们将编写一个基于 Netty 实现的客户端和服务端应用程序,相信通过学习该示例,一定能更全面的理解 Netty API 该图展示的是多个客户端同时连接到一台服务器.客户端建立一个连接后 ...
- Netty 框架学习 —— EventLoop 和线程模型
EventLoop 接口 Netty 是基于 Java NIO 的,因此 Channel 也有其生命周期,处理一个连接在其生命周期内发生的事件是所有网络框架的基本功能.通常来说,我们使用一个线程来处理 ...
- Netty 框架学习 —— 预置的 ChannelHandler 和编解码器
Netty 为许多提供了许多预置的编解码器和处理器,几乎可以开箱即用,减少了在烦琐事务上话费的时间和精力 空闲的连接和超时 检测空闲连接以及超时对于释放资源来说至关重要,Netty 特地为它提供了几个 ...
- Netty 框架学习 —— Netty 组件与设计
Channel.EventLoop 和 ChannelFuture 这一节将对 Channel.EventLoop 和 ChannelFuture 类进行讨论,它们组合在一起,可以被认为是 Netty ...
- Netty 框架学习 —— ByteBuf
概述 网络数据的基本单位总是字节,Java NIO 提供了 ByteBuffer 作为它的字节容器,但这个类的使用过于复杂.Netty 的 ByteBuf 具有卓越的功能性和灵活性,可以作为 Byte ...
- Netty 框架学习 —— ChannelHandler 与 ChannelPipeline
ChannelHandler 1. Channel 生命周期 Channel 的生命周期状态如下: 状态 描述 ChannelUnregistered Channel 已经被创建,但还未注册到 Eve ...
随机推荐
- 【日常收支账本】【Day02】通过PyCharm集成QtDesigner和PyUIC快速创建界面
一.集成QtDesigner和PyUIC PyCharm集成QtDesigner和PyUIC教程 二.在QtDesigner中画出窗体 1. 主界面 编辑账本: 新增.修改或删除记录 可视化账本:通过 ...
- STM8 STM32 GPIO 细节配置问题
在MCU的GPIO配置中我们经常需要预置某一 IO 上电后为某一固定电平, 如果恰好我们需要上电后的某IO为高电平, 那么在配置GPIO的流程上面需要特别注意. 配置如下: (以下问题仅在STM8 / ...
- Halcon、HDevelop快速入门
HDevelop基础一 HDevelop概述 HDevelop是一款机器视觉的集成开发环境.下面将对HDevelop的界面内容做一下简单的介绍. 界面介绍 打开HDevelop,将看到以下画面. ...
- 暴力+分治+贪心+DP:最大子序列和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 示例: 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 ...
- C#不用正则校验密码(附赠正则表达式)
群聊 群友A问:有没有方法可以判断字符串里面带不带标点符号啊? 群友B问:需求是什么? 群友A说:想要密码校验,网上大部分都是正则. 群友A说:密码规则是包含大小写和数字.特殊符号,还有Length& ...
- LabVIEW基于机器视觉的实验室设备管理系统(4)
目录 行动计划 后面板连线 初始化 返回 注册 账号限制 查重账号或者姓名 确认密码 注册 效果演示 我们上一期制作完了给账户修改密码,那么我们这一期就来完成账户注册这一功能.老规矩哦,先来计划 ...
- JAVAweek6
本周学习 Java语言基础:运算符[有所区别] 算术运算符 +(3+2) - * / %(取余,模运算) +(连接符)(3+''2'') ++ -- class VarDemo { //算术运算符 p ...
- Go语言数组与切片学习总结
一.数组 数组的定义:相同类型的数据集合 go语言中数组的索引从0开始 没有赋值的数值型数组,默认值为0 数组一旦被创建,它的大小就是不可改变的 (1)声明数组与打印 var 变量名 [大小]变量类型 ...
- 数据库系列:MySQL不同操作分别用什么锁?
数据库系列:MySQL慢查询分析和性能优化 数据库系列:MySQL索引优化总结(综合版) 数据库系列:高并发下的数据字段变更 数据库系列:覆盖索引和规避回表 数据库系列:数据库高可用及无损扩容 数据库 ...
- [ABC274E] Booster
Problem Statement In a two-dimensional plane, there are $N$ towns and $M$ chests. Town $i$ is at the ...