Netty实战三之Netty的组件和设计
有关Netty,我们可以从两个视角来讨论Netty:类库的视角以及框架的视角,对于使用Netty编写高效的、可重用的和可维护的代码来说,两者缺一不可。
Netty解决了两个响应的关注领域,可以大致标志为技术的和体系结构的。
它基于Java NIO的异步和事件驱动的实现,保证了高负载下应用程序性能的最大化和可伸缩性。其次,Netty也包含了一组设计模式,将应用程序逻辑从网络层解耦,简化开发过程,同时也最大限度地提高了可测试性、模块化以及代码的可重用性。
Netty网络抽象的代表:
——Channel:Socket
——EventLoop:控制流、多线程处理、并发
——ChannelFuture:异步通知
1、Channel接口
Netty的Channel接口所提供的的API,大大降低了直接使用Socket类的复杂性,此外,Channel也拥有许多预定义的、专门化实现的广泛类层次结构的根。
——EmbeddedChannel
——LocalServerChannel
——NioDatagramChannel
——NioSctpChannel
——NioSocketChannel
2、EventLoop接口
EventLoop定义了Netty的核心抽象,用于处理连接的生命周期中所发生的事件。图3-1在高层次上说明了Channel、EventLoop、Thread以及EventLoopGroup之间的关系。
——EventLoopGroup包含一个或者多个EventLoop
——一个EventLoop在它的生命周期内只和一个Thread绑定
——所有由EventLoop处理的I/O事件都将在它专有的Thread上被处理
——一个Channel在它的生命周期内只注册于一个EventLoop
——一个EventLoop可能会被分配给一个或多个Channel
注意,在这种设计中,一个给定Channel的I/O操作都是由相同的Thread执行的,实际上消除了对于同步的需要。
3、ChannelFuture接口
Netty中所有的I/O操作都是异步的,因为一个操作可能不会立即返回,所以我们需要一种用于在之后的某个时间点确定其结果的方法,为此,Netty提供了ChannelFuture接口,其addListener()方法注册了一个ChannelFutureListener,以便在某个操作完成时(无论是否成功)得到通知。
关于ChannelFuture的更多讨论——可以将ChannelFuture看作是将来要执行的结果的占位符,它究竟什么时候被执行则可能取决于若干的因素,因此不可能准确地预测,但是可以肯定的是它将会被执行,此外,所有属于同一个Channel的操作都被保证其将以它们被调用的顺序被执行。
4、ChannelHandler接口
Netty的主要组件是ChannelHandler,它充当了所有处理入站和出站数据的应用程序逻辑的容器。因为ChannelHandler的方法是由网络事件触发的。事实上,ChannelHandler可专门用于几乎任何类型的动作,例如将数据从一种格式转换为另一种格式,或者处理转换过程中所抛出的异常。
例如,ChannelInboundHandler 是一个你将会经常实现的子接口,这种类型的ChannelHandler接收入站事件和数据,这些数据随后将会被你的应用程序的业务逻辑所处理。当你要给连接的客户端发送响应时,也可以从ChannelInboundHandler冲刷数据。你的应用程序的业务逻辑通常驻留在一个或者多个ChannelInboundHandler中。
5、ChannelPipeline接口
ChannelPipeline为ChannelHandler链提供了容器,并定义了用于在该链上传播入站和出站事件流的API。当Channel被创建时,它会被自动地分配到它专属的ChannelPipeline。
——一个ChannelInitializer的实现被注册到了ServerBootstrap中; ——当ChannelInitializer.initChannel()方法被调用时,ChannelInitializer将在ChannelPipeline中安装一组自定义的ChannelHandler; ——ChannelInitializer将它自己从ChannelPipeline中移除;
ChannelHandler是专为支持广泛的用途而设计的,可以将它看作是处理往来ChannelPipeline事件(包括数据)的任何代码的通用容器。图2-3说明了这一点,其展示了从ChannelHandler派生的ChannelInboundHandler和ChannelOutboundHandler接口。
使得事件流经ChannelPipeline是ChannelHandler的工作,它们是在应用程序的初始化或者引导阶段被安装的,这些对象接收事件、执行它们所实现的处理逻辑,并将数据传递给链中的下一个ChannelHandler。它们的执行顺序是由它们被添加的顺序所决定的。ChannelPipeline就是ChannelHandler的编排顺序。
图3-3说明了一个Netty应用程序中入站和出站数据流之间的区别。从一个客户端应用程序的角度来看,如果事件的运动方向是从客户端到服务器端,那么我们称这些事件为出站,反之则称为入站。
由上图可看出入站和出站ChannelHandler可以被安装到同一个ChannelPipeline中,事件的读取将从ChannelPipeline的头部开始流动,并被传递给第一个ChannelInboundHandler,这个ChannelHandler不一定会实际修改数据,具体取决于它的具体功能,在之后,数据将会传递给链中的下一个ChannelInboundHandler,最终,数据将会到达ChannelPipeline的尾端,即所有处理结束。
出站事件也相同的流程,在到达链的头部之后,出站数据将会到达网络传输层,这里显示为Socket,通常情况下,这将触发一个写操作。
关于入站和出站ChannelHandler的更多讨论
通过使用作为参数传递到每个方法的ChannelHandlerContext,事件可以被传递给当前ChannelHandler链中的下一个ChannelHandler。因为你有时会忽略那些不感兴趣的事件,所以Netty提供了抽象基类ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter。通过调用ChannelHandlerContext上的对应方法,每个都提供了简单地将事件传递给下一个ChannelHandler的方法的实现,随后,你可以通过重写你所感兴趣的那些方法来扩展这些类。
虽然ChannelInboundHandler和ChannelOutboundHandler都扩展自ChannelHandler,但是Netty能区分ChannelInboundHandler实现和ChannelOutboundHandler实现,并确保数据只会在具有相同定向类型的两个ChannelHandler之间传递。
当ChannelHandler被添加到ChannelPipeline时,它将被分配一个ChannelHandlerContext,其代表了ChannelHandler和ChannelPipeline之间的绑定。虽然这个对象可以被用于获取底层的Channel,但是它主要还是被用于写出站数据。
在Netty中,有两种发送消息的方式。你可以直接写到Channel中,也可以写到ChannelHandler相关联的ChannelHandlerContext对象中,前一种方式将会导致消息从ChannelPipeline的尾端开始流动,而后者将导致消息从ChannelPipeline中的下一个ChannelHandler开始流动。
6、深入了解ChannelHandler
有许多不同类型的ChannelHandler,它们各自的功能主要取决于它们的超类。Netty以适配器类的形式提供了大量默认的ChannelHandler实现,其旨在简化应用程序处理逻辑的开发过程,ChannelPipeline中的每个ChannelHandler将负责把事件转发到链中的下一个ChannelHandler,这些适配器将自动执行这个操作,所以你可以只重写那些你想要特殊处理的方法和事件。
为什么需要适配器类
有些适配器类可以将编写自定义的ChannelHandler所需要的努力降到最低限度,因为他们提供了定义在对应接口中的所有方法的默认实现。
——ChannelHandlerAdapter
——ChannelInboundHandlerAdapter
——ChannelOutboundHandlerAdapter
——ChannelDuplexHandler
7、编码器和解码器
当你通过Netty发送或者接收一个消息的时候,就将会发生一次数据转换。入站就解码为Java对象,出站就编码为字节。(网络数据总是一系列的字节)
所有由Netty提供的编码器、解码器适配器类都实现了ChannelOutboundHandler或者ChannelInboundHandler接口。
你将会发现对于入站数据,channelRead方法、事件已经被重写了,对于每个从入站Channel读取消息,这个方法都将会被调用,随后,它将调用由预置解码器所提供的的decode()方法,并将已解码的字节转发给ChannelPipeline中的下一个ChannelInboundHandler。
出站消息的模式是相反方向的:编码器将消息转换为字节,并将它们转发给下一个ChannelOutboundHandler。
8、抽象类SimpleChannelInboundHandler
最常见的情况是,你的应用程序会利用一个ChannelHandler来接收解码消息,并对该数据应用业务逻辑。要创建一个这样的ChannelHandler,你只需要扩展基类SimpleChannelInboundHandler,其中T是你要处理的消息的Java类型,在这个ChannelHandler中,你将需要重写基类的一个或者多个方法,并且获取一个到ChannelHandlerContext的引用,这个引用将作为输入参数传递给ChannelHandler的所有方法。
在这种类型的ChannelHandler中,最重要的方法是channelRead0(ChannelHandlerContext,T)。除了要求不要阻塞当前的I/O线程之外,其具体实现完全取决于你。
9、引导
Netty的引导类为应用程序的网络层配置提供了容器,这涉及将一个进程绑定到某个指定的端口,或者将一个进程连接到另一个运行在某个指定主机的指定端口上的进程。
“服务器”和“客户端”实际上表示了不同的网络行为:是监听传入的连接还是建立到一个或者多个进程的连接。
面向连接的协议:请记住,严格来说,“连接”这个属于仅适用于面向连接的协议,如TCP、其保证了两个连接端点之间消息的有序传递。
因此,有两种类型的引导:一种用于客户端(简单称为Bootstrap),而另一种(ServerBootstrap)用于服务器。无论你的应用程序使用哪些协议或者处理哪种类型的数据,唯一决定它使用哪种引导类的是它是作为一个客户端还是作为一个服务器。
这两种类型的引导类之间的第一个区别已经讨论过了:ServerBootstrap将绑定到一个端口,因为服务器必须要监听连接,而Bootstrap则由想要连接到远程节点的客户端应用程序所使用的。
第二个区别可能更加明显,引导一个客户端只需要一个EventLoopGroup,但是一个ServerBootstrap则需要两个(也可以是同一个实例),为什么呢?
因为服务器需要两组不同的Channel。第一组将只包含一个ServerChannel,代表服务器自身的已绑定到某个本地端口的正在监听的套接字。而第二组将包含所有已创建的用来处理传入客户端连接(对于每个服务器已经接受的连接都有一个)的Channel。
与ServerChannel相关联的EventLoopGroup将分配一个负责为传入连接请求创建Channel的EventLoop。一旦连接被接收,第二个EventLoopGroup就会给它的Channel分配一个EventLoop。
Netty实战三之Netty的组件和设计的更多相关文章
- Netty学习(三)-Netty重要接口讲解
上一节我们写了一个HelloWorld,对于Netty的运行有了一定的了解,知道Netty是如何启动客户端和服务器端.这一节我们简要的讲解一下几个重要的接口,初步探讨Netty的运行机制,当然刚学Ne ...
- 深入了解Netty【三】Netty概述
1.简介 Netty是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端. Netty是一个NIO客户端服务器框架,它支持快速.简单地开发协议服务器和客户端等网络应用程序 ...
- 深入了解Netty【六】Netty工作原理
引言 前面学习了NIO与零拷贝.IO多路复用模型.Reactor主从模型. 服务器基于IO模型管理连接,获取输入数据,又基于线程模型,处理请求. 下面来学习Netty的具体应用. 1.Netty线程模 ...
- 重磅!阿里P8费心整理Netty实战+指南+项目白皮书PDF,总计1.08G
前言 Netty是一款用于快速开发高性能的网络应用程序的Java框架.它封装了网络编程的复杂性,使网络编程和Web技术的最新进展能够被比以往更广泛的开发人员接触到. Netty不只是一个接口和类的集合 ...
- Java架构师系统培训高并发分布式电商实战activemq,netty,nginx,redis dubbo shiro jvm虚拟机视频教程下载
15套java架构师.集群.高可用.高可扩 展.高性能.高并发.性能优化.Spring boot.Redis.ActiveMQ.Nginx.Mycat.Netty.Jvm大型分布 式项目实战视频教程 ...
- Netty4具体解释三:Netty架构设计
读完这一章,我们基本上能够了解到Netty全部重要的组件,对Netty有一个全面的认识.这对下一步深入学习Netty是十分重要的,而学完这一章.我们事实上已经能够用Netty解决一些常规的问 ...
- Netty4详解三:Netty架构设计(转)
http://blog.csdn.net/suifeng3051/article/details/28861883?utm_source=tuicool&utm_medium=referral ...
- Netty实战
一.Netty异步和事件驱动1.Java网络编程回顾socket.accept 阻塞socket.setsockopt /非阻塞2.NIO异步非阻塞a).nio 非阻塞的关键时使用选择器(java.n ...
- (入门篇 NettyNIO开发指南)第三章-Netty入门应用
作为Netty的第一个应用程序,我们依然以第2章的时间服务器为例进行开发,通过Netty版本的时间服务报的开发,让初学者尽快学到如何搭建Netty开发环境和!运行Netty应用程序. 如果你已经熟悉N ...
随机推荐
- 本周对于java中lamdba表达式与内部进行了学习 ,以下是我在学习就中遇到的问题
在java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类.我觉得其实就是类的嵌套,在一个类中再定义一个类,这里已成员内部类为讲,内部类可以自由地运用外部类定义的方法,但外部类想 ...
- 【DocFX文档翻译】DocFX 入门 (Getting Started with DocFX)
DocFX 入门 1. DocFX 是什么? DocFX 是一个基于.NET的API文档生成器,当前支持 C# 和 VB. 它可以通过你的代码中的三斜杠注释生成 API 参考文档.同样也支持你使用 M ...
- 一篇入门 -- Scala 反射
本篇文章主要让大家理解什么是Scala的反射, 以及反射的分类, 反射的一些术语概念和一些简单的反射例子. 什么是反射 我们知道, Scala是基于JVM的语言, Scala编译器会将Scala代码编 ...
- [W3bsafe]分享一个爬SQL注入漏洞的工具
分享一个爬SQL注入的工具 本文转自:i春秋社区由团队核心成员若间开发把工具放到E盘的一个文件夹 他会自动生成一个文本文件 Result.txt 最大页数 自己想弄填多少就填多少关键词 注入点关键词 ...
- Android NDK学习(二):编译脚本语法Android.mk和Application.mk
一.Android.mk Android.mk分为一下几部分: LOCAL_PATH:= $(call my-dir), 返回当前文件在系统中的路径,Android.mk文件开始时必须定义该变量. i ...
- 使用Java面向对象单词必备
第一章 class 班级,用声明类 object 目标,整个程序集对大 static 静态的 final 不可更改的,用声明常量 private 私有的,用访问权限 public ...
- UC手机浏览器js加入收藏夹
概述 对于某些网站来说,让用户一键把网页加入收藏夹的设计是非常棒的,它能提醒用户把网页加入收藏夹,从而增加用户的回访率,使网站获得更多的流量. 在PC端,只有ie和ff支持用js把网页加入收藏夹的操作 ...
- Spark基础-scala学习(四、函数式编程)
函数式编程 将函数赋值给变量 匿名函数 高阶函数 高级函数的类型推断 scala的常用高阶函数 闭包 sam转换 currying函数 return 将函数赋值给变量 scala中的函数是一等公民,可 ...
- 史上最全阿里 Java 面试题总结
以下为大家整理了阿里巴巴史上最全的 Java 面试题,涉及大量 Java 面试知识点和相关试题. JAVA基础 JAVA中的几种基本数据类型是什么,各自占用多少字节. String类能被继承吗,为什么 ...
- python(leetcode)-283移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序. 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明: 必须在原数组上操作, ...