Netty in Action (十八) 第八章节 Bootstrapping

本章内容包括

1)客户端和服务器端的Bootstrapping

2)在一个Channel中的Bootstrapping客户端

3)增加ChannelHandler

4)使用ChannelOptions和attributes

已经深入地学习过了ChannelPipeline,ChannelHandler,codec等类,你接下来的可能会问:如何将这些组件组装起来增加到你的应用中去?

答案是:“Bootstrapping”,其实到目前为止,我们已经模糊地接触过几次这个组件了,现在是时候给它下一个明确的定义了,简单地陈述一下,Bootstrapping,顾名思义,就是通过配置可以将你的程序运行起来的意思,也许这个配置的细节比它的概念可能会难一点,特别是启动的是一个网络项目的时候

与Netty框架架构的理念一致,Netty处理Bootstrapping时进行了封装,使其与你的应用解耦,使你的服务器端和客户端都与网络层解耦,不要再注意配置底层实现的细节,你会看见,所有的框架组件连接组合都是在幕后进行的,你会发现Bootstrapping就像你在拼图的时候,缺失的那部分,当你将其放入正确的拼图位置的时候,你的Netty应用的整个蓝图就会完成了

8.1 Bootstrap classes

bootstrapping的类是继承与一个抽象的父类方法,两个具体的实现类,如8.1图所示

可以将这两个具体的实现类想象成服务器端和客户端启动类,这样可以帮助你区分他们所支持的不同的应用功能点,顾名思义,一个服务器端致力于创建一个父类channel来接收来自客户端的连接,并且创建一个子channel用来服务于两者之间的对话,然而对于客户端来说,一个客户端最多接收一个单独的非父类的channel用来进行网络交互,我们也会意识到,这种模式也是使用于无连接类型的传输类型的UDP协议,因为他们不需要为每次的连接创建一个channel

我们之前几个章节学习的一些Netty组件也会在启动的过程中加入进来,他们中的一些成员将会在服务器端和客户端都会被用到,bootstrapping将一些对于所有应用类型的公共实现方法放到了AbstractBootstrap中,而相对应的一些具体的操作放在了Bootstrap和ServerBootstrap中分别用来服务客户端和服务器端

在接下来的章节中,我们将详细地讨论这两个类的细节,我们先从比较简单的Bootstrap类讲起

TIPS:为什么bootstrap的类实现了Cloneable的接口呢?

你有时候需要创建多个channel,这些channel有着相同或者相似的配置,为了在不需要创建一个新的bootstrap实例来为每一个channel一一配置的前提下去支持这种功能模式,AbstractBootstrap就被标记了Cloneable,在一个已经配置好的bootstrap的基础上调用一个clone方法将会返回一个可以立即投入使用的bootstrap实例

请注意,这只是对Bootstrap的EventLoopGroup的浅拷贝,所以EventLoopGroup将在所有的channel中共享,这个是可以接受的,因为大部分的clone的channel它的生命周期经常是很短暂的,一个经典的案例就是创建一个Http请求

一个AbstractBootstrap的完整声明是这样的:

在这个声明中,子类B是父类中的一个类型参数,这样做的好处就是在运行时这个对象的引用可以被返回,这样就可以支持流式编程模式

子类的可以如下定义

8.2 Bootstrapping clients and connectionless protocols

Bootstrap被用在客户端或者使用在无连接的应用中,表8.1给出了这个类的一些概述,这些里面的很多方法是继续于AbstractBootstrap类

下一个小节我们将会一步步地分析客户端的bootstrapping,我们也会讨论在选择可利用的组件实现的时候,兼容性的一些问题

8.2.1 Bootstrapping a client

Bootstrap是用来为客户端创建一个channel并且为应用可以利用无连接的协议,如图8.2所示

下面的代码清单展示了Bootstrap的客户端使用NIO的TCP协议

这个例子很好地展示了流式编程风格,所有的方法除了connect方法之外都是通过前面方法返回的原始对象的引用链式调用的

8.2.2 Channel and EventLoopGroup compatibility

下面的目录是来自于io.netty.channel包的结构,你可以清晰地看见包名与包下对应的类的前缀名是一样的,这些类名是用来关联EventLoopGroup和Channel,并以NIO和OIO两种方式去实现

兼容性是必须去维护的,你不能混合的使用这些不同命名前缀的组件,例如将NioEventLoopGroup和OioSocketChannel混合使用,下面的代码清单展示了这样混合使用的尝试

