从零开始学习Netty - 学习笔记 -Netty入门-ChannelFuture

5.2.2.Channel

Channel 的基本概念

在 Netty 中,Channel 是表示网络传输的开放连接的抽象。它提供了对不同种类网络传输的统一视图,比如 TCP 和 UDP。

Channel 的生命周期

Channel 的生命周期包括创建、激活、连接、读取、写入和关闭等阶段。Netty 中的 Channel 具有状态,根据不同的事件触发状态转换。

Channel channel = ...; // 获取 Channel 实例

// 检查 Channel 是否打开
if (channel.isOpen()) {
    // 进行数据读取操作
    channel.read();
}

// 关闭 Channel
channel.close();

Channel 的异步 I/O

Netty 中的 Channel 支持异步的 I/O 操作,这意味着可以在不阻塞线程的情况下进行网络通信。下面是一个简单的读取操作示例:

// 从 Channel 中读取数据
channel.read(new ChannelHandler() {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 处理读取到的数据
        ByteBuf buf = (ByteBuf) msg;
        System.out.println(buf.toString(Charset.defaultCharset()));
        buf.release(); // 释放资源
    }
});

ChannelHandler 和 ChannelPipeline

ChannelHandler 用于处理入站和出站的事件,而 ChannelPipeline 是一系列 ChannelHandler 的链,负责处理 Channel 传递的事件。

// 创建一个 ChannelInitializer 用于初始化 ChannelPipeline
ChannelInitializer<SocketChannel> initializer = new ChannelInitializer<SocketChannel>() {
    @Override
    protected void initChannel(SocketChannel ch) {
        ChannelPipeline pipeline = ch.pipeline();
        
        // 添加自定义的 ChannelHandler 到 ChannelPipeline 中
        pipeline.addLast("handler", new MyChannelHandler());
    }
};

// 在 ServerBootstrap 中应用 ChannelInitializer
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(eventLoopGroup)
         .channel(NioServerSocketChannel.class)
         .childHandler(initializer); 

channel的主要作用

  • close():主要用来关闭channel
  • **closeFuture():**用来处理channel的关闭
    • sync方法作用是同步等待channel的关闭
    • addListener方法是异步等待channel关闭
  • **pipeline():**方法添加处理器
  • **write():**方法是将数据写入
  • **writeAndFlush():**方法是将数据写入并刷出

