【Netty】Netty ChannelHandler(四)

news/2024/7/7 23:23:02

文章目录

  • 前言
  • 一、ChannelHandler
  • 二、ChannelInboundHandler
  • 三、ChannelOutboundHandler
  • 四、ChannelDuplexHandler
  • 总结

前言

前两篇文章我们已经对Netty进行了简单的了解和架构设计原理的剖析。
相关文章链接如下:

  • Netty 概述(一)
  • Netty 架构设计(二)
  • Netty Channel 概述(三)

先简略了解一下ChannelPipeline和ChannelHandler的概念。

想象一个流水线车间。当组件从流水线头部进入,穿越流水线,流水线上的工人按顺序对组件进行加工,到达流水线尾部时商品组装完成。可以将ChannelPipeline当做流水线,ChannelHandler当做流水线工人。源头的组件当做event,如read,write等等。

本篇文章我们先来讲讲ChannelHandler的相关知识,下面进入正文吧。

一、ChannelHandler

在这里插入图片描述

  • ChannelHandler并不处理事件,而由其子类代为处理:ChannelInboundHandler拦截和处理入站事件,ChannelOutboundHandler拦截和处理出站事件。
  • ChannelHandler和ChannelHandlerContext通过组合或继承的方式关联到一起成对使用。事件通过ChannelHandlerContext主动调用如fireXXX()和write(msg)等方法,将事件传播到下一个处理器。

注意:入站事件在ChannelPipeline双向链表中由头到尾正向传播,出站事件则方向相反。

ChannaleHandler 作为最顶层的接口,并不处理入站和出站事件,所以接口中只包含最基本的方法:

 // Handler本身被添加到ChannelPipeline时调用
 void handlerAdded(ChannelHandlerContext var1) throws Exception;
  // Handler本身被从ChannelPipeline中删除时调用
 void handlerRemoved(ChannelHandlerContext var1) throws Exception;// 发生异常时调用
 @Deprecated
 void exceptionCaught(ChannelHandlerContext var1, Throwable var2) throws Exception;

Sharable注解:

当客户端连接到服务器时,Netty新建一个ChannelPipeline处理其中的事件,而一个ChannelPipeline中含有若干ChannelHandler。如果每个客户端连接都新建一个ChannelHandler实例,当有大量客户端时,服务器将保存大量的ChannelHandler实例。为此,Netty提供了Sharable注解,如果一个ChannelHandler状态无关,那么可将其标注为Sharable,如此,服务器只需保存一个实例就能处理所有客户端的事件。

 @Inherited
 @Documented
 @Target({ElementType.TYPE})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Sharable {
 }

作为ChannelHandler的默认实现,ChannelHandlerAdapter有个重要的方法isSharable(),代码如下:

public boolean isSharable() {
     Class<?> clazz = this.getClass();
     // 每个线程一个缓存
     Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();
     Boolean sharable = (Boolean)cache.get(clazz);
     if (sharable == null) {
         // Handler是否存在Sharable注解
         sharable = clazz.isAnnotationPresent(Sharable.class);
         cache.put(clazz, sharable);
     }return sharable;
 }

这里引入了优化的线程局部变量InternalThreadLocalMap,即每个线程都有一份ChannelHandler是否Sharable的缓存。这样可以减少线程间的竞争,提升性能。

二、ChannelInboundHandler

ChannelInboundHandler处理入站数据以及各种状态变化,当Channel状态发生改变会调用ChannelInboundHandler中的一些生命周期方法。这些方法与Channel的生命密切相关。

入站数据,就是进入socket的数据。下面展示一些该接口的生命周期API:

类型描述
channelRegistered当 Channel 已经注册到它的 EventLoop 并且能够处理 I/O 时被调用
channelUnregistered当 Channel 从它的 EventLoop 注销并且无法处理任何 I/O 时被调用
channelActive当 Channel 处于活动状态时被调用;Channel 已经连接/绑定并且已经就绪
channelInactive当 Channel 离开活动状态并且不再连接它的远程节点时被调用
channelReadComplete当Channel上的一个读操作完成时被调用
channelRead当从 Channel 读取数据时被调用
ChannelWritabilityChanged当Channel的可写状态发生改变时被调用。用户可以确保写操作不会完成得太快(以避免发生 OutOfMemoryError)或者可以在 Channel 变为再次可写时恢复写入。可以通过调用Channel的isWritable()方法来检测Channel 的可写性。与可写性相关的阈值可以通过Channel.config().setWriteHighWaterMark()和 Channel.config().setWriteLowWaterMark()方法来设置
userEventTriggered当 ChannelnboundHandler.fireUserEventTriggered()方法被调用时被调用,因为一个 POJO 被传经了 ChannelPipeline

