netty源码解解析(4.0)-16 ChannelHandler概览

  本章开始分析ChannelHandler实现代码。ChannelHandler是netty为开发者提供的实现定制业务的主要接口,开发者在使用netty时,最主要的工作就是实现自己的ChannelHandler。ChannelHandler在设计上需要和ChannelPipeline配合共同实现pipeline的事件传递能力,这要求ChannelHandler需要实现一些固定的基本功能。由于这个原因,如果让用户自己完整地实现,会显得比较麻烦。为此netty实现类一系列的类来帮助开发者以简单的方式实现自己的ChannelHandler,而且只需要把注意力聚焦在自己业务和定制的部分上。在netty中,ChannelHandler不仅仅是几个简单的接口定义,而是一系列的实现,这些实现针对不同的问题,其中包括:

  单一功能性的Handler实现:

    IP过滤,实现IP黑白名单功能。

    写日志。

    SSL实现。

    超时处理。

编码和序列化格式的支持:

    base64

gzip,snappy压缩算法

    protoBuf

    String

    自定义数据包格式。

  常见应用层协议:

    http/https(后面的版本还支持http2)

    haprox

ChannelHandler体系结构

  上图上的所有接口的实现都位于io.netty.channel包中,其中,ChannelHandler是最顶层抽象的接口,ChannelInboundHandler是处理inbound事件的接口,ChannelOutboundHandler是处理outbound事件的接口。ChannelHandlerApapter是与I/O无关的抽象实现。ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter是最简单的实现,他们什么都没做,仅仅使用ChannelHandlerContext把事件传递到pipleline中下一个handler,如果你不清楚在正在处理事件之后如何传递事件,请参考这两个类中的代码。比如,ChannelInboundAdapter中是这样传递read事件的:

1 @Override
2 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
3         ctx.fireChannelRead(msg);
4 }

CombinedChannelDuplexhandler兼具ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter的能力。如果你想让一个Handler同时处理inbound和outbound事件,请继承这个类。

ChannelHandler的共享和独占模式

  netty为ChannelHandler设计了两种模式,如果用ChannelHandler.Sharable修饰你的Handler, 那么这个Handler实例就是可共享的,否则它就是被某个Channel独占的。

@ChannelHandler.Sharable
public class MyHandler extends ChannelInboundHandler{
}

  这个MyHandler的实例是可以被不同的Channel共享的。在初始化Channel的时候可以这加入到pipeline中:

1 public class HandlerInitializer extends ChannelInitializer<SocketChanne>{
2     private static MyHandler handler   = new MyHandler();
3
4     @Override
5     protected void initChannel(SocketChannel ch) throws Exception{
6         ch.pipeline().addLast(handler);
7     }
8 }

  如果去掉@ChannelHandler.Sharable,它就是独占的,默认的都是独占。它可以这样用:

public class HandlerInitializer extends ChannelInitializer<SocketChanne>{

     @Override
     protected void initChannel(SocketChannel ch) throws Exception{
         ch.pipeline().addLast(new MyHandler());
     }
}

  ChannelHandlerAdapter的isSharable方法负责处理@ChannelHandler.Sharable:

public boolean isSharable() {
        /**
         * Cache the result of {@link Sharable} annotation detection to workaround a condition. We use a
         * {@link ThreadLocal} and {@link WeakHashMap} to eliminate the volatile write/reads. Using different
         * {@link WeakHashMap} instances per {@link Thread} is good enough for us and the number of
         * {@link Thread}s are quite limited anyway.
         *
         * See <a href="https://github.com/netty/netty/issues/2289">#2289</a>.
         */
        Class<?> clazz = getClass();
        Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();
        Boolean sharable = cache.get(clazz);
        if (sharable == null) {
            sharable = clazz.isAnnotationPresent(Sharable.class);
            cache.put(clazz, sharable);
        }
        return sharable;
    }

  现在已经清楚了@ChannelHandler.Sharable影响和用法。那么问题来了,netty为什么要设计这样一个东西,它解决了什么问题呢?弄清这个问题之前,我们先来弄清楚ChannelHandler的作用。每个ChannelHandler实例都在Channel的pipeline上占据一个节点,每个节点带代表了事件处理流水线上的一道工序,具体每道工序是什么,由开发者自己定义和实现。总体来说,netty把工序分成两种类型: 有状态的和无状态的。有状态的就是独占的,无状态的是可以共享的。有状态和无状态这个两个概念太过于抽象。举个例子,http协议是一个基于TCP的应用层协议,实现这个协议的时候有一道工序是从tcp流中读到一个完整http数据报,tcp不保证每次read都刚好是一个完整的http数据报,很有可能是不完整的,这时候要把不完整的数据保存在缓冲区中, 下次read到数据之后追加到缓冲区,直到得到一个完整的http数据报。处理这道工序的Handler是有状态的,必须使用独占的Handler。

  一个Handler的实例是否可以共享,由它处理的工序决定的。即使netty没有设计这种独占/共享模式,开发者也应该可以把握好Handler的用法。