例如刚刚的客户端代码

		// 1.创建启动器
		try {
			new Bootstrap()
					// 2.指定线程模型 一个用于接收客户端连接,另一个用于处理客户端读写
					.group(new NioEventLoopGroup())
					// 3.选择客户端的Channel的实现
					.channel(NioSocketChannel.class)
					// 4.添加处理器
					.handler(new ChannelInitializer<NioSocketChannel>() {
						// 5.初始化处理器
						@Override
						protected void initChannel(NioSocketChannel ch) throws Exception {
							// 6.添加具体的handler 客户端是需要一个编码器
							ch.pipeline().addLast(new StringEncoder());
						}
					})
					// 7.连接到服务器
					.connect(new InetSocketAddress("localhost", 8080))
					.sync() // 阻塞方法 知道连接建立
					.channel() // 代表客户端和服务端的连接
					// 8.向服务器发送数据
					.writeAndFlush("hello, world");
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
5.2.2.1.连接问题sync
			// 1.创建启动器
			try {
				ChannelFuture channelFuture = new Bootstrap()
						.group(new NioEventLoopGroup())
						.channel(NioSocketChannel.class)
						.handler(new ChannelInitializer<NioSocketChannel>() {
							@Override
							protected void initChannel(NioSocketChannel ch) throws Exception {
								ch.pipeline().addLast(new StringEncoder());
							}
						})
						// 7.连接到服务器
						// connect方法是异步的,返回一个ChannelFuture(异步调用 就是不关心结果,直接返回)
						// main线程发起了调用,真正执行了connect是另外一个线程 nio线程
						.connect(new InetSocketAddress("localhost", 8080));
				// 7.1.同步等待连接成功 如果不调用sync()方法,main线程会继续往下执行,不会等待connect()方法的执行结果
				channelFuture.sync();
				// 7.2.获取连接对象 如果没有调用sync()方法,这里的channel此时还没有真正建立起连接
				Channel channel = channelFuture.channel(); // 连接对象
				logger.error("channel: {}", channel);
				// 8.向服务器发送数据
				channel.writeAndFlush("hello, world");
			} catch (Exception e) {
				throw new RuntimeException(e);
			}

image-20240226071218470

image-20240226071322706

5.2.2.2.处理结果

带有Future Promise 的类型,都是和异步方法配套使用的,用来正确处理结果的

  • 调用channelFuture.sync()处理同步结果,sync()主要是阻塞当前线程,直到nio线程连接建立完毕

  • 使用addListener(new ChannelFutureListener() )

    • 	// 使用addListener(回调对象)方法,可以在ChannelFuture执行完成后,再执行一些操作
        				channelFuture.addListener(new ChannelFutureListener() {
        					// 在NIO线程连接建立好后,会调用operationComplete方法
        					@Override
        					public void operationComplete(ChannelFuture channelFuture) throws Exception {
        						if (channelFuture.isSuccess()) {
        							// 7.2.获取连接对象 如果没有调用sync()方法,这里的channel就会是null
        							Channel channel = channelFuture.channel(); // 连接对象
        							logger.error("channel: {}", channel);
        							// 8.向服务器发送数据
        							channel.writeAndFlush("hello, world");
        						} else {
        							// 7.3.连接失败
        							Throwable cause = channelFuture.cause();
        							logger.error("connect failed: {}", cause);
        						}
        					}
        				});
      
5.2.2.3.处理关闭

小需求 : 客户端 不断接收用于输入的信息,然后发送给客户端,当用户端输入q 退出 关闭channel

/**
 *
 * @author 13723
 * @version 1.0
 * 2024/2/27 21:46
 */
public class CloseFutureClient {
	private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());


	public static void main(String[] args) throws InterruptedException {


		ChannelFuture channelFuture = new Bootstrap()
				.group(new NioEventLoopGroup())
				.channel(NioSocketChannel.class)
				.handler(new ChannelInitializer<NioSocketChannel>() {
					@Override
					protected void initChannel(NioSocketChannel ch) throws Exception {
						ch.pipeline().addLast(new StringEncoder());
					}
				})
				.connect(new InetSocketAddress("localhost", 8080));

		// 客户端 不断接收用于输入的信息,然后发送给客户端,当用户端输入q 退出
		// 建立建立
		Channel channel = channelFuture.sync().channel();
		logger.error("channel: {} ",channel);
		// 接收用户输入的需求
		new Thread(()->{
			Scanner scanner = new Scanner(System.in);
			while (true){
				String s = scanner.nextLine();
				if ("q".equals(s)){
					// 退出 关闭channel
                    // 1s 后才真正的关闭
					channel.close();
					// 退出循环
					logger.error("处理关闭之后的操作!");
					break;
				}
				// 向服务器 发送数据
				channel.writeAndFlush(s);
			}
		},"input").start();
	}
}

在这里插入图片描述

在这里插入图片描述

  • 解决

    • 使用CloseFuture.sync()

      		// 关闭Channel
      		// 获取closeFuture对象 1.同步受理关闭 2.异步处理关闭
      		ChannelFuture closeFuture = channel.closeFuture();
      		logger.error("wait close... ");
      		closeFuture.sync();
      		logger.error("处理关闭之后的操作!");
      

      在这里插入图片描述

    • 使用addListener(new ChannelFutureListener())

      		closeFuture.addListener(new ChannelFutureListener() {
      			@Override
      			public void operationComplete(ChannelFuture channelFuture) throws Exception {
      				logger.error("处理关闭之后的操作!");
      			}
      		});
      

      在这里插入图片描述