这个代码块会引起IllegalStateException的异常,因为它混合了不相容的传输方式

TIPS:关于更多的IllegalStateException

在启动bootstrap时,你在调用bind或者connect方法之前,你必须使用如下的方法设置那些启动时的必要参数,例如group方法,channel方法或者channelFactory方法,和handler方法,如果做这些操作失败的话就会抛出IllegalStateException异常,handler方法特别重要,因为它是用来配置ChannelPipeline的

8.3 Bootstrapping servers

我们先列出ServerBootstrap的API,来给我们学习ServerBootstrap时做一个一个知识总览,然后我们就一步步地讲解启动服务器端时的一些小细节,并且讲解一些相关知识点,包括一个特殊的案例:从一个服务端的channel去启动一个客户端

8.3.1 The ServerBootstrap class

表8.2列出了ServerBootstrap的几个方法

下一个章节,我们将讲解server端的bootstrapping的一些细节

8.3.2 Bootstrapping a server

你可能已经看到表8.2中列出的一些方法并没有在表8.1中出现过,例如childHandler(),childAttr(),childOption()方法,这些是典型的支持服务器端操作的方法,具体来说,ServerChannel的实现负责用于创建一个子Channel,这个Channel负责接收一些链接,然后ServerBootstrap用来启动ServerChannel,提供了这些方法来简化一些链接channel的配置任务

图8.3展示了一个ServerBootstrap用bind方法来创建一个ServerChannel的情形,并且ServerChannel用来管理一定数量的子Channel

下面的代码清单实现了图8.3中服务器端启动的具体实现

8.4 Bootstrapping clients from a Channel

假设你的服务器端在需要处理一个客户端请求时,需要将服务器端的角色定位一个第三方的客户端的角色,这种情形是很容易经常发生的,例如代理服务器,这种情况下你需要与一个组织上的已经存在的系统再次进行交互,例如web server或者数据库,在这种情况下,你就需要从ServerChannel中启动一个客户端的Channel

你可以按照小节8.2.1中描述的那样创建一个Bootstrap,但是这并不是一个最有效的解决方案,因为它需要你再次定义另一个EventLoop用来管理新的客户端的channel,这就会需要新增额外的线程,当进行在原有的Channel和客户端channel之间数据交互的时候,就会产生线程之间的上下文切换

一个更好的解决方案就是分享接收的Channel的EventLoop,通过将EventLoop传递给Bootstrap的group方法,因为被分配到同一个EventLoop所有的Channel使用同一个线程,这样就阻止了更多额外进程的创建和我们之前提及的上下文切换的问题,这种分享EventLoop的方案说明图如下8.4图展示:

实现EventLoop共享需要通过调用group方法来设置EventLoop,如下面的代码清单所示:

关于共享EventLoop这个主题我们在这个小节已经讨论过了,这个解决防范反映了我们在编写Netty应用的一个思路和应该遵守的方针:尽可能的重复利用EventLoop来减少创建线程的开销

8.5 Adding multiple ChannelHandlers during a bootstrap

在我们之前的展示的所有代码中,我们在bootstrap处理一个单独的ChannelHandler的时候只调用了handler和childhandler方法,这对于一些简单的应用来说是很高效的,但是这并不能满足我们的一些比较复杂的需求,例如,如果一个应用需要支持多协议,那么就需要多个ChannelHandler了,来替代哪些笨重冗余的一些类

我们之前提及过很多次,你可以将你需要的所有ChannelHandler链式的放入到ChannelPipeline中去,但是如果在启动过程中只能设置一个ChannelHandler时怎么办的?

为了解决这个使用案例,Netty提供了一个特殊的父类ChannelInboundHandlerAdapter

它定义了如下的方法:

这个方法提供了一个简单的途径可以将多个ChannelHandler添加到ChannelPipeline中,你可以简单地通过提供一个ChannelInitializer的具体实现给bootstrap,当EventLoop注册到Channel时,你自定义的initChannel的方法就会被调用,当所有的返回之后,ChannelInitializer实例将会将自己从ChannelPipeline中移除

下面的代码清单定义了一个ChannelInitializerImpl类,且将其注册到bootstrap的childHandler方法上,你将会看到这个复杂操作可以被这么直白易懂地编写出

如果你的应用需要利用多个ChannelHandler,你可以自定义你自己的ChannelInitializer来将其安装到管道中

8.6 Using Netty ChannelOptions and attributes

