Netty4详解三:Netty架构设计

2023-11-05

     读完这一章,我们基本上可以了解到Netty所有重要的组件,对Netty有一个全面的认识,这对下一步深入学习Netty是十分重要的,而学完这一章,我们其实已经可以用Netty解决一些常规的问题了。

一、先纵览一下Netty,看看Netty都有哪些组件?


     为了更好的理解和进一步深入Netty,我们先总体认识一下Netty用到的组件及它们在整个Netty架构中是怎么协调工作的。Netty应用中必不可少的组件:
  • Bootstrap or ServerBootstrap
  • EventLoop
  • EventLoopGroup
  • ChannelPipeline
  • Channel
  • Future or ChannelFuture
  • ChannelInitializer
  • ChannelHandler
     Bootstrap,一个Netty应用通常由一个Bootstrap开始,它主要作用是配置整个Netty程序,串联起各个组件。
     Handler,为了支持各种协议和处理数据的方式,便诞生了Handler组件。Handler主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。
     ChannelInboundHandler,一个最常用的Handler。这个Handler的作用就是处理接收到数据时的事件,也就是说,我们的业务逻辑一般就是写在这个Handler里面的,ChannelInboundHandler就是用来处理我们的核心业务逻辑。
     ChannelInitializer,当一个链接建立时,我们需要知道怎么来接收或者发送数据,当然,我们有各种各样的Handler实现来处理它,那么ChannelInitializer便是用来配置这些Handler,它会提供一个ChannelPipeline,并把Handler加入到ChannelPipeline。
     ChannelPipeline,一个Netty应用基于ChannelPipeline机制,这种机制需要依赖于EventLoop和EventLoopGroup,因为它们三个都和事件或者事件处理相关。
     EventLoops的目的是为Channel处理IO操作,一个EventLoop可以为多个Channel服务。
     EventLoopGroup会包含多个EventLoop。
     Channel代表了一个Socket链接,或者其它和IO操作相关的组件,它和EventLoop一起用来参与IO处理。
     Future,在Netty中所有的IO操作都是异步的,因此,你不能立刻得知消息是否被正确处理,但是我们可以过一会等它执行完成或者直接注册一个监听,具体的实现就是通过Future和ChannelFutures,他们可以注册一个监听,当操作执行成功或失败时监听会自动触发。总之,所有的操作都会返回一个ChannelFuture。
二、Netty是如何处理连接请求和业务逻辑的呢?-- Channels、Events 和 IO
     Netty是一个非阻塞的、事件驱动的、网络编程框架。当然,我们很容易理解Netty会用线程来处理IO事件,对于熟悉多线程编程的人来说,你或许会想到如何同步你的代码,但是Netty不需要我们考虑这些,具体是这样:
      一个Channel会对应一个EventLoop,而一个EventLoop会对应着一个线程,也就是说,仅有一个线程在负责一个Channel的IO操作。
     关于这些名词之间的关系,可以见下图:
     
     
     如图所示:当一个连接到达,Netty会注册一个channel,然后EventLoopGroup会分配一个EventLoop绑定到这个channel,在这个channel的整个生命周期过程中,都会由绑定的这个EventLoop来为它服务,而这个EventLoop就是一个线程。
     说到这里,那么EventLoops和EventLoopGroups关系是如何的呢?我们前面说过一个EventLoopGroup包含多个Eventloop,但是我们看一下下面这幅图,这幅图是一个继承树,从这幅图中我们可以看出,EventLoop其实继承自EventloopGroup,也就是说,在某些情况下,我们可以把一个EventLoopGroup当做一个EventLoop来用。