`

此时关闭,会会发现客户端并没有结束,因为线程虽然结束,但是NioEventLoopGroup 里面可能还有线程,这是时关闭,需要调

**shutdownGracefully()**方法

// 将NioEventLoopGroup提出来  
NioEventLoopGroup group = new NioEventLoopGroup();
ChannelFuture channelFuture = new Bootstrap()
        .group(group)
.........
    
// 然后在处理善后中调用 
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
    logger.error("处理关闭之后的操作!");
    // 需要保证整个全部关闭
    group.shutdownGracefully();
}

在这里插入图片描述

5.2.2.4.为什么使用异步

思考下面这样的场景,4个医生给人看病,每个病人花费20分钟,而且医生看病的过程中,是以病人为单位的,一个病人看完了,才能看下一个病人,假设病人源源不断来,可以计算一天4个医生工作8小时,处理病人总数 4 * 8 * 3 = 96

在这里插入图片描述

经研究 发现 看病可以分为 四个步骤 经拆分后每个步骤仅需要五分钟

在这里插入图片描述

因此 可以做如下优化,只有一开始, 医生 2 3 4 需要分别等待 5 10 15分钟开能开始执行工作,但是只要后续病人源源不断的来,他们就能满负荷工作,并且处理病人的能力提高 到了, 4 * 8 * 12 整个效率 是原先的 4 倍

(满负载情况下)第一个医生 只挂号,一个号五分钟,那么 一个小时 可以处理 12个,之前一个医生从头到尾只能看一个病人,那么一个小时只能看3个

在这里插入图片描述

  • 单线程没法异步提高效率,必须配合多线程,多核心cpu才能发挥异步的优势
  • 异步并没有缩短响应时间,反而有所增加(提高的是吞吐量,单位时间内能够处理请求的速度)
  • 合理任务的拆分,也是利用异步的关键

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/414516.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CGI程序与ShellShock漏洞

CGI是什么&#xff1f; CGI&#xff08;通用网关接口&#xff0c;Common Gateway Interface&#xff09;程序是一种用于在Web服务器上执行动态内容的技术。与服务器上普通的后端代码相比&#xff0c;CGI程序有几个区别&#xff1a; 执行环境&#xff1a; CGI程序在服务器上作为…

js中Symbol的理解与应用

文章目录 一、Symbol特性1.1 不支持语法new Symbol()1.2 唯一性1.3 不与其他值隐式转换1.4 不可枚举1.5 类型为symbol 二、Symbol常见方法2.1 Symbol.toStringTag2.2 Symbol.iterator2.3 Symbol.for() 三、Symbol应用 在JavaScript中&#xff0c;Symbol 是一种基本数据类型&…

el-table 多选表格存在分页,编辑再次操作勾选会丢失原来选中的数据

el-table表格多选时&#xff0c;只需要添加type"selection"&#xff0c; row-key及selection-change&#xff0c;如果存在分页时需要加上reserve-selection&#xff0c;这里就不写具体的实现方法了&#xff0c;可以查看我之前的文章&#xff0c;这篇文章主要说一下存…

智能指针(C++)

目录 一、智能指针是什么 二、为什么需要智能指针 三、智能指针的使用和原理 3.1、RALL 3.2 智能指针的原理 3.3、智能指针的分类 3.3.1、auto_ptr 3.3.2、unique_ptr 3.3.3、shared_ptr 3.2.4、weak_ptr 一、智能指针是什么 在c中&#xff0c;动态内存的管理式通过一…

VPX基于全国产飞腾FT-2000+/64核+复旦微FPGA的计算刀片

6U VPX计算板 产品简介 产品特点 飞腾计算平台&#xff0c;国产化率100% VPX-MPU6902是一款基于飞腾FT-2000/64核的计算刀片&#xff0c;主频2.2GHz&#xff0c;负责业务数据流的管控和调度。搭配自带独立显示芯片的飞腾X100芯片&#xff0c;可用于于各类终端及服务器类应用场…

Spring与SpringBoot入门

Spring入门 要使用Spring最起码需要引入两个依赖: <!-- Spring Core&#xff08;核心&#xff09; --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.3.20</version>…

小白水平理解面试经典题目LeetCode 655. Print Binary Tree【Tree】

655 打印二叉树 一、小白翻译 给定二叉树的 root &#xff0c;构造一个 0 索引的 m x n 字符串矩阵 res 来表示树的格式化布局。格式化布局矩阵应使用以下规则构建&#xff1a; 树的高度为 height &#xff0c;行数 m 应等于 height 1 。 列数 n 应等于​​xheight1​​ - …

爆火的1分钟声音克隆GPT-SoVITS项目 linux系统 ubuntu22.04安装2天踩坑教程

原项目地址&#xff1a;https://github.com/RVC-Boss/GPT-SoVITS 1分钟素材&#xff0c;最后出来的效果确实不错。 1. cuda环境安装 cuda环境准备 根据项目要求在cuda11.8和12.3都测试了通过。我这里是用cuda11.8 cuda11.8安装教程&#xff1a; ubuntu 22.04 cuda多版本和…

【软件测试】--功能测试4-html介绍

1.1 前端三大核心 html:超文本标记语言&#xff0c;由一套标记标签组成 标签&#xff1a; 单标签&#xff1a;<标签名 /> 双标签:<标签名></标签名> 属性&#xff1a;描述某一特征 示例:<a 属性名"属性值"> 1.2 html骨架标签 <!DOC…

蓝桥杯第十四届电子类单片机组决赛程序设计

目录 前言 单片机资源数据包_2023&#xff08;点击下载&#xff09; 一、第十四届比赛题目 1.比赛题目 2.题目解读 1&#xff09;任务要求 2&#xff09;注意事项 二、显示功能实现 1.关于高位为0时数码管熄灭功能的实现 2.关于显示小数位的处理 3.关于“校准值”的…

某查查首页瀑布流headers加密

目标网站&#xff1a; 某查查 对目标网站分析发现 红框内的参数和值都是加密的&#xff0c;是根据算法算出来的&#xff0c;故进行逆向分析。 由于没有固定参数名&#xff0c;只能通过搜索headers&#xff0c;在搜索的位置上打上断点&#xff0c;重新请求。 断点在此处断住&a…

【计算机】本科考研还是就业?

其实现在很多计算机专业的学生考研&#xff0c;也是无奈的选择 技术发展日新月异&#xff0c;而在本科阶段&#xff0c;大家学着落后的技术&#xff0c;出来找工作自然会碰壁。而且现在用人单位的门槛越来越高&#xff0c;学历默认研究生起步&#xff0c;面试一般都是三轮起步…

循序渐进丨MogDB / openGauss 如何实现自增主键

概述 自增主键是我们在设计数据库表结构时经常使用的主键生成策略&#xff0c;主键的生成可以完全依赖数据库&#xff0c;无需人为干预&#xff0c;在新增数据的时候&#xff0c;我们只需要将主键的值设置为default&#xff0c;数据库就会为我们自动生成一个主键值。 MySQL 主键…

基于沁恒微 ch643q 多通道采集 adc 驱动层实现

一、代码 #include "main.h"/********************************************************************** fn ADC_Function_Init** brief Initializes ADC collection.** return none*/ void ADC_Function_Init(void) {ADC_InitTypeDef ADC_InitStructure …

【Go 快速入门】协程 | 通道 | select 多路复用 | sync 包

文章目录 前言协程goroutine 调度使用 goroutine 通道无缓冲通道有缓冲通道单向通道 select 多路复用syncsync.WaitGroupsync.Mutexsync.RWMutexsync.Oncesync.Map 项目代码地址&#xff1a;05-GoroutineChannelSync 前言 Go 1.22 版本于不久前推出&#xff0c;更新的新特性可…

LoRa技术在智能气象监测中的应用与解决方案分享

LoRa技术在智能气象监测领域的应用具有广泛的前景&#xff0c;通过LoRa技术可以实现对气象数据的远程采集、传输和监测&#xff0c;为气象行业提供更加智能化和高效的解决方案。以下将探讨LoRa技术在智能气象监测中的应用与解决方案分享。 首先&#xff0c;LoRa技术可以用于连…

python|闲谈2048小游戏和数组的旋转及翻转和转置

目录 2048 生成数组 n阶方阵 方阵旋转 顺时针旋转 逆时针旋转 mxn矩阵 矩阵旋转 测试代码 测试结果 翻转和转置 2048 《2048》是一款比较流行​的数字游戏​&#xff0c;最早于2014年3月20日发行。原版2048由Gabriele Cirulli首先在GitHub上发布&#xff0c;后被移…

【C语言】数据存储篇,内存中的数据存储----C语言整型,浮点数的数据在内存中的存储以及大小端字节序【图文详解】

欢迎来CILMY23的博客喔&#xff0c;本篇为​【C语言】数据存储篇&#xff0c;内存中的数据存储----C语言整型&#xff0c;浮点数的数据在内存中的存储以及大小端字节序【图文详解】&#xff0c;感谢观看&#xff0c;支持的可以给个一键三连&#xff0c;点赞关注收藏。 前言 C语…

GIS之深度学习02:Anaconda2019版本安装(py38)

Anaconda是一个专注于数据科学和机器学习的开源发行版&#xff0c;内置了丰富的工具和库&#xff0c;包括Python解释器、NumPy、SciPy、Pandas、Scikit-learn、TensorFlow等&#xff0c;使用户能够轻松进行科学计算和数据分析。其强大的包管理器conda简化了软件包的安装和环境管…

Linux/Spectra

Enumeration nmap 第一次扫描发现系统对外开放了22&#xff0c;80和3306端口&#xff0c;端口详细信息如下 22端口运行着ssh&#xff0c;80端口还是http&#xff0c;不过不同的是打开了mysql的3306端口 TCP/80 进入首页&#xff0c;点击链接时&#xff0c;提示域名不能解析&…