前记
昨天面试一家公司,所有的题都答的稀碎,也经过一些深思熟虑的动作,决定放弃之前的一家offer,“我的目标是什么?我怎么才能更加接近他?而不是再一次稀里糊涂的入职”
Neety是什么
三点概括
- 是一个基于NIO的 client-server 框架,可以快速简单的开发网络应用程序
- 极大简化并又花了TCP和UDP套接字服务器等网络编程,并且性能以及安全性等方面都要更好
- 支持多种协议如 FTP SMTP HTTP以及各种二进制和基于文本的传统协议
很多项目比如 Dubbo RocketMQ Elasticsearch 都用到了 Netty
他的好处我列一点
- 支出多种传输类型 阻塞和非阻塞
- 简单而强大的线程模型
- 自带编解码器解决TCP粘包、拆包问题
- 自带各种协议栈
- 安全性,有完整的SSL/TLS
- 社区活跃
- 大型开源项目都用到了
应用场景
主要用来做网络通信
- RPC
- http服务器
- 即时通讯系统
- 实现消息推送系统
Netty核心组件有哪些,分别有什么用
-
Channel
是netty对网络操作抽象类,包含基本的I/O操作,比如bind() connect() read() write()
常用的channel接口实现类是 NioServerSocketChannel(服务端) NioSocketChaneel(客户端) 大大降低了直接使用Socket的复杂性
-
EventLoop
“定义了Netty的核心抽象,用于处理链接的生命周期中所发生的事情”
主要作用是 负责监听网络事件并调用事件处理器进行相关IO操作的处理
Channel 为netty网络操作(读写等)的抽象类
EventLoop 负责处理注册到其上的Channel处理IO操作
-
ChannelFuture
Netty是异步非阻塞的,所有IO操作都是异步的
通过ChannelFuture接口的addListener()方法注册一个ChannelFutureListener,当操作成果或失败时,监听就会自动出发返回结果
也可以通过ChannelFuture接口的sync()方法让异步操作变成同步
-
ChannelHandler 和 ChannelPipeline
ChannelHandler 是消息的具体处理器,负责处理写操作,客户端连接等事件
ChannelPipeline 为 ChannelHandler 的链,通过 addList() 方法添加一个或多个 ChannelHandler
EventLoopGroup 了解吗 和 EventLoop 啥关系
EventLoopGroup 中包含多个 EventLoop
EventLoop 和 Thread 一对一 , 从而保证了线程安全
EventLoopGroup 分为俩种
-
Boss EventLoopGroup
用于接收连接
-
Worker EventLoopGroup
用处具体的处理
当客户端通过 connect 方法连接到服务端时, bossGroup 处理客户端请求,然后会将连接提交给 workerGroup 来处理其IO相关操作
Boostrap 和 ServerBootstrap 了解吗
Boostrap 是客户端的启动引导类/辅助类
- 指定线程模型(NioEventLoopGroup)
- 尝试建立连接(connet())
- 优雅的关闭相关线程组资源
ServerBootstrap 是服务端启动引导类/服务类
- 指定线程模型(boosGroup workerGroup)
- 绑定端口(bind())
- 等待连接关闭
- 优雅的关闭相关线程组资源
源码了解吗? NioEventLoopGroup 默认的构造函数会起多少线程
cpu核心数*2
Netty线程模型了解吗
大部分网络框架都是基于Reactor模式设计开发的
- Reactor模式基于事件驱动,采用多路复用将事件分发给相应的handler处理,适合海量IO的场景
在Netty主要靠NioEventLoopGroup线程池来实现具体的线程模型的。在实现服务端的时候,会初始化俩个线程组
- bossGroup 接受连接
- workerGroup 负责具体的处理,交由对应的Handler处理
Netty的线程模型
-
单线程模型
一个线程需要处理 accept read decode process encode send 事件,对高并发场景不适用
-
多线程模型
一个Acceptor线程只负责监听客户端的连接。一个NIO线程池负责具体处理 accept read decode process encode send事件。遇到并发连接大会成为性能瓶颈
-
主从多线程模型
从一个主线程NIO线程池中选择一个线程作为Acceptor线程,绑定监听端口,接受客户端连接,其他线程负责后续接入认证等工作。连接建立完成后,Sub NIO 线程池负责具体处理IO读写
Neety服务端和客户端的启动过程了解吗
服务端
- 创建俩个NioEventLoopGroup对象实例
- boosGroup 用于处理客户端TCP连接。一般线程数为1
- workerGroup 负责每一条连接的具体读写数据的处理逻辑,负责IO读写操作,交由对应的Handle处理。一般线程数为cpu*2
- 创建 ServerBootstrap 引导类
- 通过 .group() 方法给引导类 ServerBootstrap 配置俩大线程组,确定线程模型
- 通过 .channel()方法给引导类 ServerBootstrap 指定了IO模型为 NIO
- 通过 .childHandler() 给引导类创建一个 ChannelInitializer 然后指定了服务端消息的业务处理逻辑 HelloServerHandler 对象
- 调用 ServerBootstrap 类的 bind()方法绑定端口
客户端
- 创建一个NioEventLoopGroup 对象实例
- 创建客户端引导类 Bootstrap
- 通过 .group() 方法给引导类 Bootstrap 配置一个线程组
- 通过 channel() 方法给引导类 Bootstrap 指定了IO模型为NIO
- 通过 .childHandle() 给引导类创建一个 ChannelInitializer 然后指定了客户端的业务处理逻辑 HelloClientHandler 对象
- 调用 Bootstrap 的 connect() 方法进行连接,需要俩个参数 IP Port。 connect方法是Future类型的返回,需要通过 addListener 方法可以监听是否成功
什么是TCP粘包、拆包,有什么解决办法
基于TCP发送数据时,出现了多个字符“粘”在一次或者被“拆”开的问题
解决方法:
-
使用Netty自带的解码器
- LineBashdFrameDecoder 发送端发送数据包的时候,每个数据包之间以换行符作为分割,工作原理就是一次遍历 ByteBuf 中的可读字节,判断是否有换行符
- DelimiterBasedFrameDecoder 可以自定义分隔符解码器
- FixedLengthFramDecoder 固定长度解码器
-
自定义序列化编解码器
通常情况下使用 Protostuff Hessian2 json 序列方式
Netty长连接,心跳机制了解吗
TCP进行读写之前,服务端和客户端需要建立一个连接,也就是三次握手,这个过程比较消耗资源。短连接都是读写完成后就关闭连接。长连接不会主动关闭,后续读写操作会继续使用这个链接
TCP长连接过程中,心跳机制是为了防止对方因为网络异常等原因,无法发现对方已经掉线。心跳机制的工作原理:在client和server之间一定时间没有数据交互时,双方中一方就会发送一个特殊的数据包给对方,收到后会回复一个数据报文,就知道对方仍然在线了
Netty零拷贝了解吗
在Netty层面,零拷贝主要体现在对于数据操作的优化
- 使用Netty提供的 CompositeByteBuffer类,可以将多个ByteBuffer合并为一个逻辑上的ByteBuffer 避免了各个ByteBuffer之间的拷贝
- ByteBuffer 支持 slice 操作,可以将ByteBuffer 分解为多个共享同一个存储区域的 ByteBuf 避免内存的拷贝
- 通过 FileRegion 包装的 FileChannel.tranferTo 实现文件传输,可以直接将文件缓冲区的数据发送到目标 Channel 避免了传统通过循环 write方式导致的内存拷贝问题