读完这一章,我们基本上能够了解到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是一个非堵塞的、事件驱动的、网络编程框架。当然,我们非常easy理解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()方法来绑定到一个port监听连接;Bootstrap用于Client端,须要调用connect()方法来连接server端,但我们也能够通过调用bind()方法返回的ChannelFuture中获取Channel去connectserver端。
     2.client的Bootstrap一般用一个EventLoopGroup,而server端的ServerBootstrap会用到两个(这两个也能够是同一个实例)。为何server端要用到两个EventLoopGroup呢?这么设计有明显的优点,假设一个ServerBootstrap有两个EventLoopGroup,那么就能够把第一个EventLoopGroup用来专门负责绑定到port监听连接事件。而把第二个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的:

     

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3VpZmVuZzMwNTE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">


     从上图中我们能够看到,一个ChannelPipeline能够把两种Handler(ChannelInboundHandler和ChannelOutboundHandler)混合在一起。当一个数据流进入ChannelPipeline时,它会从ChannelPipeline头部開始传给第一个ChannelInboundHandler,当第一个处理完后再传给下一个。一直传递到管道的尾部。与之相相应的是。当数据被写出时。它会从管道的尾部開始,先经过管道尾部的“最后”一个ChannelOutboundHandler,当它处理完毕后会传递给前一个ChannelOutboundHandler。

数据在各个Handler之间传递。这须要调用方法中传递的ChanneHandlerContext来操作, 在netty的API中提供了两个基类分ChannelOutboundHandlerAdapter和ChannelOutboundHandlerAdapter,他们只实现了调用ChanneHandlerContext来把消息传递给下一个Handler,由于我们只关心处理数据。因此我们的程序中能够继承这两个基类来帮助我们做这些。而我们仅需实现处理数据的部分就可以。

     我们知道InboundHandler和OutboundHandler在ChannelPipeline中是混合在一起的。那么它们怎样区分彼此呢?事实上非常easy。由于它们各自实现的是不同的接口。对于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,非常easy便能够知道它是继承自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



Netty4具体解释三:Netty架构设计的更多相关文章

  1. Netty4详解三:Netty架构设计(转)

    http://blog.csdn.net/suifeng3051/article/details/28861883?utm_source=tuicool&utm_medium=referral ...

  2. Netty 源码分析系列(二)Netty 架构设计

    前言 上一篇文章,我们对 Netty做了一个基本的概述,知道什么是Netty以及Netty的简单应用. Netty 源码分析系列(一)Netty 概述 本篇文章我们就来说说Netty的架构设计,解密高 ...

  3. 一文看懂 Netty 架构设计

    本文重点分析 Netty 的逻辑架构及关键的架构质量属性,希望有助于大家从 Netty 的架构设计中汲取营养,设计出高性能.高可靠性和可扩展的程序. Netty 的三层架构设计 Netty 采用了典型 ...

  4. Android APP架构设计——MVP的使用示例

    0. 前言 为了更好地进行移动端架构设计,我们最常用的就是MVC.MVP和MVVM,作为三个最耳熟能详的三大架构,应用可谓非常广泛.对于这三种架构设计以及优缺点已经在Android APP架构设计-- ...

  5. MVC实用架构设计(三)——EF-Code First(5):二级缓存

    前言 今天我们来谈谈EF的缓存问题. 缓存对于一个系统来说至关重要,但是是EF到版本6了仍然没有见到有支持查询结果缓存机制的迹象.EF4开始会把查询语句编译成存储过程缓存在Sql Server中,据说 ...

  6. MVC实用架构设计(三)——EF-Code First(4):数据查询

    前言 首先对大家表示抱歉,这个系列已经将近一个月没有更新了,相信大家等本篇更新都等得快失望了.实在没办法,由于本人水平有限,写篇博客基本上要大半天的时间,最近实在是抽不出这么长段的空闲时间来写.另外也 ...

  7. MVC实用架构设计(三)——EF-Code First(3):使用T4模板生成相似代码

    前言 经过前面EF的<第一篇>与<第二篇>,我们的数据层功能已经较为完善了,但有不少代码相似度较高,比如负责实体映射的 EntityConfiguration,负责仓储操作的I ...

  8. petshop4.0 具体解释之中的一个(系统架构设计)

    前言:PetShop是一个范例,微软用它来展示.Net企业系统开发的能力.业界有很多.Net与J2EE之争,很多数据是从微软的PetShop和Sun的PetStore而来.这样的争论不可避免带有浓厚的 ...

  9. MVC实用架构设计(三)——EF-Code First(1):Repository,UnitOfWork,DbContext

    前言 终于到EF了,实在不好意思,最近有点忙,本篇离上一篇发布已经一个多星期了,工作中的小迭代告一段落,终于有点时间来继续我们的架构设计了,在这里先对大家表示歉意. 其实这段时间我并不是把这个系列给忘 ...

随机推荐

  1. go环境变量配置liteide配置

    1.go环境变量配置 http://download.csdn.net/detail/defonds/9408855下载后直接安装如果都选默认就只要添加一个gopath就可以了 不是默认添加goroo ...

  2. 都9102年了,还不会Docker?10分钟带你从入门操作到实战上手

    Docker简述 Docker是一种OS虚拟化技术,是一个开源的应用容器引擎.它可以让开发者将应用打包到一个可移植的容器中,并且该容器可以运行在几乎所有linux系统中(Windows10目前也原生支 ...

  3. cocos2d-x解析xml时的Bug

    cocos2d-x中使用tinyxml解析xml配置.如下: tinyxml2::XMLDocument doc; if (tinyxml2::XML_SUCCESS != doc.LoadFile( ...

  4. CentOS 6.9下双网卡绑定单个IP地址及装网卡绑定到一个网桥(转)

    说明:经过查阅,原来双网卡绑定一个IP的专业名词叫做bond,可以实现负载均衡:如果想要实现两张网卡绑定到一个网桥,基本思路是两张网卡设置同一个网桥是行不通的,但如果先实现bond,然后将bond绑定 ...

  5. php中NULL、false、0、" "有何区别?

    php中很多还不懂php中0,"",null和false之间的区别,这些区别有时会影响到数据判断的正确性和安全性,给程序的测试运行造成很多麻烦.先看一个例子: <? $str ...

  6. gitHub 基础命令

    设置开发人员信息 git config --global user.name "chen" git config --global user.email "xxxxx@q ...

  7. Java构造和解析Json数据的两种方法详解一——json-lib

    转自:http://www.cnblogs.com/lanxuezaipiao/archive/2013/05/23/3096001.html 在www.json.org上公布了很多JAVA下的jso ...

  8. JSON相关

  9. SQL Server 更新 触发器

    - 复制代码 代码如下: create trigger TR_MasterTable_Update on MasterTable after update as if update ([Type])- ...

  10. Android检测网络连接

    Android检测网络连接 import android.app.AlertDialog; import android.content.Context; import android.content ...