详解Netty内部网络实现原理
发布时间:2022-07-30 13:23:55 所属栏目:云计算 来源:互联网
导读:Netty 是一个在 Java 生态里应用非常广泛的的网络编程工具包,它在 2004 年诞生到现在依然是火的一塌糊涂,光在 github 上就有 30000 多个项目在用它。所以要想更好地掌握网络编程,我想就绕不开 Netty。所以今天我们就来分析分析 Netty 内部网络模块的工作
Netty 是一个在 Java 生态里应用非常广泛的的网络编程工具包,它在 2004 年诞生到现在依然是火的一塌糊涂,光在 github 上就有 30000 多个项目在用它。所以要想更好地掌握网络编程,我想就绕不开 Netty。所以今天我们就来分析分析 Netty 内部网络模块的工作原理。 友情提示,本文算上代码将近有两三万字,比较长,如果时间紧迫中间部分可以跳着看。第一节和最后的第六节建议必读。当然直接拖到尾部收藏点赞点转发,也是 ok 的,哈哈! 另外,今天又给大家申请到了赞助,在文末给大家申请了5本冰河的新书《深入理解高并发编程》,抽奖送给大家。 一、Netty 用法 我们首先找一个 Netty 的例子,本篇文章整体都是围绕这个例子来展开叙述的。我们下载 Netty 的源码,并在 examples 中找到 echo 这个 demo。同时,为了防止代码更新导致对本文叙述的影响,我们切到 4.1 分支上来。 复制 # git checkout https://github.com/netty/netty.git # git checkout -b 4.1 # cd example/src/main/java/io/netty/example/echo 1. 2. 3. 在这个 demo 的 EchoServer 中,展示了使用 Netty 写 Server 的经典用法。(飞哥在文章中会在不影响核心逻辑的表达上,对原始代码尽心适当的精简,比如下面代码中的 try 就被我丢了) 复制 public final class EchoServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); final EchoServerHandler serverHandler = new EchoServerHandler(); ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); if (sslCtx != null) { p.addLast(sslCtx.newHandler(ch.alloc())); } p.addLast(serverHandler); } }); // Start the server. ChannelFuture f = b.bind(PORT).sync(); ...... } } 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 如果你是一个 Java 新手,或者干脆像飞哥一样没用 Netty 写过服务,相信上述代码基本是看不懂的。究其根本原因是相比 C/C++ ,Java 的封装程度比较高。Java 语言本身的 JVM 中 NIO 对网络的封装就已经屏蔽了很多底层的概念了,再加上 Netty 又封装了一层,所以 Java 开发者常用的一些术语和概念和其它语言出入很大。 比如上面代码中的 Channel、NioEventLoopGroup 等都是其它语言中所没见过的。不过你也不用感到害怕,因为这其中的每一个概念都是 socket、进程等底层概念穿了一身不同的衣服而已。接下来我们分别细了解一下这些概念。 1.1 NioEventLoopGroup 如果你没接触过 Netty,可以简单把 NioEventLoopGroup 理解为一个线程池就可以。每一个 NioEventLoopGroup 内部包含一个或者多个 NioEventLoop。 图片 其中 NioEventLoop 是对线程、epoll 等概念进行了一个集中的封装。 首先,EventLoop 本身就是一个线程。为什么这么说,我们通过看 NioEventLoop 的继承关系就能看出来。NioEventLoop 继承于 SingleThreadEventLoop,而 SingleThreadEventLoop 又继承于 SingleThreadEventExecutor。SingleThreadEventExecutor 实现了在 Netty 中对本地线程的抽象。 复制 public abstract class SingleThreadEventExecutor extends ... { private volatile Thread thread; private final Queue<Runnable> taskQueue; } 1. 2. 3. 4. 在 SingleThreadEventExecutor 中不但封装了线程对象 Thread,而且还配置了一个任务队列 taskQueue,用于其它线程向它来放置待处理的任务。 1.2 selector 另外 NioEventLoopEventLoop 以 selector 的名义封装了 epoll(在 Linux 操作系统下)。 图片 在 NioEventLoop 对象内部,会有 selector 成员定义。这其实就是封装的 epoll 而来的。我们来看具体的封装过程。以及 selectedKeys,这是从 selector 上发现的待处理的事件列表。 复制 public final class NioEventLoop extends SingleThreadEventLoop{ // selector private Selector selector; private Selector unwrappedSelector; // selector 上发现的各种待处理事件 private SelectedSelectionKeySet selectedKeys; } 1. 2. 3. 4. 5. 6. 7. 8. NioEventLoopGroup 在构造的时候,会调用 SelectorProvider#provider 来生成 provider,在默认情况下会调用 sun.nio.ch.DefaultSelectorProvider.create 来创建。 复制 //file:java/nio/channels/spi/SelectorProvider.java public abstract class SelectorProvider { public static SelectorProvider provider() { // 1. java.nio.channels.spi.SelectorProvider 属性指定实现类 // 2. SPI 指定实现类 ...... // 3. 默认实现,Windows 和 Linux 下不同 provider = sun.nio.ch.DefaultSelectorProvider.create(); return provider; } } 在 Linux 下,默认创建的 provider 使用的就是 epoll。 复制 //file:sun/nio/ch/DefaultSelectorProvider.java public class DefaultSelectorProvider { public static SelectorProvider create() { String osname = AccessController .doPrivileged(new GetPropertyAction("os.name")); if (osname.equals("Linux")) return createProvider("sun.nio.ch.EPollSelectorProvider"); } } (编辑:开发网_开封站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
站长推荐
热点阅读