三、我们来看看如何配置一个Netty应用?-- BootsStrapping
     我们利用BootsStrapping来配置netty 应用,它有两种类型,一种用于Client端:BootsStrap,另一种用于Server端:ServerBootstrap,要想区别如何使用它们,你仅需要记住一个用在Client端,一个用在Server端。下面我们来详细介绍一下这两种类型的区别:
     1.第一个最明显的区别是,ServerBootstrap用于Server端,通过调用bind()方法来绑定到一个端口监听连接;Bootstrap用于Client端,需要调用connect()方法来连接服务器端,但我们也可以通过调用bind()方法返回的ChannelFuture中获取Channel去connect服务器端。
     2.客户端的Bootstrap一般用一个EventLoopGroup,而服务器端的ServerBootstrap会用到两个(这两个也可以是同一个实例)。为何服务器端要用到两个EventLoopGroup呢?这么设计有明显的好处,如果一个ServerBootstrap有两个EventLoopGroup,那么就可以把第一个EventLoopGroup用来专门负责绑定到端口监听连接事件,而把第二个EventLoopGroup用来处理每个接收到的连接,下面我们用一幅图来展现一下这种模式:
        
     PS: 如果仅由一个EventLoopGroup处理所有请求和连接的话,在并发量很大的情况下,这个EventLoopGroup有可能会忙于处理已经接收到的连接而不能及时处理新的连接请求,用两个的话,会有专门的线程来处理连接请求,不会导致请求超时的情况,大大提高了并发处理能力。
      我们知道一个Channel需要由一个EventLoop来绑定,而且两者一旦绑定就不会再改变。一般情况下一个EventLoopGroup中的EventLoop数量会少于Channel数量,那么就很有可能出现一个多个Channel公用一个EventLoop的情况,这就意味着如果一个Channel中的EventLoop很忙的话,会影响到这个Eventloop对其它Channel的处理,这也就是为什么我们不能阻塞EventLoop的原因。
     当然,我们的Server也可以只用一个EventLoopGroup,由一个实例来处理连接请求和IO事件,请看下面这幅图:
     
 
四、我们看看Netty是如何处理数据的?-- Netty核心ChannelHandler
     下面我们来看一下netty中是怎样处理数据的,回想一下我们前面讲到的Handler,对了,就是它。说到Handler我们就不得不提ChannelPipeline,ChannelPipeline负责安排Handler的顺序及其执行,下面我们就来详细介绍一下他们:
 ChannelPipeline and handlers
     我们的应用程序中用到的最多的应该就是ChannelHandler,我们可以这么想象,数据在一个ChannelPipeline中流动,而ChannelHandler便是其中的一个个的小阀门,这些数据都会经过每一个ChannelHandler并且被它处理。这里有一个公共接口ChannelHandler:
     
     从上图中我们可以看到,ChannelHandler有两个子类ChannelInboundHandler和ChannelOutboundHandler,这两个类对应了两个数据流向,如果数据是从外部流入我们的应用程序,我们就看做是inbound,相反便是outbound。其实ChannelHandler和Servlet有些类似,一个ChannelHandler处理完接收到的数据会传给下一个Handler,或者什么不处理,直接传递给下一个。下面我们看一下ChannelPipeline是如何安排ChannelHandler的:
     
     从上图中我们可以看到,一个ChannelPipeline可以把两种Handler(ChannelInboundHandler和ChannelOutboundHandler)混合在一起,当一个数据流进入ChannelPipeline时,它会从ChannelPipeline头部开始传给第一个ChannelInboundHandler,当第一个处理完后再传给下一个,一直传递到管道的尾部。与之相对应的是,当数据被写出时,它会从管道的尾部开始,先经过管道尾部的“最后”一个ChannelOutboundHandler,当它处理完成后会传递给前一个ChannelOutboundHandler。
数据在各个Handler之间传递,这需要调用方法中传递的ChanneHandlerContext来操作, 在netty的API中提供了两个基类分ChannelOutboundHandlerAdapter和ChannelOutboundHandlerAdapter,他们仅仅实现了调用ChanneHandlerContext来把消息传递给下一个Handler,因为我们只关心处理数据,因此我们的程序中可以继承这两个基类来帮助我们做这些,而我们仅需实现处理数据的部分即可。
     我们知道InboundHandler和OutboundHandler在ChannelPipeline中是混合在一起的,那么它们如何区分彼此呢?其实很容易,因为它们各自实现的是不同的接口,对于inbound event,Netty会自动跳过OutboundHandler,相反若是outbound event,ChannelInboundHandler会被忽略掉。
     当一个ChannelHandler被加入到ChannelPipeline中时,它便会获得一个ChannelHandlerContext的引用,而ChannelHandlerContext可以用来读写Netty中的数据流。因此,现在可以有两种方式来发送数据,一种是把数据直接写入Channel,一种是把数据写入ChannelHandlerContext,它们的区别是写入Channel的话,数据流会从Channel的头开始传递,而如果写入ChannelHandlerContext的话,数据流会流入管道中的下一个Handler。   