当为每一个创建的channel一个个手动配置时将会变得非常冗余,幸运的是,我们不需要这么做,相反,你可以用option方法来将ChannelOptions应用到bootstrap中,你提供的一些属性值将会自动应用到bootstrap创建的所有channel中去,ChannelOptions中可以配置一些底层的连接细节例如keep-alive或者超时配置和buffer配置

Netty应用经常需要与一些项目中专属应用交互,Netty中的一些组件可能会被用在Netty项目之外的项目中运用到,例如Channel组件,在这种场景中,一些常用的属性和数据将会变得不可用,Netty提供了AttributeMap抽象,这个集合是由channel和bootstrap类提供的,和AttributeKey<T>这是一个通用的类用来帮助你插入和检索一些属性值,有了这些工具类,你可以在客户和服务端Channel中安全地交互任何形式的数据

思考一下下面的例子,当一个服务器端的应用需要追踪用户和channel之间关系的时候,你可以通过存储一个user的id做为一个channel的属性来实现这个功能,这个简单的小功能可以依据他们的id来路由到他们的客户端信息,或者来关闭客户端连接当channel中长时间没有任何活动的时候

下面的代码清单向你展示了如何使用ChannelOption来配置一个Channel这个属性中存储了一个Integer类型的变量

8.7 Bootstrapping DatagramChannels

先前的bootstrap代码示例都是使用ServerChannel,这是基于TCP的,但是有时候Bootstrap也可以用于无连接的协议,Netty提供了各种各样的DatagramChannel的具体实现来达到这个目的,唯一的不同就是你不用connect方法而是使用bind方法,如下面代码展示:

8.8 Shutdown

Bootstrapping使你的应用进入运行的状态,但是迟早你都需要将应用优雅地关闭,当然,你可以使用JVM处理,让所有的应用退出,但这不能满足“优雅”二字,这两个字代表了可以很干净地去释放所有的资源,其实在关闭Netty应用的时候,并没有太多需要特别关注的,只需要有几点细节需要牢记在心

首先,你需要先关闭EventLoopGroup,这可以处理任何后续的事件和任务,接下来就是释放所有的存活的线程,你只需要调用EventLoopGroup的shutdownGracefully方法,这个方法将会返回一个Future对象,当关闭的操作完成的时候,将会通知Future对象,注意到shutdownGracefully这个方法也是异步操作,所以你需要要么阻塞当前的操作指导关闭操作的完成,要么注册一个监听器来监听Future对象来确定关闭是否完成

下面的代码清单展示了如何优雅地关闭

另外,你可以在调用EventLoopGroup的shutdownGracefully方法之前调用Channel.close方法来清晰地关闭所有存活的channel对象,但是在所有的案例中,请一定要关闭EventLoopGroup对象本身

8.9 Summary

在这个章节中,我们学习了如何启动Netty的客户端和服务器端,包括使用无连接的协议,我们使用了一些具体的案例来讲解,这些案例包括在服务器端的应用去启动一个客户端的channel,还包括在启动过程中如何使用ChannelInitializer来处理安装多个channel

你也了解到在channel中进行配置一些具体的选项,如何使用attribute来将一些附加的信息添加到channel中,最后我们学习到如何优雅地关闭一个应用来按顺序地去释放所有的资源

在下一个章节,我们将学习Netty提供的一些工具,用来测试你自定义的ChannelHandler的实现

时间: 2024-10-28 21:44:10

Netty in Action (十八) 第八章节 Bootstrapping的相关文章

Netty in Action (十五) 第六章节 第一部分 ChannelHandler和ChannelPipeline

本章内容包括: 1)ChannelHandler和ChannelPipeline的APIs 2)检测内存泄漏 3)异常处理 在之前的一个章节中,我们学习了ByteBuf,Netty的数据容器,在这个章节中,我们将讲解Netty的数据流和对应的处理组件,然后我们将我们已经学过的所有组件整合在一起 你已经知道多个ChannelHandler可以被链式的放入ChannelPipeline来将所有的处理逻辑组织在一起,我们将学习包涵这些有关类的很多用户案例和他们之间的对应关系------ChannelH

矩阵十题【八】 HDU 1715 A Simple Math Problem

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1757 题目大意: If x < 10   ,则  f(x) = x. If x >= 10 ,则  f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + -- + a9 * f(x-10); 给出k,m和a0~a9,求f(k)%m,  k<2*10^9 , m < 10^5 这是一个递推式,故可以用矩阵乘法来求 和上题类似,具体思路过程见上题

Python——简单实现十进制对十六、八、二进制的转换

