概述
在前面的章节中,分别对 Netty 的各个核心组件进行了详细的介绍。那么这些组件是如何协同起来一起工作,构建成为一个引用程序的了?答案就在于引导——简单说来,就是对 Netty 提供的各个核心组件进行配置,组合成为一个可以运行的应用程序。
由于应用程序又有 客户端(client)和服务端(server)两种类型,所以 Netty 中提供了服务端引导(ServerBootStrap )和 客户端引导(BootStrap)。
8.1 引导客户端
8.1.1Channel 和 EventLoopGroup 的兼容性
Netty 传输有 NIO 和 OIO 两种方式,都有相关的 EventLoopGroup 和 Channel 实现。不同传输方式对应的组件不能混用。如下图
如上图列出的组件,我们要么使用 NIO 为前缀的所有组件,要么使用 OIO 为前缀的所有组件,不能混用,否则会抛异常(IllegalStateException)
注:在调用 BootStrap 的 bind() 或者 connect()方法之前,必须先调用
group()—— 用于确定处理事件的 EventLoopGroup
channel()—— 用于确定待使用的通道类型
handler()—— 用于确定处理具体业务逻辑的 handler 方法
否则,也会抛出 IllegalStateException 异常。
8.2 引导服务器
服务的引导和客户端基本类似,主要的区别在于:服务端会创建两个 EventLoopGroup,一个专门用于创建连接,另一个专门用于连接创建好之后与客户端数据通信的处理
编写 Netty 应用程序有一个一般的准则:尽可能的重用 EventLoop,以减少线程创建所带来的开销(因为每一个 EventLoop 都由一个线程支撑着)
在引导的过程中添加多个 ChannelHandler
在 Netty 业务程序中,我们的逻辑部分都是在 ChannelHandler 中进行处理的,所以我们在引导客户端或者服务端的时候,有必要添加多个 ChannelHandler。那怎么添加了?Netty 提供了一个 ChannelInitializer 类,这个内容有一个抽象方法 initChannel(Channel ch),你只需实现这个方法,让后通过 ChannelPipeLine 的相关方法添加你需要的 ChannelHandler 即可。类似于下面这样
bootStrap.handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline cpl = ch.pipeline(); cpl.addLast(new LoggingHandler(LogLevel.INFO)); cpl.addLast(new EchoClientHandler()); } });
8.2.1 使用 Netty 的 ChannelOption 和 属性
AttributeMap的作用在于关联组件和某些数据,如跟踪某个 channel 具体是哪个客户(userId)在使用。通过AttrbuteMap就可以把指定用户的 userId 存储到指定的 channel,实现绑定。类似下面这样做
final AttributeKey<Integer> userId = AttributeKey.valueOf("userId"); BootStrap bootStrap = new BootStrap(); ... bootStrap.attr(userId, 123456);//绑定 ... channel.attr(userId)//获取 ...
详细内容可以参考AttributeMap简介
8.3 关闭
应用程序启动之后,最终的归宿都是会关闭的,就和人的最终归宿都是会进去极乐世界一样的,如何优雅的离去,这是一个值得思考的问题。Netty 为我们提供了一种优雅关闭所有通信的方法:EventLoopGroup.shutdownGracefully(),该方法可以干净的释放所有的资源。优雅退出有如下好处:
- 尽快的释放NIO线程、句柄等资源;
- 如果使用flush做批量消息发送,需要将积攒在发送队列中的待发送消息发送完成;
- 正在write或者read的消息,需要继续处理;
- 设置在NioEventLoop线程调度器中的定时任务,需要执行或清理。
详细内容可以参考Netty优雅的关闭
总结
本章节介绍了
Netty 的各个组件如何通过引导协同串联起来,形成一个应用程序。
Netty 的一个一般原则:尽可能的重用 EventLoop(处理所有的事件、IO流),以减少线程创建所带来的开销,因为每一个EventLoop 都由一个线程支撑着
AttributeMap,一个特殊的容器,用于存储属于某个组件特有的数据
shutdownGracefully:优雅的关闭方式
下一章,将对 Netty 的单元测试进行讲述