ChannelInboundHandlerAdapter作为ChannelInboundHandler的实现,默认将入站事件自动传播到下一个入站处理器。其中的代码高度一致,如下:

 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
     ctx.fireChannelRead(msg);
 }

三、ChannelOutboundHandler

出站操作和数据将由 ChannelOutboundHandler 处理。它的方法将被 Channel、 ChannelPipeline 以及 ChannelHandlerContext 调用。 ChannelOutboundHandler 的一个强大的功能是可以按需推迟操作或者事件,这使得可以通过一些复杂的方法来处理请求。

例如, 如果到远程节点的写入被暂停了, 那么你可以推迟冲刷操作并在稍后继续。

同理,ChannelOutboundHandlerAdapter作为ChannelOutboundHandler的事件,默认将出站事件传播到下一个出站处理器:

@Override
 public void read(ChannelHandlerContext ctx) throws Exception {
     ctx.read();
 }

四、ChannelDuplexHandler

ChannelDuplexHandler则同时实现了ChannelInboundHandler和ChannelOutboundHandler接口。如果一个所需的ChannelHandler既要处理入站事件又要处理出站事件,推荐继承此类。

总结

以上就是关于ChannelHandler的分析,相信你对ChannelHandler也有一定的了解,下期我们再来分析ChannelPipeline的源码。


http://lihuaxi.xjx100.cn/news/1165479.html

相关文章

【CMU 15-445】学习笔记与Lab汇总

又来开新坑啦&#xff01;本门课程为CMU的数据库原理课程15-445&#xff0c;我学习的版本为Fall2022&#xff0c;教授为Andy&#xff08;上课有DJ&#xff09;。准备跟着课程的Schedule来做&#xff0c;就是先看配套教材的相应章节&#xff08;Database-System-Concept-7th&…

「API 接口获取方法」

在创建一个应用程序的过程中&#xff0c;获取数据是非常关键的一步。而通过API接口获取数据通常是最好的方式之一。那么&#xff0c;如何通过关键字获取API接口呢&#xff1f;以下是一些步骤&#xff1a; 1.确定你需要获取的数据类型 首先&#xff0c;你需要确定你需要获取的…

HTML+CSS实训——Day03——仿网易云音乐的主页界面

仓库链接:https://github.com/MengFanjun020906/HTML_SX 一些今天需要用到的知识点 弹性盒子 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedg…

Extjs 学习ing

启用Ext.Loader (function(){Ext.Loader.setConfig({enabled:true});ext.onReady(function(){})})();创建数据模型对象 model // Model定义为字段、任意方法和与模型相关的属性的集合( 如 validations 验证器等&#xff09; //define 创建 Ext.define(student,{extend:Ext…

VMware是什么?VMware虚拟机最新安装教程

VMware Workstation是一款虚拟机软件&#xff0c;允许用户将Linux、Windows等多个操作系统作为虚拟机在单台PC上运行; 用户可以在虚拟机上重现服务器、桌面和平板电脑环境&#xff0c;无需重新启动即可跨不同操作系统同时运行应用。 通过对个人笔记本(PC)硬件资源的虚拟&#…

怎么把录音转文字?推荐你这三款工具

随着科技不断发展&#xff0c;录音转文字的技术也逐渐被广泛应用于各种场景中。其中最常见的一种就是会议记录。在日常工作中&#xff0c;会议是企业和组织中必不可少的一个环节&#xff0c;但在会议过程中的录音和记录往往需要花费大量的时间和精力。这个时候&#xff0c;我们…

Three.js教程:点、线、网格模型介绍

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 其他系列工具&#xff1a; NSDT简石数字孪生 点、线、网格模型介绍 经过前面几章学习相信你对点模型Points、线模型Line、网格模型Mesh已经有了大致了解&#xff0c;本节课就对点、线、网格模型模型进行简单总结。 点模型…

day4 - 使用图像绘制动态时钟

本期的主要内容是利用OpenCV中包含的绘图函数&#xff0c;例如绘制线段、绘制矩形、绘制圆形等来绘制一个动态时钟的表盘。 完成本期内容&#xff0c;你可以&#xff1a; 掌握OpenCV常见的绘图函数 学会使用绘图函数绘制简单的图像 若要运行案例代码&#xff0c;你需要有&a…