五、我们最关心的部分,如何处理我们的业务逻辑? -- Encoders, Decoders and Domain Logic
     Netty中会有很多Handler,具体是哪种Handler还要看它们继承的是InboundAdapter还是OutboundAdapter。当然,Netty中还提供了一些列的Adapter来帮助我们简化开发,我们知道在Channelpipeline中每一个Handler都负责把Event传递给下一个Handler,如果有了这些辅助Adapter,这些额外的工作都可自动完成,我们只需覆盖实现我们真正关心的部分即可。此外,还有一些Adapter会提供一些额外的功能,比如编码和解码。那么下面我们就来看一下其中的三种常用的ChannelHandler:
Encoders和Decoders
     因为我们在网络传输时只能传输字节流,因此,才发送数据之前,我们必须把我们的message型转换为bytes,与之对应,我们在接收数据后,必须把接收到的bytes再转换成message。我们把bytes to message这个过程称作Decode(解码成我们可以理解的),把message to bytes这个过程成为Encode。
     Netty中提供了很多现成的编码/解码器,我们一般从他们的名字中便可知道他们的用途,如ByteToMessageDecoder、MessageToByteEncoder,如专门用来处理Google Protobuf协议的ProtobufEncoder、 ProtobufDecoder。
     我们前面说过,具体是哪种Handler就要看它们继承的是InboundAdapter还是OutboundAdapter,对于Decoders,很容易便可以知道它是继承自ChannelInboundHandlerAdapter或 ChannelInboundHandler,因为解码的意思是把ChannelPipeline传入的bytes解码成我们可以理解的message(即Java Object),而ChannelInboundHandler正是处理Inbound Event,而Inbound Event中传入的正是字节流。Decoder会覆盖其中的“ChannelRead()”方法,在这个方法中来调用具体的decode方法解码传递过来的字节流,然后通过调用ChannelHandlerContext.fireChannelRead(decodedMessage)方法把编码好的Message传递给下一个Handler。与之类似,Encoder就不必多少了。
Domain Logic
     其实我们最最关心的事情就是如何处理接收到的解码后的数据,我们真正的业务逻辑便是处理接收到的数据。Netty提供了一个最常用的基类SimpleChannelInboundHandler<T>,其中T就是这个Handler处理的数据的类型(上一个Handler已经替我们解码好了),消息到达这个Handler时,Netty会自动调用这个Handler中的channelRead0(ChannelHandlerContext,T)方法,T是传递过来的数据对象,在这个方法中我们便可以任意写我们的业务逻辑了。
Netty从某方面来说就是一套NIO框架,在Java NIO基础上做了封装,所以要想学好Netty我建议先理解好Java NIO,建议大家阅读一下我的另两篇文章:
转载请说明出处,原文链接: http://blog.csdn.net/suifeng3051/article/details/28861883 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Netty4详解三:Netty架构设计 的相关文章

