zoukankan      html  css  js  c++  java
  • 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 超时没有写数据断开连接。  

      

      

      

      

      

  • 相关阅读:
    docker构建镜像
    SpringBoot 配置的加载
    Gradle实战(02)--Gradle Build的创建
    Gradle实战(01)--介绍与安装
    统计最常用10个命令的脚本
    jackson序列化与反序列化的应用实践
    go http请求流程分析
    java线程的3种实现方式及线程池
    git多账号使用
    java多版本管理
  • 原文地址:https://www.cnblogs.com/brandonli/p/10612357.html
Copyright © 2011-2022 走看看