首页 > 其他分享 >Netty高手必知必会的ChannelHandlerContext技巧

Netty高手必知必会的ChannelHandlerContext技巧

时间:2023-05-15 21:31:32浏览次数:38  
标签:Netty 调用 ChannelHandler 必知 ChannelHandlerContext ctx ChannelPipeline Channel

1 概述

ChannelHandlerContext 代表 ChannelHandler 和 ChannelPipeline 之间的关联,每当有 ChannelHandler 添加到 ChannelPipeline,都会创建 ChannelHandlerContext。

1.1 主要功能

管理它所关联的 ChannelHandler 和在同一个 ChannelPipeline 中的其他 ChannelHandler 之间的交互。

ChannelHandlerContext一些方法也存在于 Channel 和 ChannelPipeline 本身,若:

  • 调用 Channel 或 ChannelPipeline 的这些方法,它们将沿整个 ChannelPipeline 传播
  • 调用 ChannelHandlerContext 的相同方法,则将从当前所关联的 ChannelHandler 开始,并且只会传播给位于该ChannelPipeline 中的下一个能够处理该事件的 ChannelHandler

1.2 ChannelHandlerContext API

表 6-10 ChannelHandlerContext 的 API:

Netty高手必知必会的ChannelHandlerContext技巧_线程安全

Netty高手必知必会的ChannelHandlerContext技巧_线程安全_02

注意到最后一个 read 方法的注释①:通过配合 ChannelConfig.setAutoRead(boolean autoRead)方法,可以实现反应式系统的特性之一背压(back-pressure)。

FAQ

  • ChannelHandlerContext 和 ChannelHandler 之间的关联(绑定)永远不会改变,所以对它的引用安全
  • 相较其他类的同名方法,ChannelHandler Context的方法将产生更短的事件流,尽可能利用该特性获得最大性能

2 使用 ChannelHandlerContext

2.1 家族关系

图 6-4 展示家族关系:

Netty高手必知必会的ChannelHandlerContext技巧_方法调用_03

2.2 实例

通过 ChannelHandlerContext 获取到 Channel 的引用。调用Channel#write()将导致写入事件从tail到head地流经 ChannelPipeline:

代码清单 6-6:从 ChannelHandlerContext 访问 Channel
   
 ChannelHandlerContext ctx = ..;
 // 获取到与 ChannelHandlerContext相关联的 Channel 的引用
 Channel channel = ctx.channel();
 // 通过 Channel 写入缓冲区
 channel.write(Unpooled.copiedBuffer("Netty in Action", CharsetUtil.UTF_8));

类似例子,但这次是写入 ChannelPipeline。ChannelPipline引用是通过 ChannelHandlerContext 获取:

代码清单 6-7 通过 ChannelHandlerContext 访问 ChannelPipeline
   
 ChannelHandlerContext ctx = ..;
 // 获取到与 ChannelHandlerContext 相关联的 ChannelPipeline 的引用
 ChannelPipeline pipeline = ctx.pipeline();
 // 通过 ChannelPipeline写入缓冲区
 pipeline.write(Unpooled.copiedBuffer("Netty in Action", CharsetUtil.UTF_8));

如图 6-5所见,代码清单 6-6 和代码清单 6-7 中的事件流一样。

Netty高手必知必会的ChannelHandlerContext技巧_方法调用_04

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

2.3 从 ChannelPipeline 中的某特定点开始传播事件

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

要想调用从某个特定的 ChannelHandler 开始的处理过程,须获取到在(ChannelPipeline)该 ChannelHandler 之前的 ChannelHandler 所关联的 ChannelHandlerContext。这个 ChannelHandlerContext 将调用和它所关联的 ChannelHandler 之后的ChannelHandler。代码清单 6-8 和图 6-6 说明这种用法。

// 获取到 ChannelHandlerContext 的引用
 ChannelHandlerContext ctx = ..;
 // write()方法将把缓冲区数据发送到下一个 ChannelHandler
 ctx.write(Unpooled.copiedBuffer("JavaEdge", CharsetUtil.UTF_8));

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

Netty高手必知必会的ChannelHandlerContext技巧_线程安全_05

我们刚才所描述的用例是常见的,对于调用特定的 ChannelHandler 实例上的操作尤其有用。

3 ChannelHandler 和 ChannelHandlerContext 高级用法

3.1 获得private的 ChannelPipeline 的引用

代码清单 6-6,可调用 ChannelHandlerContext#pipeline()获得private修饰的 ChannelPipeline 的引用。使得运行时得以操作 ChannelPipeline 的 ChannelHandler,利用这点实现一些复杂设计。如通过将 ChannelHandler 添加到 ChannelPipeline 实现动态的协议切换。

3.2 缓存到 ChannelHandlerContext 的引用

供稍后使用。可能发生在任何的 ChannelHandler 方法之外,甚至来自不同线程。代码清单 6-9 展示了用这种模式来触发事件。

public class WriteHandler extends ChannelHandlerAdapter {
   
    private ChannelHandlerContext ctx;
   
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
       // 存储到 ChannelHandlerContext的引用以供稍后使用
       this.ctx = ctx; 
    }
   
    // 使用之前存储的到 ChannelHandlerContext 的引用来发送消息
    public void send(String msg) { 
       ctx.writeAndFlush(msg);
    } 
 }

3.3 多个 ChannelPipeline 共享同一 ChannelHandler

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

正例

代码清单 6-10 展示这种模式的一个正确实现:

