写点什么

Netty 入门教程——认识 Netty,今年最新整理的《高频 Java 面试题集合》

作者:Java高工P7
  • 2021 年 11 月 10 日
  • 本文字数:2640 字

    阅读完需:约 9 分钟

如第一部分所述,netty 是一款收到大公司青睐的框架,在我看来,netty 能够受到青睐的原因有三:


  1. 并发高

  2. 传输快

  3. 封装好

Netty 为什么并发高

Netty 是一款基于 NIO(Nonblocking I/O,非阻塞 IO)开发的网络通信框架,对比于 BIO(Blocking I/O,阻塞 IO),他的并发性能得到了很大提高,两张图让你了解 BIO 和 NIO 的区别:



阻塞 IO 的通信方式



非阻塞 IO 的通信方式


从这两图可以看出,NIO 的单线程能处理连接的数量比 BIO 要高出很多,而为什么单线程能处理更多的连接呢?原因就是图二中出现的 Selector。


当一个连接建立之后,他有两个步骤要做,第一步是接收完客户端发过来的全部数据,第二步是服务端处理完请求业务之后返回 response 给客户端。NIO 和 BIO 的区别主要是在第一步。


在 BIO 中,等待客户端发数据这个过程是阻塞的,这样就造成了一个线程只能处理一个请求的情况,而机器能支持的最大线程数是有限的,这就是为什么 BIO 不能支持高并发的原因。


而 NIO 中,当一个 Socket 建立好之后,Thread 并不会阻塞去接受这个 Socket,而是将这个请求交给 Selector,Selector 会不断的去遍历所有的 Socket,一旦有一个 Socket 建立完成,他会通知 Thread,然后 Thread 处理完数据再返回给客户端——这个过程是不阻塞的,这样就能让一个 Thread 处理更多的请求了。


下面两张图是基于 BIO 的处理流程和 netty 的处理流程,辅助你理解两种方式的差别:



BIO 的处理流程



NIO 的处理流程


除了 BIO 和 NIO 之外,还有一些其他的 IO 模型,下面这张图就表示了五种 IO 模型的处理流程:


![Netty 入门教程——认识 Netty](https://imgconvert.csdnimg.cn/aHR0cDovL3AzLn


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


BzdGF0cC5jb20vbGFyZ2UvcGdjLWltYWdlL2FlNzIzNDI2YjY4YzQ4ZjdhYjI5ZmQ3MmNkOTY3ODM4?x-oss-process=image/format,png)


五种常见的 IO 模型


  • BIO,同步阻塞 IO,阻塞整个步骤,如果连接少,他的延迟是最低的,因为一个线程只处理一个连接,适用于少连接且延迟低的场景,比如说数据库连接。

  • NIO,同步非阻塞 IO,阻塞业务处理但不阻塞数据接收,适用于高并发且处理简单的场景,比如聊天软件。

  • 多路复用 IO,他的两个步骤处理是分开的,也就是说,一个连接可能他的数据接收是线程 a 完成的,数据处理是线程 b 完成的,他比 BIO 能处理更多请求。

  • 信号驱动 IO,这种 IO 模型主要用在嵌入式开发,不参与讨论。

  • 异步 IO,他的数据请求和数据处理都是异步的,数据请求一次返回一次,适用于长连接的业务场景。


以上摘自 Linux IO 模式及 select、poll、epoll 详解

Netty 为什么传输快

Netty 的传输快其实也是依赖了 NIO 的一个特性——零拷贝。我们知道,Java 的内存有堆内存、栈内存和字符串常量池等等,其中堆内存是占用内存空间最大的一块,也是 Java 对象存放的地方,一般我们的数据如果需要从 IO 读取到堆内存,中间需要经过 Socket 缓冲区,也就是说一个数据会被拷贝两次才能到达他的的终点,如果数据量大,就会造成不必要的资源浪费。 Netty 针对这种情况,使用了 NIO 中的另一大特性——零拷贝,当他需要接收数据的时候,他会在堆内存之外开辟一块内存,数据就直接从 IO 读到了那块内存中去,在 netty 里面通过 ByteBuf 可以直接对这些数据进行直接操作,从而加快了传输速度。 下两图就介绍了两种拷贝方式的区别,摘自 Linux 中的零拷贝技术,第 1 部分



传统数据拷贝



零拷贝


上文介绍的 ByteBuf 是 Netty 的一个重要概念,他是 netty 数据处理的容器,也是 Netty 封装好的一个重要体现,将在下一部分做详细介绍。

为什么说 Netty 封装好?

要说 Netty 为什么封装好,这种用文字是说不清的,直接上代码:


  • 阻塞 I/O


public class PlainOioServer {


public void serve(int port) throws IOException {


final ServerSocket socket = new ServerSocket(port); //1


try {


for (;;) {


final Socket clientSocket = socket.accept(); //2


System.out.println("Accepted connection from " + clientSocket);


new Thread(new Runnable() { //3


@Override


public void run() {


OutputStream out;


try {


out = clientSocket.getOutputStream();


out.write("Hi!\r\n".getBytes(Charset.forName("UTF-8"))); //4


out.flush();


clientSocket.close(); //5


} catch (IOException e) {


e.printStackTrace();


try {


clientSocket.close();


} catch (IOException ex) {


// ignore on close


}


}


}


}).start(); //6


}


} catch (IOException e) {


e.printStackTrace();


}


}


}


  • 非阻塞 IO


public class PlainNioServer {


public void serve(int port) throws IOException {


ServerSocketChannel serverChannel = ServerSocketChannel.open();


serverChannel.configureBlocking(false);


ServerSocket ss = serverChannel.socket();


InetSocketAddress address = new InetSocketAddress(port);


ss.bind(address); //1


Selector selector = Selector.open(); //2


serverChannel.register(selector, SelectionKey.OP_ACCEPT); //3


final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes());


for (;;) {


try {


selector.select(); //4


} catch (IOException ex) {


ex.printStackTrace();


// handle exception


break;


}


Set<SelectionKey> readyKeys = selector.selectedKeys(); //5


Iterator<SelectionKey> iterator = readyKeys.iterator();


while (iterator.hasNext()) {


SelectionKey key = iterator.next();


iterator.remove();


try {


if (key.isAcceptable()) { //6


ServerSocketChannel server =


(ServerSocketChannel)key.channel();


SocketChannel client = server.accept();


client.configureBlocking(false);


client.register(selector, SelectionKey.OP_WRITE |


SelectionKey.OP_READ, msg.duplicate()); //7


System.out.println(


"Accepted connection from " + client);


}


if (key.isWritable()) { //8


SocketChannel client =


(SocketChannel)key.channel();


ByteBuffer buffer =


(ByteBuffer)key.attachment();


while (buffer.hasRemaining()) {


if (client.write(buffer) == 0) { //9


break;


}


}


client.close(); //10


}


} catch (IOException ex) {


key.cancel();


try {


key.channel().close();


} catch (IOException cex) {


// 在关闭时忽略


}


}


}


}


}


}


  • Netty

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
Netty入门教程——认识Netty,今年最新整理的《高频Java面试题集合》