Java面试题(六):Netty
Netty基础
1、讲讲Netty的特点?
Netty是一个高性能、异步事件驱动的NIO框架,提供了对TCP、UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。
Netty作为业界最流行的nio框架之一,它的健壮性、功能、性能、可定制性、可扩展性都是首屈一指的。
Netty提供 asynchronous event-driven (异步事件驱动)的网络应用框架,是一个用以快速开发高性能、高可靠性协议的服务器和客户端。Netty的高性能、高可靠性得益于其优秀的哲学设计思想,netty采用Reactor模式设计,主要优势体现在:
- 异步非阻塞通信;
- 零拷贝;
- 内存池;
- 高效的Reactor线程模型;
- 无锁化的串行设计理念;
- 高效的并发编程;
- 高性能的序列化框架;
- 灵活的TCP参数配置能力。
2、BIO、NIO和AIO的区别?
参考
https://blog.csdn.net/dreamer23/article/details/80903978
- Java BIO : 同步阻塞
- Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
- Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持
I/O属于底层操作,需要操作系统支持,并发也需要操作系统的支持,所以性能方面不同操作系统差异会比较明显。另外NIO的非阻塞,需要一直轮询,也是一个比较耗资源的。所以出现AIO
3、NIO的组成是什么?
-
Channels 通道
Java NIO 的通道类似流,但又有些不同:
既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
通道可以异步地读写。
通道中的数据总是要先读到Buffer,或者总是要从Buffer中写入。 -
Buffers 缓冲区
高效的数据容器,除布尔类型,所有原始数据类型都有Buffer实现。 -
Selectors 选择器
Selector(选择器)是一个特殊的组件,能够检测一到多个 NIO 通道,并能够知晓通道是否为诸如读写事件做好准备。一个单独的线程可以管理多个 Channel,从而管理多个网络连接。
非阻塞 IO 的核心在于使用一个 Selector 来管理多个通道,可以是 SocketChannel,也可以是 ServerSocketChannel,将各个通道注册到 Selector 上,指定监听的事件。
之后可以只用一个线程来轮询这个 Selector,看看上面是否有通道是准备好的,当通道准备好可读或可写,然后才去开始真正的读写,这样速度就很快了。我们就完全没有必要给每个通道都起一个线程。
4、如何使用 Java NIO 搭建简单的客户端与服务端实现网络通讯?
服务端:
1、初始化TCP连接ServerSocketChannel,并监听端口
2、注册到selector(监听其ACCEPT事件)
3、selector阻塞,直到有监听的事件发生(selector.select())
4、通过迭代器依次访问select出来的Channel事件
5、判断Channel事件类型,isAcceptable 有新的连接,isReadable 有数据可以读取
客户端:
1、初始化socket连接,获取socket输入流(socket.getInputStream())输出流(socket.getOutputStream())
2、socket输出流向服务端写数据
3、socket输入流接收服务端发来的数据
5、如何使用 Netty 搭建简单的客户端与服务端实现网络通讯?
略,见前面写的netty博客
6、讲讲Netty 底层操作与 Java NIO 操作对应关系?
主要对java NIO netty对其进行了封装,方便了我们使用
8、Netty 的线程模型是什么?
Reactor多线程模型
9、Channel 与 Socket是什么关系,Channel 与 EventLoop是什么关系,Channel 与 ChannelPipeline是什么关系?
-
Channel 与 Socket关系:
Socket:网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket
Channel:是一个链接,它提供了如下的功能。
1:获取当前链接的状态
2:配置当前链接参数
3:进行read,write,connect,bind等通道支持的操作。
4:该Channel关联的ChannelPipeLine处理所有的IO事件和绑定在这个channel的请求 -
Channel 与 EventLoop关系:
一个Channel在它的生命周期内只注册于一个EventLoop;
一个EventLoop可能会被分配给一个或多个Channel -
Channel 与 ChannelPipeline关系:
image.png
一个Channel包含了一个ChannelPipeline,而ChannelPipeline中又维护了一个由ChannelHandlerCOntext组成的双向链表,这个链表的头是HeadContext,链表的尾是TailContext,并且么个ChannelHandlerContext中又关联着一个CHannelHandler;
10、EventLoop与EventLoopGroup 是什么关系?
NioEventLoopGroup是NioEventLoop的组合,用于管理NioEventLoop
EventLoop接口用于处理连接的生命周期中所发生的事件。
- 一个EventLoop在它的生命周期内只和一个Thread绑定
- 所有由EventLoop处理的I/O事件都将在它专有的Thread上被处理
- 一个Channel在它的生命周期内只注册于一个EventLoop
- 一个EventLoop可能会被分配给一个或者多个Channel
粘包与半包和分隔符相关问题
11、什么是粘包与半包问题?
粘包和半包,指的都不是一次是正常的 ByteBuf 缓存区接收
-
粘包,就是接收端读取的时候,多个发送过来的 ByteBuf “粘”在了一起。
换句话说,接收端读取一次的 ByteBuf ,读到了多个发送端的 ByteBuf ,是为粘包。 -
半包,就是接收端将一个发送端的ByteBuf “拆”开了,形成一个破碎的包,我们定义这种 ByteBuf 为半包。
换句话说,接收端读取一次的 ByteBuf ,读到了发送端的一个 ByteBuf的一部分,是为半包
12、粘包与半包为何会出现?
在操作系统层面来说,我们使用了 TCP 协议。
操作系统底层,是按照字节流的方式读入,到了 Netty 应用层面,需要二次拼装成 ByteBuf。
这就是粘包和半包的根源。
在Netty 层面,拼装成ByteBuf时,就是对底层缓冲的读取,这里就有问题了。
首先,上层应用层每次读取底层缓冲的数据容量是有限制的,当TCP底层缓冲数据包比较大时,将被分成多次读取,造成断包,在应用层来说,就是半包。
其次,如果上层应用层一次读到多个底层缓冲数据包,就是粘包
13、如何避免粘包与半包问题?
只能解决,不能避免
14、如何使用包定长 FixedLengthFrameDecoder 解决粘包与半包问题?原理是什么?
FixedLengthFrameDecoder:固定长度的拆包器
每个应用层数据包的都拆分成都是固定长度的大小,比如 1024字节
15、如何使用包分隔符 DelimiterBasedFrameDecoder 解决粘包与半包问题?原理是什么?
DelimiterBasedFrameDecoder:分隔符拆包器
每个应用层数据包,都通过自定义的分隔符,进行分割拆分。
16、Dubbo 在使用 Netty 作为网络通讯时候是如何避免粘包与半包问题?
略
17、Netty框架本身存在粘包半包问题?
18、什么时候需要考虑粘包与半包问题?
解码的时候需要考虑
Netty源码分析相关问题
19、服务端如何进行初始化?
服务端初始化的步骤
- 创建ServerBootstrap启动辅助类,通过Builder模式进行参数配置;
ServerBootstrap b= new ServerBootstrap(); - 创建并绑定Reactor线程池EventLoopGroup;
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
b.group(bossGroup,workerGroup) - 设置并绑定服务端Channel通道类型;
Channel初始化主要是指对通道类型进行设置,常见的通道类型主要有NioServerSocktChannel异步非阻塞服务端TCP通道,NioSocketChannel异步非阻塞客户端通道,OioServerSocketChannel同步阻塞服务端通道,OioSocketChannel同步阻塞客户端通道,NioDatagramChannel异步非阻塞UDP通道,OioDatagramChannel同步阻塞UDP通道等
-
绑定服务端通道数据处理器责任链Handler;
b.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new TimeServerHandler());
}
}); -
绑定并启动监听端口;
b.bind(port).sync();
20、何时接受客户端请求?
服务端启动后就可以接收
21、何时注册接受 Socket(Channel) 并注册到对应的 EventLoop 管理的 Selector ?
- 在channel通道创建和初始化完毕后,会通过group.register()方法将channel通道注册到EventLoop线程池中;从线程池中轮询获取一个线程EventLoop并与之绑定;而EventLoop线程池会绑定一个selector选择器
线程启动后,线程执行绑定的selector的select()方法监听注册的channel的状态,并执行定时任务
22、客户端如何进行客户端如何进行初始化?
客户端初始化的步骤
-
创建Bootstrap启动辅助类,通过Builder模式进行参数配置;
Bootstrap b = new Bootstrap(); -
创建并绑定Reactor线程池EventLoopGroup;
EventLoopGroup group = new NioEventLoopGroup();
b.group(group) -
设置并绑定服务端Channel通道类型;
b.channel(NioSocketChannel.class) -
绑定服务端通道数据处理器责任链Handler;
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new TimeClientHandler());
}
}); -
连接特定IP和端口;
b.connect(host,port).sync();
23、何时创建的 DefaultChannelPipeline ?
24、讲讲Netty的零拷贝?
所谓的 Zero-copy, 就是在操作数据时, 不需要将数据 buffer 从一个内存区域拷贝到另一个内存区域. 因为少了一次内存的拷贝, 因此 CPU 的效率就得到的提升
Netty 的 Zero-copy 体现在如下几个个方面:
Netty 提供了 CompositeByteBuf 类, 它可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝.
通过 wrap 操作, 我们可以将 byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象, 进而避免了拷贝操作.
ByteBuf 支持 slice 操作, 因此可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf, 避免了内存的拷贝.
通过 FileRegion 包装的FileChannel.tranferTo 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel, 避免了传统通过循环 write 方式导致的内存拷贝问题.
WebSocket 协议开发相关问题
25、讲讲如何实现 WebSocket 长连接?
26、讲讲WebSocket 帧结构的理解?
27、浏览器、服务器对 WebSocket 的支持情况
28、如何使用 WebSocket 接收和发送广本信息?
29、如何使用 WebSocket 接收和发送二进制信息?
八、网络
- http 响应码 301 和 302 代表的是什么?有什么区别?
- forward 和 redirect 的区别?
- 简述 tcp 和 udp的区别?
- tcp 为什么要三次握手,两次不行吗?为什么?
- 说一下 tcp 粘包是怎么产生的?
- OSI 的七层模型都有哪些?
- get 和 post 请求有哪些区别?
- 如何实现跨域?
- 说一下 JSONP 实现原理?