ChannelHandlerContext——ChannelHandler和ChannelPipeline之二

目录

ChannelHandlerContext接口

使用ChannelHandlerContext

ChannelHandler和ChannelHandlerContext的高级用法

异常处理

处理入站异常

处理出站异常


本文继上文《ChannelHandler和ChannelPipeline之一》,接着讲ChannelHandlerContext接口。

ChannelHandlerContext接口

ChannelHandlerContext代表了ChannelHandler和ChannelPipeline之间的关联,每当有ChannelHandler添加到ChannelPipeline中时都会创建ChannelHandlerContext。ChannelHandlerContext的主要功能是管理它所关联的ChannelHandler和在同一个ChannelPipeline中的其他ChannelHandler之间的交互。

ChannelHandlerContext有很多的方法,其中一些方法也存在于Channel和ChannelPipeline本身上,但是有一点重要的不同。如果调用Channel或者ChannelPipeline上的这些方法,它们将沿着整个ChannelPipeline进行传播。而调用ChannelHandlerContext上的相同方法,则将从当前关联的ChannelHandler开始,并且只会传播给位于该ChannelPipeline中的下一个能够处理该事件的ChannelHandler。下表对ChannelHandlerContext的API进行了总结。

方法名称描述
alloc返回和这个实例相关联的Channel所配置的ByteBufAllocator
bind绑定到给定的SocketAddress,并返回ChannelFuture
channel返回绑定到这个实例的Channel
close关闭Channel,并返回ChannelFuture
connect连接给定的SocketAddress,并返回ChannelFuture
deregister从之前分配的EventExecutor注销,并返回ChannelFuture
disconnect从远程节点断开,并返回ChannelFuture
executor返回调度事件的EventExecutor
fireChannelActive触发对下一个ChannelOutboundHandler上的channelActive()方法(已连接)的调用
fireChannelInactive触发对下一个ChannelOutboundHandler上的channelInactive()方法(已关闭)的调用
fireChannelRead触发对下一个ChannelOutboundHandler上的channelRead()方法(已接收的消息)的调用
fireChannelReadComplete触发对下一个ChannelOutboundHandler上的channelReadComplete()方法的调用
fireChannelRegistered触发对下一个ChannelOutboundHandler上的channelRegistered()方法的调用
fireChannelUnregistered触发对下一个ChannelOutboundHandler上的channelUnregistered()方法的调用
fireChannelWritabilityChanged触发对下一个ChannelOutboundHandler上的channelWritabilityChanged()方法的调用
fireExceptionCaught触发对下一个ChannelOutboundHandler上的channelExceptionCaught(Throwable)方法的调用
fireUserEventTriggered触发对下一个ChannelOutboundHandler上的userEventTriggered(Object)方法的调用
handler返回绑定到这个实例的ChannelHandler
isRemoved如果所关联的ChannelHandler已经从所关联的ChannelPipeline中移除,则返回true
name返回这个实例的唯一名称
pipeline返回这个实例所关联的ChannelPipeline
read将数据从Channel读取到第一个入站缓冲区;如果读取成功则触发一个channelRead事件,并(在最后一个消息被读取完成后)通知ChannelInboundHandler的channelReadComplete(ChannelHandlerContext)方法
write通过这个实例写入消息并经过ChannelPipeline
writeAndFlush通过这个实例写入消息并冲刷并经过ChannelPipeline

当使用ChannelHandlerContext的API的时候,请牢记以下两点:

  • ChannelHandlerContext和ChannelHandler之间的关联(绑定)是永远不会变的,所以缓存对它的引用是安全的;
  • 相对于其他类的同名方法,ChannelHandlerContext的方法将产生更短的事件流,应该尽可能地利用这个性能获得最大的性能。

使用ChannelHandlerContext

接下来我们讨论ChannelHandlerContext的用法,以及存在于ChannelHandlerContext、Channel和ChannelPipeline上的方法的行为。下图展示了它们之间的关系:

以下代码通过ChannelHandlerContext获得Channel,调用Channel上的write方法将会导致写入事件从尾端到头部地流经ChannelPipeline。

ChannelHandlerContext ctx = ...
Channel channel = ctx.channel();
channel.write(Unpooled.copiedBuffer("Netty In Action", CharsetUtil.UTF_8));

以下代码类似,但是这一次是写入ChannelPipeline。我们再次看到,(到ChannelPipeline的)引用是通过ChannelHandlerContext获得的。

ChannelHandlerContext ctx = ...
ChannelPipeline pipeline = ctx.pipeline();
pipeline.write(Unpooled.copiedBuffer("Netty In Action", CharsetUtil.UTF_8));