随机推荐

  • 设置git bash打印log的颜色

    在git安装目录下的 gitconfig文件中编辑 color diff auto status auto branch auto interactive true ui true alias hist log color graph pr
  • 人工智能正在重塑生产方式、优化产业结构、提升生产效率、赋能千行百业

    近日 清华大学科技情报大数据挖掘与服务系统平台AMiner发布 人工智能发展报告2011 2020 阐述过去十年人工智能取得的重要成果 探讨人工智能未来发展前景与蓝图 含此在内 多项人工智能研究报告均引发业界广泛关注 专家表示 作为引领新一
  • 如何方便的去除返回Date时间戳后面的0(或者做其他设置)

    这是get请求后 返回个前端的时间戳 可以看到后面有三个的0 如何去除这0呢 但我们在java里面返回的类又是date 有两个方法 一 重新定义返回视图对象 将createTime 和 updateTime定义为long 然后在赋值的时候更
  • DataGridView控件用法(二):为每行记录最后加“编辑”-“删除”按钮列

    1 在DataGridView控件用法 一 中已经显示出列表数据 这时我们需要对每行数据记录进行编辑 需要添加 编辑 删除 查看 这样的超链接 代码如下 view source print 1 为每行数据增加编辑列 2 设定列不能自动作成
  • Ansible自动化运维详细教程及playbook详解

    Ansible 一种集成 IT 系统的配置管理 应用部署 执行特定任务的开源平台 是 AnsibleWorks 公司名下的项目 该公司由 Cobbler 及 Func 的作者于 2012 年创建成立 Ansible 基于 Python 语言
  • Kafka3.0.0版本——消费者(消费者组原理)

    目录 一 消费者组原理 1 1 消费者组概述 1 2 消费者组图解示例 1 3 消费者组注意事项 一 消费者组原理 1 1 消费者组概述 Consumer Group CG 消费者组 由多个consumer组成 形成一个消费者组的条件 是所
  • vue3+vite配置全局scss

    首先下载scss sass loader sass npm install save dev sass loader npm install save dev node sass npm install save dev sass 打开vi
  • 西门子博图指令(定时器操作四)

    定时器功能指令介绍四 综述 加载脉冲定时器持续时间 介绍 程序 程序演示 加载接通延时定时器持续时间 介绍 程序 程序演示 加载关断延时定时器持续时间 介绍 程序 程序演示 加载时间累加器持续时间 介绍 程序 程序演示 源程序 综述 主要介
  • R语言 删除空行

    data lt data complete cases data 5 6 删除第五六列有空值的行 data lt na omit data 删除有空值的行 转载于 https www cnblogs com shilo930 p 11057
  • Nginx配置

    Nginx配置 一 nginx常用配置命令 二 nginx配置文件 一 第一部分 全局块 二 第二部分 events块 三 第三部分 http块 1 http全局块 2 server块 3 location块 三 nginx配置反向代理 一
  • Qt的Graphics-View框架和OpenGL结合详解

    Qt的Graphics View框架和OpenGL结合详解 演示程序下载地址 这里 程序源代码下载地址 这里 这是一篇纯技术文 介绍了这一个月来我抽时间研究的成果 Qt中有一个非常炫的例子 Boxes 它展示了Qt能够让其Graphics
  • 微信小程序如何用setData修改data中的数据

    wx request url url data classid classid header content type application json success function res console log res data 打
  • java输入输出方式

    oj public class Main public static void main String args 主代码 Scanner读入 import java util Scanner public class Main public
  • MySQL——流程控制函数

    在 MySQL 中 流程控制函数是指可以控制存储过程 stored procedure 或函数 function 中执行流程的语句 以下是几个常用的流程控制函数 1 IF函数 实现IF ELSE 的效果 如果expr1为true 则返回ex
  • arduino控制42步进电机——先动起来

    前言 好久之前就想分享出来 现在有点晚了 不过还是想简单的分享一下 在学校里一次做创新项目的机会 让我认识了步进电机 当时老师让我们用PLC发出脉冲的方式让步进电机动起来 可是我刚拿到步进电机时以为会像小马达一样 给正负直流电源就会跑 但是
  • c语言试卷自动批改系统.c

    一 题目要求 c语言试卷自动批改系统 1 answer txt为题目的答案文件 2 学生的答题信息用文件保存 即学号 txt 里面的内容为学生的答案 3 根据学号 txt与answer txt来判断学生答案是否正确来获取学生分数 每题五分
  • 火爆!用GPT接入midjounery绘图

    先看效果图吧 画了一只狗 gpt next web这个项目接入mj绘图 需要登录进入midjounery api网站后 复制一下api token key 3 网上找下GPT Next这个项目 建议电脑 手机有时候不显示图片 按照下方图示
  • 软件质量模型的六大特性和27个子特性

    软件质量模型的六大特性和27个子特性如下 ISO IEC9123软件质量特性 图示 一 功能性 1 适合性 解释软件产品是否提供了相应的功能 2 准确性 软件提供的功能是否正确 用户需要的 即解释软件对不对 3 互操作性 产品与产品之间交互
  • SQL优化(二):根据工具来SQL优化

    前一章节 讲的是explain的使用以及他结果的解析 实际上工具远远不止explain一个 还有慢查询 SHOW PROFILE这些 而且光读工具的说明书也不行 还要实际的根据工具返回的结果找到原因 根据实际情况是物理宿主机配置太低了要升级
  • Netty4详解三:Netty架构设计

    读完这一章 我们基本上可以了解到Netty所有重要的组件 对Netty有一个全面的认识 这对下一步深入学习Netty是十分重要的 而学完这一章 我们其实已经可以用Netty解决一些常规的问题了 一 先纵览一下Netty 看看Netty都有哪