持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情
背景 周末闲来无事,在网上看到很多大神都对Netty有所了解,非常推荐要阅读其源码,作为菜鸟的我不禁也想了解一番,话不多说,立马开始。
要了解netty,首先得了解NIO和IO之间的区别
| IO | NIO |
|---|---|
| 面向流 | 面向换从 |
| 阻塞IO | 非阻塞IO |
| 无 | 选择器 |
还有一个区别是IO使用多线来处理多个连接的请求,而NIO可以使用单个线程处理多个连接。
我们Java中也有NIO相关的库,但是有诸多不便 a.NIO的类库和API繁杂,使用麻烦,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等;
b.需要具备其它的额外技能做铺垫,例如熟悉Java多线程编程,因为NIO编程涉及到Reactor模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的NIO程序;
c.可靠性能力补齐,工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等,NIO编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大
但是如果我们使用Netty就可以解决部分以上问题 a.API使用简单,开发门槛低;
b.功能强大,预置了多种编解码功能,支持多种主流协议;
c.定制能力强,可以通过ChannelHandler对通信框架进行灵活的扩展;
d.性能高,通过与其它业界主流的NIO框架对比,Netty的综合性能最优;
e.成熟、稳定,Netty修复了已经发现的所有JDK NIO BUG,业务开发人员不需要再为NIO的BUG而烦恼;
f.社区活跃,版本迭代周期短,发现的BUG可以被及时修复,同时,更多的新功能会被加入;
核心组件
Netty有如下核心组件
- Bootstrap or ServerBootstrap a.一个用于客户端(简称为Bootstrap),另一个用于服务器(ServerBootstrap)
b. Bootstrap一个客户端只需要一个EventLoopGroup,但是一个ServerBootstrap需要2个(可以是同一个实例)
-
EventLoop
-
EventLoopGroup EventLoop定义了Netty的核心抽象,用来处理发生在一个连接生命周期中的event。 EventLoopGroup用来管理EventLoop分配的线程池。 一个EventLoopGroup包含一个或者多个EventLoop 所有的EventLoop在它的整个生命周期里都绑定在一个线程上 所有被一个EventLoop处理的event都是由EventLoop绑定的线程处理的 一个Channel在它的生命周期内只注册到一个EventLoop上 一个EventLoop可以被分配给一个或者多个Channel。
-
ChannelPipeline ChannelPipeline可以理解成一个消息( 或消息事件,ChanelEvent)流转的管道,在这个通道中可以被附上许多用来处理消息的handler,当消息在这个管道中流转的时候,如果有与这个消息类型相对应的handler,就会触发这个handler去执行相应的动作。 ChannelInBoundHandler负责数据进入并在ChannelPipeline中按照从上至下的顺序查找调用相应的InBoundHandler。 ChannelOutBoundHandler负责数据出去并在ChannelPipeline中按照从下至上的顺序查找调用相应的OutBoundHandler。 DefaultChinnelPipeline内部使用了两个特殊的Handler来表示Handler链的头和尾. ChannelHandlerContext 主要控制事件如何传播。 ChannelHandlerContext.fireIN_EVT()传递InboundHandler, ChannelHandlerContext.OUT_EVT()传递OutboundHandler.
-
Channel 基本的I/O操作(bind(),connect(),read()和write())依赖于底层网络传输层提供的基本类型。在基于Java的网络编程中,基本的构造单元就是Socket类。 重点实现: NioSocketChannel NioServerSocketChannel NioDatagramChannel
-
Future or ChannelFuture ChannelFuture的作用是用来保存Channel异步操作的结果。在Netty中所有的IO操作都是异步的,因此,你不能立刻得知消息是否被正确处理,但是我们可以过一会等它执行完成或者直接注册一个监听,具体的实现就 是通过Future和ChannelFuture,他们可以注册一个监听,当操作执行成功或失败时监听会自动触发。总之,所有的操作都会返回一个 ChannelFuture。 例如关闭通道操作:
ChannelFuture future = ctx.channel().close();
future.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) {
// Perform post-closure operation
// ...
}
});
-
ChannelInitializer
-
ChannelHandler 应用开发者主要组件是ChannelHandler,它作为一个应用逻辑的容器,处理输入输出数据。ChannelHandler实现了具体应用逻辑来处理状态变化和进行数据加工。 ChannelHandler的典型使用包括: 转换数据格式 当异常发生时发出通知 当一个Channel状态变成活跃(active)或者闲置时(inactive)发出通知 当一个Channel在一个EventLoop上注册或者撤销注册时发出通知 发出关于用户定义的event的通知
-
ChannelHandlerContext
-
ByteBuf ByteBuf则是Java NIO Buffer的新轮子,官方列出了一些ByteBuf的特性: 需要的话,可以自定义buffer类型; 通过组合buffer类型,可实现透明的zero-copy; 提供动态的buffer类型,如StringBuffer一样,容量是按需扩展; 无需调用flip()方法; 常常比ByteBuffer快。
ByteBuf提供了大量的方法,比较常用的有下面这些: writeXxx(xxx value) 这组方法将不同类型的数据写到buf里,同时将writerIndex往前移适当的距离
readXxx() 这组方法从buf里读出某种类型的数据,同时将readerIndex往前移适当的距离
skipBytes(int length) 将readerIndex往前移指定的距离
setXxx(int index, xxx value) 这组方法将不同类型的数据写到buf的指定位置
getXxx(int index) 这组方法从buf的指定位置读出一个某种类型的数据
readerIndex()/writerIndex() 访问readerIndex和writerIndex
readerIndex(int)/writerIndex(int) 设置readerIndex和writerIndex
readableBytes() 返回可读区域的字节数
writableBytes() 返回可写区域的字节数
clear() 清除buf(把readerIndex和writerIndex都设为0)
discardReadBytes() 扔掉已读数据
总结 今天只是对Netty的一些基本概念的了解,作为小白的我对netty的认识还非常浅薄,据了解netty在非常多的场景都大量的应用,而且效果想当不错,对性能有较大提升,尤其是是IM领域似乎用的还比较多,以后有机会一定要在IM中对Netty来个实战。
学无止尽,持续学习!