虽然被调用的Channel或ChannelPipeline上的write()方法将一直传播事件通过整个ChannelPipeline,但是在ChannelHandler的级别上,事件从一个ChannelHandler到下一个ChannelHandler的移动是由ChannelHandlerContext上的调用完成的。

为什么会想要从ChannelPipeline中的某个特定点开始传播事件呢?

  • 为了减少事件传经对它不感兴趣的ChannelHandler所带来的开销。
  • 为了避免将事件传经那些可能会对它产生兴趣的ChannelHandler。

要想调用从某个特定的ChannelHandler开始的处理过程,必须获取到在(ChannelPipeline)该ChannelHandler之前的ChannelHandler所关联的ChannelHandlerContext。这个ChannelHandlerContext将调用和它相关联的ChannelHandler之后的ChannelHandler。

如下图所示,消息将从下一个ChannelHandler开始流经ChannelPipeline,绕过了前面所有的ChannelHandler。

以上描述的用例是常见的,对于调用特定的ChannelHandler实例上的操作尤其有用。

ChannelHandler和ChannelHandlerContext的高级用法

你可以通过调用ChannelHandlerContext的pipeline()方法来获得被封闭的ChannelPipeline的引用。这使得运行时得以操作ChannelPipeline的ChannelHandler,我们可以利用这一点实现一些复杂的设计。例如,你可以通过将ChannelHandler添加到ChannelPipeline中实现动态的协议切换。

另一种高级的用法是缓存到ChannelHandlerContext的引用以供稍后使用,这可能会发生在任何的ChannelHandler之外,甚至来自于不同的线程。

public class WriteHandler extends ChannelHandlerAdapter {private ChannelHandlerContext ctx;public void handlerAdded(ChannelHandlerContext ctx) {this.ctx = ctx;}public void send(String msg) {ctx.writeAndFlush(msg);}
}

因为一个ChannelHandler可以从属于多个ChannelPipeline,所以它也可以绑定到多个ChannelHandlerContext。对于这种用法旨在多个ChannelPipeline中共享同一个ChannelHandler,对应的ChannelHandler必须要使用@Sharable注解标注;否则,试图将它添加到多个ChannelPipeline时将会触发异常。显而易见,为了安全地被用于多个并发的Channel(即连接),这样的ChannelHandler必须是线程安全的。

public class UnsharableHandler extends ChannelInboundHandlerAdapter {private int count;public void channelRead(ChannelHandlerContext ctx, Object msg) {count++;System.out.println("channelRead(...) called the " + count + " time");ctx.fireChannelRead(msg);}
}

以上代码的时间将会导致问题,因为它拥有状态,即用于跟踪方法调用次数的实例变量count。将这个类的一个实例添加到一个ChannelPipeline将极有可能在它被多个并发的Channel访问时导致问题。(当然,这个简单的问题可以通过使channelRead()方法变位同步方法来修正。)

总之,只应该在你确认了你的ChannelHandler是线程安全后才使用@Sharable注解。

为何要共享同一个ChannelHandler

在多个ChannelPipeline中安装同一个ChannelHandler的一个常见原因是用于收集跨越多个Channel的统计信息。

异常处理

异常处理是任何真实应用程序的重要组成部分,它也可以通过多种方式来实现。因此,Netty提供了几种方式用于处理入站或者出站处理过程中所抛出的异常。

处理入站异常

如果在处理入站事件的过程中有异常被抛出,那么它将从它在ChannelInboundHandler里被触发的那一点开始流经ChannelPipeline。要想处理这种类型的入站异常,你需要在你的ChannelInboundHandler实现中重写下面的方法。

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception

以下代码展示了一个简单的示例,其关闭了Channel并打印了异常的栈跟踪信息。

public class InboundExceptionHandler extends ChannelInboundHandlerAdapter {public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}
}

因为异常将会继续按照入站方向流动(就像所有的入站事件一样),所以实现了前面所示逻辑的ChannelInboundHandler通常位于ChannelPipeline的最后。这确保了所有的入站异常总是会被处理,无论它们可能发生在ChannelPipeline中的什么位置。

你应该如何响应异常,可能很大程度上取决于你的应用程序。你可能想要关闭Channel(和连接),也可能会尝试进行恢复。如果你不实现任何处理入站异常的逻辑(或者没有消费该异常),那么Netty将会记录该异常没有被处理的事实。

  • ChannelHandler.exceptionCaught()的默认实现是简单地将当前异常转发给ChannelPipeline中的下一个ChannelHandler;
  • 如果异常到达了ChannelPipeline的尾端,它将会被记录为未被处理;
  • 要想定义自定义的处理逻辑,你需要重写exceptionCaught()方法。然后你需要决定是否需要将该异常传播出去。