#coding:utf-8 '''     最近在看数据结构----Stack     这也是来自国外大神写的一个" 教学 " 十进制数字转换成----十六.八.二进制 ''' # 通过面向对象class了一个Stack class Stack:     def __init__(self):         self.items = [] def isEmpty(self):         return self.items == [] def push(self, item):

《Netty in action》目录修复版本分享

最近阅读了Netty in action一书.深感外国友人的书籍编写能力强大.作者由简入深.精简描述了Netty的相关知识,如何使用等等. 本来想翻译一下的.尝试着翻译了一点之后.发现非常痛苦啊.ps.笔者英语不是很好. 很多时候需要自己先把英文理解成中文然后再拼凑起来.写出来的中文译文感觉好奇怪.所以就不拿出来献丑了.把网上的PDF电子书分享一下. 前段时间去找的电子书目录都存在问题的. NIA一书分为四个部分.16个章节.网上的第五版书籍是完整的.但是目录结构完全错误了.再次使用软件修复了.

Netty In Action中国版 - 第二章:第一Netty程序

本章介绍 获得Netty4最新的版本号 设置执行环境,以构建和执行netty程序 创建一个基于Netty的server和client 拦截和处理异常 编制和执行Nettyserver和client 本章将简介Netty的核心概念,这个狠心概念就是学习Netty是怎样拦截和处理异常.对于刚開始学习netty的读者.利用netty的异常拦截机制来调试程序问题非常有帮助.本章还会介绍其它一些核心概念.如server和client的启动以及分离通道的处理程序.本章学习一些基础以便后面章节的深入学习. 本

《Netty In Action中文版》第二章:第一个Netty程序

注:本篇内容出自<Netty In Action>一书:         注:本人原创译文,转载请注明出处! 本章介绍 获取Netty4最新版本 设置运行环境来构建和运行netty程序 创建一个基于Netty的服务器和客户端 拦截和处理异常 编写和运行Netty服务器和客户端 本章将简单介绍Netty的核心概念,这个狠心概念就是学习Netty是如何拦截和处理异常,对于刚开始学习netty的读者,利用netty的异常拦截机制来调试程序问题很有帮助.本章还会介绍其他一些核心概念,如服务器和客户端的

Netty In Action中文版 - 第四章:Transports(传输)

本章内容 Transports(传输) NIO(non-blocking IO,New IO), OIO(Old IO,blocking IO), Local(本地), Embedded(嵌入式) Use-case(用例) APIs(接口) 网络应用程序一个非常重要的工作是数据传输. 数据传输的过程不一样取决是使用哪种交通工具,可是传输的方式是一样的:都是以字节码传输.Java开发网络程序数据传输的过程和方式是被抽象了的.我们不须要关注底层接口.仅仅须要使用Java API或其它网络框架如Net

Python:编程“八荣八耻”

以动手实践为荣,以只看不练为耻. 以打印日志为荣,以单步跟踪为耻. 以空白分隔为荣,以制表分隔为耻. 以单元测试为荣,以手工测试为耻. 以代码重用为荣,以复制粘贴为耻. 以多态应用为荣,以分支判断为耻. 以Pythonic为荣,以冗余拖沓为耻. 以总结思考为荣,以不求甚解为耻. 刚刚看到,不可尽信,也不可全部不信.理性看待. 1. "以动手实践为荣, 以只看不练为耻" 俺写一个程序时间通常是这么分配的. 70% 的时间用来寻找和阅读现有代码, 如果找到了, 就不用自己写了.如果不够用或

Netty In Action中文版 - 第六章:ChannelHandler

本章介绍 ChannelPipeline ChannelHandlerContext ChannelHandler Inbound vs outbound(入站和出站) 接受连接或创建他们仅仅是你的应用程序的一部分,尽管这些不论什么非常重要,可是一个网络应用程序旺旺是更复杂的,须要很多其它的代码编写,如处理传入和传出的数据.Netty提供了一个强大的处理这些事情的功能,同意用户自己定义ChannelHandler的实现来处理数据.使得ChannelHandler更强大的是能够连接每一个Chann

Netty in Action Version5

1.Netty介绍 1.1为什么需要Netty 1.1.1不是所有的网络框架都是一样的 1.1.2Netty的功能非常丰富 框架组成 1.2异步设计 1.2.1Callbacks(回调) 简单的回调 public interface Fetcher { void fetchData(FetchCallback callback); } public interface FetchCallback { void onData(Data data); void onError(Throwable c