Netty4具体解释三:Netty架构设计

     读完这一章,我们基本上能够了解到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的:

从上图中我们能够看到,一个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,建议大家阅读一下我的另两篇文章:

Java
NIO具体解释(一)

Java NIO具体解释(二)

转载请说明出处,原文链接:http://blog.csdn.net/suifeng3051/article/details/28861883

时间: 2024-10-23 22:36:22

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

UML笔记1---结合架构设计用对象建模

UML笔记1---结合架构设计用对象建模 一.UML的视图和图 视图,只是表达系统某一方面特征的UML建模组件的子集:视图被划分成三个视图域:结构分类.动态行为和模型管理. ---结构分类,描述了系统中的结构成员及其相互关系.类元包括类.用例.构件和节点.类元为研究系统动态行为奠定了基础.类元视图包括静态视图.用例视图和实现视图. ---动态行为描述了系统随时间变化的行为.行为用从静态视图中抽取的瞬间值的变化来描述.动态行为视图包括状态机视图.活动视图和交互视图. ---模型管理说明了模型的分层

架构设计:系统间通信(7)——IO通信模型和Netty 下篇

接上文<架构设计:系统间通信(6)--IO通信模型和Netty 上篇> 5.再次审视为什么使用Netty 上篇文章我们讨论了Netty的基本原理,重要概念,并使用java代码描述了Netty的基本使用.当然Netty的技术涵盖点远远不是那一篇基础代码就可以全部概括的,但是至少可以给读者一个切入点.让大家去思考一个我们一直在讨论的问题:为什么有了JAVA NIO框架后我们还需要有Netty这样的框架对底层再次进行封装? 5-1.IO模型的封装 5-1-1.再次总结IO模型 在前文中我们已经提到了

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

本文转自:http://www.cnblogs.com/guomingfeng/p/mvc-ef-t4.html 〇.目录 一.前言 二.工具准备 三.T4代码生成预热 (一) 单文件生成:HelloWorld.cs (二) 多文件生成 四.生成数据层实体相关相似代码 (一) 生成准备 (二) 生成实体相关相似代码 生成实体映射配置类 生成实体仓储接口 生成实体仓储实现 五.源码获取 系列导航 一.前言 经过前面EF的<第一篇>与<第二篇>,我们的数据层功能已经较为完善了,但有不少

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

前言:PetShop是一个范例,微软用它来展示.Net企业系统开发的能力.业界有很多.Net与J2EE之争,很多数据是从微软的PetShop和Sun的PetStore而来.这样的争论不可避免带有浓厚的商业色彩,对于我们开发者而言,没有必要过多关注.然而PetShop随着版本号的不断更新,至如今基于.Net 2.0的PetShop4.0为止,整个设计逐渐变得成熟而优雅,却又非常多能够借鉴之处.PetShop是一个小型的项目,系统架构与代码都比較简单,却也凸现了很多颇有价值的设计与开发理念.本系列试

XMPP协议实现即时通讯底层书写 (三) IOS XMPPFramework --IM底层架构设计+技术准备工作

最近发生了一些不是很愉快的事情,导致断更很长一段时间,很抱歉."不要炫技,理解原理,对自己的代码负责,才能对团队和项目负责"--郭前辈在群里说过的语录,让我很是欢喜和受教.鄙人写第一次写blog是在2011年,那时候写技术blog的初衷是为了写日记:今天我学到了什么知识,技术,记录自己程序猿的成长点滴.随着技术的积累,写blog为了分享:傻逼,如果你也碰到这种问题,这是我的解决方案,看了这些XXX处理好的,可以"抄"这份60分的答案来解决问题.到现在这阶段,写blo

个人网站架构设计(三) - 从设计到前端到后台

在五月份,写过两篇博客,提到了要给自己做个网站,当时人在实习,没太多的时间,只是把大概的思路捋了一番,顺道也买了个云主机(配置比较低,内存才500M).接着返校处理毕业事宜,于是六月也随着同学之间挥泪告别的声音渐渐远去.七月,家里呆着,中旬回公司.想必这也是我近几年最长的一次假期了=. = 一.先说设计 1. 阮一峰的博客 目前我的博客设计是 fork 了 BeiYuu 的主题,然后七改八改,除了主页 BeiYuu 还认得出是他的之外,其他页面已经动了很大的手术,而这些手术灵感都是源自阮一峰阮大

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

前言 终于到EF了,实在不好意思,最近有点忙,本篇离上一篇发布已经一个多星期了,工作中的小迭代告一段落,终于有点时间来继续我们的架构设计了,在这里先对大家表示歉意. 其实这段时间我并不是把这个系列给忘记了,而是一直在思考,想着接下来应该怎么写.因为园子里已经有很多非常优秀的EF的文章了,比如: Entity Framework Code First 学习日记 [译著]Code First :使用Entity. Framework编程 Entity Framework技术系列 EF框架step b

[笔记]《游戏架构设计与策划基础》第三章 游戏概念及原型设计

概念设计的过程:产生创意.加工创意和创建游戏概念设计文档. 3.1 创意的来源 (1)大胆设想 (2)利用现有的娱乐资源 (3)利用现有的游戏体系 (4)收集创意 3.2 加工创意 (1)合成--需要考虑如何将两个概念融合而成一款游戏,带给玩家新的游戏体验. (2)共鸣--含有协作的意思,它使故事和主题内容对游戏玩家能够产生更加深刻的影响. 3.3 游戏概念设计文档 一般包括以下要素的部分或全部:      标题--游戏的名称.      平台--游戏适合的平台.      种类--游戏的种类.

ThinkPHP 3.2.3(三)架构之模块化设计

一.概念 应用:基于同一个入口文件访问的项目称之为一个应用. 模块:一个应用下面可以包含多个模块,每个模块在应用目录下面都是一个独立的子目录,是一个包含配置文件.函数文件和MVC文件(目录)的集合. 控制器:每个模块可以包含多个控制器,一个控制器通常体现为一个控制器类. 操作:每个控制器类可以包含多个操作方法,也可能是绑定的某个操作类,每个操作是URL访问的最小单元. 二.典型的URL访问规则 http://serverName/index.php(或者其他应用入口文件)/模块/控制器/操作/[