处理出站异常

用于处理出站操作中的正常完成以及异常的选项,都基于以下的通知机制。

  • 每个出站操作都将返回一个ChannelFuture。注册到该ChannelFuture的ChannelFutureListener将在操作完成时被通知该操作是成功了还是出错了。
  • 几乎所有的ChannelOutboundHandler上的方法都会传入一个ChannelPromise的实例。作为ChannelFuture的子类,ChannelPromse也可以被分配用于异步通知的监听器。但是,ChannelPromise还具有提供立即通知的可写方法。
ChannelPromise seSuccess();
ChannelPromise setFailure(Throwable cause);

添加ChannelFutureListener只需要调用ChannelFuture实例上的addListener(ChannelFutureListener)方法,并且有两种不同的方式可以做到这一点。其中最常用的方式是调用出站操作(如write方法)所返回的ChannelFuture上的addListener()方法。

ChannelFuture future = channel.write(msg);
future.addListener(new ChannelFutureListener() {public void operationComplete(ChannelFuture f) {if (!f.isSuccess()) {f.cause().printStackTrace();f.channel().close();}}
});

第二方式是將ChannelFutureListener添加到即將作为参数传递给ChannelOutboundHandler的方法的ChannelPromise。

public class OutboundExceptionHandler extends ChannelOutboundHandlerAdapter {public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {promise.addListener(new ChannelFutureListener() {public void operationComplete(ChannelFuture f) {if (!f.isSuccess()) {f.cause().printStackTrace();f.channel().close();}}});}
}

ChannelPromise的可写方法

通过调用ChannelPromise上的setSuccess和setFailure方法,可以使一个操作的状态在ChannelHandler的方法返回给其调用者时便即刻被感知到。

为什么选用一种方式而不是另一种呢? 对于细致的异常处理,你可能会发现,在调用出站操作时添加ChannelFutureListener更加合适。而对于一般的异常处理,你可能会发现,第一种实现方式更加简单。

如果你的ChannelOutboundHandler本身抛出异常会发生什么呢?在这种情况下,Netty本身会通知任何已经注册到对应ChannelPromise的监听器。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

需求响应+配网重构!含高比例新能源和用户需求响应的配电网重构程序代码!

前言 配电网重构作为配电网优化运行的手段之一,通过改变配电网的拓扑结构,以达到降低网损、改善电压分布、提升系统的可靠性与经济性等目的。近年来,随着全球能源消耗快速增长以及环境的日趋恶化,清洁能源飞速发展,分…

超简单白话文机器学习 - 逐步回归Lasso,Ridge正则化(含算法讲解,公式全解,手写代码实现,调包实现)

1. 提高泛化能力 1.1 概念 正则化是一种在机器学习和统计建模中用于防止过拟合的技术。过拟合是指模型在训练数据上表现很好,但在未见过的测试数据或新数据上表现不佳。正则化通过在损失函数(如最小二乘误差)中添加一个惩罚项,限…

仓库管理WMS软件(Warehouse Management Software)百科解析

一、什么是仓库管理软件(WMS)? 仓库管理软件(WMS)全称Warehouse Management System,是一种专门用于仓库作业流程优化和库存控制的软件系统。它通过先进的自动识别与数据采集技术,实现对仓库货物…

Leecode热题100---114:二叉树展开为链表

题目: 给你二叉树的根结点 root ,请你将它展开为一个单链表: 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。 展开后的单链表应该与二叉树 先序遍历 顺序相同…

Spring Boot | Spring Boot 实现 “记住我“ 功能