常用的功能性Handler

  io.netty.handler包中是netty提供的常用的功能性的handler:

  io.netty.handler.ipfilter.RuleBasedIpFilter  使用IpFilterRule过过滤远程地址。

  io.netty.handler.ipfilter.UniqueIpFilter  一个地址只允许和服务器建立一个连接。

  io.netty.handler.logging.LoggingHandler 异步读/写日志。

  io.netty.handler.stream.ChunkedWriteHandler 异步读/写文件。

  io.netty.handler.timeout.IdleStateHandler  超时触发空闲状态事件。

  io.netty.handler.timeout.ReadTimeoutHandler 超时没有收到数据断开连接。

  io.netty.handler.timeout.WriteTimeoutHandler 超时没有写数据断开连接。  

  

  

  

  

  

原文地址:https://www.cnblogs.com/brandonli/p/10612357.html

时间: 2024-08-28 00:45:59

netty源码解解析(4.0)-16 ChannelHandler概览的相关文章

netty源码解解析(4.0)-14 Channel NIO实现:读取数据

 本章分析Nio Channel的数据读取功能的实现. Channel读取数据需要Channel和ChannelHandler配合使用,netty设计数据读取功能包括三个要素:Channel, EventLoop和ChannelHandler.Channel有个read方法,这个方法不会直接读取数据,它的作用是通知持有当前channel的eventLoop可以从这个这个channel读取数据了,这个方法被调用之后eventLoop会在channel有数据可读的时候从channel读出数据然后把数

netty源码解解析(4.0)-21 ByteBuf的设计原理

????io.netty.buffer包中是netty ByteBuf的实现.ByteBuf是一个二进制缓冲区的抽象接口,它的功能有: 可以随机访问.顺序访问. 支持基本数据类型(byte, short, int, long, float, double)的序列化和反序列化. 不限制底层原始的数据类型,可以是byte[], NIO Buffer, String, IO Stream, 直接内(C语言中可以表示为指向一块内置起始地址的指针void*, 内存的长度), 等等. 为什么需要ByteBu

netty源码解解析(4.0)-6 线程模型-IO线程EventLoopGroup和NIO实现(一)

接口定义 io.netty.channel.EventLoopGroup extends EventExecutorGroup 方法 说明 ChannelFuture register(Channel channel) 把一个channel注册到一个EventLoop ChannelFuture register(Channel channel, ChannelPromise promise); 同上 io.netty.channel.EventLoop extends OrderedEvent

netty源码解解析(4.0)-23 ByteBuf内存管理:分配和释放

ByteBuf内存分配和释放由具体实现负责,抽象类型只定义的内存分配和释放的时机. 内存分配分两个阶段: 第一阶段,初始化时分配内存.第二阶段: 内存不够用时分配新的内存.ByteBuf抽象层没有定义第一阶段的行为,但定义了第二阶段的方法: public abstract ByteBuf capacity(int newCapacity) 这个方法负责分配一个长度为newCapacity的新内存. 内存释放的抽象实现在AbstractReferenceCountedByteBuf中实现,这个类实

netty源码解解析(4.0)-24 ByteBuf基于内存池的内存管理

PooledByteBuf的初始化过程分为两个步骤:创建实例:初始化内存.这两个步骤的代码如下: 507383170 protected PooledByteBuf(Recycler.Handle recyclerHandle, int maxCapacity) { super(maxCapacity); this.recyclerHandle = recyclerHandle; } void init(PoolChunk<T> chunk, long handle, int offset,

netty源码解解析(4.0)-1 核心架构

netty是java开源社区的一个优秀的网络框架.使用netty,我们可以迅速地开发出稳定,高性能,安全的,扩展性良好的服务器应用程序.netty封装简化了在服务器开发领域的一些有挑战性的问题:jdk nio的使用:多线程并发:扩展性.它还提供了多种应用层协议的支持:http/https/websock, protobuf, 自定义协议, 简化了服务器协议的开发. netty是一个基于事件驱动的框架,它把事件分成两种类型:输入事件(inbound)和输出事件(outbound), 整个框架都是围

netty源码解解析(4.0)-8 ChannelPipeline的设计

io.netty.channel.ChannelPipeline 设计原理 上图中,为了更直观地展示事件处理顺序, 故意有规律地放置两种handler的顺序,实际上ChannelInboundHandler和ChanneOutboundHandler的顺序可以是任意,取决于用户调用add方法把handler方在哪里. ChannelPipeline的特性: 1. 它是一个双向链表 2. 每个节点持有一个ChannelHandler实例,这个实例可以是ChannelInboundHandler类型

JAVA框架底层源码剖析系列Spring,Mybatis,Springboot,Netty源码深度解析

<Spring源码深度解析>从核心实现和企业应用两个方面,由浅入深.由易到难地对Spring源码展开了系统的讲解,包括Spring的设计理念和整体架构.容器的基本实现.默认标签的解析.自定义标签的解析.bean的加载.容器的功能扩展.AOP.数据库连接JDBC.整合MyBatis.事务.SpringMVC.远程服务.Spring消息服务等内容. <Spring源码深度解析>不仅介绍了使用Spring框架开发项目必须掌握的核心概念,还指导读者如何使用Spring框架编写企业级应用,并

Linux 源码安装MariaDB 10.0.16

cmake软件 tar -zxvf cmake-2.8.8.tar.gz cd cmake-2.8.8 ./bootstrap make && make install 依赖包 yum install ncurses-devel -y yum install pcre pcre-devel -y yum install gcc* -y 创建用户 groupadd mysql useradd -g mysql mysql echo "oracle" |passwd --s