代码清单 6-10 可共享的 ChannelHandler
   
 // 使用注解@Sharable标注
 @Sharable 
 public class SharableHandler extends ChannelInboundHandlerAdapter {
   
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
      
      System.out.println("Channel read message: " + msg);
      // 记录方法调用,并转发给下一个 ChannelHandler
      ctx.fireChannelRead(msg); 
    } 
 }

前面的 ChannelHandler 实现符合所有的将其加入到多个 ChannelPipeline 的需求,即使用注解@Sharable,且也不持有任何状态。

反例

相反,代码清单 6-11 中的实现有问题。

代码清单 6-11 @Sharable 的错误用法
 
 // 使用注解@Sharable
 @Sharable 
 public class UnsharableHandler extends ChannelInboundHandlerAdapter { 
    private int count;
   
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
      // 将 count 字段的值加 1
      count++;
      // 记录方法调用
      System.out.println("channelRead(...) called the " 
      + count + " time");
      // 并转发给下一个ChannelHandler
      ctx.fireChannelRead(msg);
    } 
 }
拥有状态

主要问题在于,对其所持有的状态的修改并非线程安全,如可通过 AtomicInteger 规避。

即用于跟踪方法调用次数的实例变量count。将这个类的一个实例添加到ChannelPipeline将极有可能在它被多个并发的Channel访问时导致问题。(这简单问题可通过使channelRead()变为同步方法来修正)。总之,只应该在确定了你的 ChannelHandler 是线程安全的时才使用 @Sharable

为何要共享同一个ChannelHandler ?

常见原因:用于收集跨多个 Channel 的统计信息。

4 总结

本文就是 ChannelHandlerContext 和它与其他的框架组件之间的关系的讨论。

标签:Netty,调用,ChannelHandler,必知,ChannelHandlerContext,ctx,ChannelPipeline,Channel
From: https://blog.51cto.com/JavaEdge/6281011

相关文章

  • 5种你未必知道的JavaScript和CSS交互的方法
    随着浏览器不断的升级改进,CSS和JavaScript之间的界限越来越模糊。本来它们是负责着完全不同的功能,但最终,它们都属于网页前端技术,它们需要相互密切的合作。我们的网页中都有.js文件和.css文件,但这并不意味着CSS和js是独立不能交互的。下面要讲的这五种JavaScript和CSS共同合作的方......
  • 博学谷学习记录 自我总结 用心分享 | Netty框架使用
    Netty框架的作用1.Netty是一款用于快速开发高性能的网络应用程序的Java框架。它封装了网络编程的复杂性,定义了一种架构模型和一套丰富的设计模式。通过Netty框架,可以编写更高效的、可复用的、可维护的代码。有了Netty,你可以实现自己的HTTP服务器,FTP服务器,UDP服务器,RPC服务器,WebSo......
  • Netty实战(06)-ChannelPipeline 接口
    1概述把ChannelPipeline看成拦截流经Channel的入、出站事件的ChannelHandler的实例链,就易看出这些ChannelHandler之间的交互如何组成一个应用程序数据和事件处理逻辑的核心。每个新建的Channel都会被分配一个新的ChannelPipeline。这项关联是永久性的;Channel既不能附加另外......
  • Netty 爱好者必看!一文详解 ChannelHandler 家族,助你快速掌握 Netty 开发技巧!
    1Channel接口的生命周期Channel定义了一组和ChannelInboundHandlerAPI密切相关的简单但功能强大的状态模型1.1Channel的状态状态描述ChannelUnregisteredChannel已经被创建,但还未注册到EventLoopChannelRegisteredChannel已经被注册到了EventLoopChannelActiveChanne......
  • 必知必会的JavaScript前端面试题篇(二),不看后悔!
    必知必会的JavaScript前端面试题篇(二),不看后悔!1.JavaScript有哪些数据类型以及它们的区别?js一共有八种数据类型,分别是:Undefined,Null,Boolean,Number,String,Object,Symbol,BigInt其中Symbol,BigInt是ES6中新增的数据类型:Symbol:代表唯一的数据类型,主要为了解......
  • netty运行测试类时报错:Unrecognized option: --illegal-access=deny
    netty(4.1.42.Final )运行netty-buffer模块测试类时报错:Unrecognizedoption:--illegal-access=deny Unrecognizedoption:--illegal-access=denyError:CouldnotcreatetheJavaVirtualMachine.Error:Afatalexceptionhasoccurred.Programwillexit.解题思路:1、......
  • Netty之TCP粘包和拆包
    一、概述TCP是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发给接收端的包,更有效的发给对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样做虽然提高了效率,但......
  • Netty
    1.定义Netty是一个基于NIO的客户、服务器端的编程框架,使用Netty可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。(官方解释)1.Nio比Bio,Aio的优势......
  • sql必知必会笔记
    sql关键字作为保留字使用,不能用于表或者列的名称sql语句不区分大小写sql语句中空格会被忽略select列名之间用逗号分隔少用*号检索,会降低性能distinct关键字作用于所有的列限制结果语法top5关键字sqlserver和access中使用fetchfirst5rowsonlyDB2......
  • java netty socket实例:报文长度+报文内容,springboot
    前言说实话,javanetty方面的资料不算多,尤其是自定义报文格式的,少之又少自己写了个简单的收发:报文长度+报文内容发送的话,没有写自动组装格式,自己看需求吧,需要的话,自己完善服务端启动可以直接用类文件启动,也可以通过springboot。我这里写的是用springboot启动的,可以自己按照需求自......