目录: 一、SpringBoot 中 自定义 "用户授权管理" ( 总体内容介绍 )二、实现 "记住我" 功能 ( 通过 "HttpSecurity类" 的 rememberMe( )方法来实现 "记住我" 功能 ) :2.1 基于 "简单加密 Token" 的方式 ( 实现 "记住我&…

av_dump_format经验分析,FFmpeg获取媒体文件总时长(FLV获取总时长的误区)

播放器有个功能,当用户打开视频时,需要读取媒体文件的总时长等信息,不巧的时,获取FLV时总失败,下面来具体分析下FLV和MP4获取总时长的原因和区别: 播放器有个获取MediaInfo的接口,功能如下&am…

echarts全局设置饼图的颜色

🌷第一步 在js文件中写入你需要的颜色 这里的颜色也可以写渐变的 🌷下一步 在main.is中引用全局挂载 🌷最后一步 在初始化的时候加一个macarons即可 🌷第一步 在js文件中写入你需要的颜色 这里的颜色也可以写渐变的 (functi…

探索k8s集群中kubectl的陈述式资源管理

一、k8s集群资源管理方式分类 1.1陈述式资源管理方式:增删查比较方便,但是改非常不方便 使用一条kubectl命令和参数选项来实现资源对象管理操作 即通过命令的方式来实 1.2声明式资源管理方式:yaml文件管理 使用yaml配置文件或者json配置文…

【研发日记】嵌入式处理器技能解锁(一)——多任务异步执行调度的三种方法

文章目录 前言 Timer中断调度 Event中断调度 StateFlow调度 分析和应用 总结 参考资料 前言 近期在一些嵌入式系统开发项目中,在使用嵌入式处理器时,遇到了挺多费时费力的事情。所以利用晚上和周末时间,在这些方面深入研究了一下&…

Joomla 3.7.0 (CVE-2017-8917) SQL注入漏洞环境

1 漏洞概述 Joomla是一个基于PHP的内容管理系统(CMS),广泛应用于各类网站。2017年,Joomla 3.7.0版本被发现存在SQL注入漏洞(CVE-2017-8917),攻击者可以利用该漏洞对数据库进行未授权查询或操作…

The Sandbox 和 Bitkub 联手增强东南亚元宇宙中心

作为去中心化游戏虚拟世界和区块链平台的先驱,The Sandbox 正与泰国领先的区块链网络 Bitkub Blockchain Technology Co., Ltd. 展开创新合作。双方合作的目的是将Bitkub元宇宙的影响力扩展到The Sandbox,建立一个元宇宙中心,向用户承诺从 Bi…

C# GetManifestResourceStream 获取项目资源为null解决方案(亲测)

GetManifestResourceStream 获取项目资源为null 使用Stream s assembly.GetManifestResourceStream(Assembly.GetExecutingAssembly().GetName().Name resourceName) 获取资源文件,返回流为null,如图所示: 解决方案 设置资源文件的 属性&…

<MySQL> 【数据类型】

目录 一、数据类型一览 二、整型 (一)INT (二)BIT 三、浮点数 (一)FLOAT (二)DECIMAL (三)如何选择 四、字符 (一)CHAR &a…

超简单白话文机器学习 - 支持向量机SVM(含算法讲解,公式全解,手写代码实现,调包实现)

1. 支持向量机SVM 支持向量机,因其英文名为support vector machine,故一般简称SVM,通俗来讲,它是一种二类分类模型,其基本模型定义为特征空间上的间隔最大的线性分类器,其学习策略便是间隔最大化&#xff…

优雅草便民工具v2.0.4更新

优雅草便民工具v2.0.4更新 优雅草便民工具v2.0.4更新 2024年5月20日v2.0.4更新优雅草便民工具youyacao-tools-增加淘宝联想词功能和ai绘画功能apk下载 https://fenfacun.youyacao.com/tools204.apk 介绍 优雅草便民工具是一款由成都市一颗优雅草科技有限公司打造的便民查询公益…

JVM(8):虚拟机性能分析和故障解决工具之jinfo工具

1 jinfo(Configuration Info forJava)作用 实时地查看和调整虚拟机各项参数 2 命令格式 jinfo [options] 参数解释: 第一个参数:options no option:输出全部的参数和系统属性-flag name:输出对应名称的参数-flag [|-]name&am…

6. Spring面试题汇总

Java全栈面试题汇总目录-CSDN博客 1. 什么是Spring框架? Spring是一种轻量级开发框架,旨在提高开发人员的开发效率以及系统的可维护性。 我们一般说Spring框架指的都是Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助…

如何轻松访问 Android 手机和平板电脑上的内部存储

概括 在数字设备领域,我们的智能手机充当虚拟金库,在其范围内存储个人数据、珍贵记忆和重要信息的宝库。因此,我们将指导您如何访问 Android 上的内部存储,确保您可以安全、轻松地检查内部文件系统并管理文件。同时,您…

无人机监测系统:天空之眼,精准掌握地球脉动

在当今信息化快速发展的时代,无人机技术以其独特的优势,正在成为资源调查、环境监测和规划支持的重要工具。无人机监测系统通过搭载多种传感器和设备,能够快速、高效地获取地表信息,为决策提供科学依据。 项目背景 随着全球环境…

Llama 3 CPU推理优化指南

备受期待的 Meta 第三代 Llama 现已发布,我想确保你知道如何以最佳方式部署这种最先进的 (SoTA) LLM。在本教程中,我们将重点介绍如何执行仅权重量化 (WOQ) 来压缩 8B 参数模型并改善推理延迟,但首先,让我们讨论一下 Meta Llama 3…