文档章节

Netty 源码分析(一):引言和 Java NIO 介绍

编走编想
 编走编想
发布于 2015/10/01 18:23
字数 2117
阅读 499
收藏 25

为什么要介绍 Netty

如今优秀的开源项目非常多,仅在 Java 服务器端开发领域,优秀的开源项目就不胜枚举。比如从十年前就开始流行到现在依旧十分活跃的 Spring Framework,如今已经发展为一个覆盖服务器端、大数据等多个领域的平台级开源项目。还有早年间的 MVC 框架、ORM 框架,到现在涉及各个服务器开发领域的开源技术。

但这次我要介绍的是 Netty 这个网络 IO 框架,而非 Spring 这样的流行项目。原因在于,Netty 这样的框架所实现的功能相比 Spring Framework 来说来说更为基础。因为对于服务器端开发来说,Spring Framework 核心的 IoC 和 AOP 技术其实并不是必须的。当然没有这两个技术,开发复杂的项目会很困难,但这两者只是充分而非必要的条件。但 Netty 这样的技术其实对于服务器端开发来说是必须的。

从产品的角度讲,没有像 Netty 这样的网络 IO 框架,就不会有优秀的 Web 服务器、应用服务器等等的平台,而没有这些平台,仅靠 IoC 和 AOP 等技术是不可能产生优秀的软件产品。一个服务器端软件,即便没有直接使用 Netty,那往往也是间接地使用了 Netty 这样的框架。所以 Netty 这样的框架对于一个优秀的服务器端项目或产品来说十分重要。

同时,因为 Netty 不仅被广泛地直接使用,还被很多优秀的技术,例如 Thrift、Storm、Spark 等采用,作为其基础的 IO 层的实现技术。所以,深入了解 Netty 还可以帮助开发人员更好地使用这些技术,在遇到一些技术问题时也可以从更深地角度去分析。

从程序员学习的角度讲,像 Netty 这样的网络 IO 框架,它们大量使用了 IO 和并发这两个技术。而这两个技术,对于优秀的服务器端开发人员来说是必备的。通过深入了解 Netty 的实现原理,能让程序员从实践的角度去学习优秀的 IO 和并发设计。除了 IO 和并发这两个对于服务器端开发非常重要的技术,了解 Netty 的实现原理还可以让程序员去学习如何设计实现一个复杂的框架,学习到设计模式、数据结构等的应用技巧。所以,深入了解 Netty 对于提高非常有帮助。

从工作的角度讲,国内外的很多公司也同样广泛使用 Netty,比如Twitter、Facebook、华为、阿里巴巴等等。所以在日常工作中也很容易接触到 Netty 的使用。

此外,Netty 的文档相对 Spring 这样的技术来说还是偏匮乏。同时,Netty 3/4/5 这几个版本的变化还是非常大的,尤其是从 3 到 4。这给使用者带来了很多的问题,而深入了解 Netty 会帮助使用者解决这些问题。

所以,总结一下,深入了解 Netty 的好处有:

  1. 提高程序员在网络 IO 和并发,以及设计模式、数据结构等领域的内功
  2. 学习如何设计复杂的框架
  3. 帮助解决使用 Netty 以及将 Netty 作为基础的技术时遇到的问题

目录

  • Java NIO 介绍
  • Netty 主要组件和类图
  • 源码分析之 Server Side Channel Init and Register
  • 源码分析之 Server Side Channel Port Bind
  • 源码分析之 Server Side Accept Client Connect
  • 源码分析之 Read Message
  • 源码分析之 Write Message
  • Netty 线程模型

Java NIO 介绍

Java NIO 对于 Netty 来说是基础技术(Java NIO 是基于 Linux epoll 等技术),所以下面将介绍一下 Java NIO 方面的基础知识。

对于并发技术,因为它相比较 Java NIO 来说在工作中被直接使用到的机会要多得多,所以这里就不做介绍了。但是后续会介绍 Netty 中的并发设计。

示例

Selector selector = null;
ServerSocketChannel server = null;
try {
    selector = Selector.open(); // 打开 Selector
    server = ServerSocketChannel.open(); // 打开 ServerSocketChannel
    server.socket().bind(new InetSocketAddress(port)); // 绑定端口
    server.configureBlocking(false); // 设置为非阻塞模式
    server.register(selector, SelectionKey.OP_ACCEPT); // 将 ServerSocketChannel 注册到 Selector 上
    while (true) {
        selector.select();
        for (Iterator<SelectionKey> i = selector.selectedKeys().iterator(); i.hasNext();) {
            SelectionKey key = i.next();
            i.remove();
            if (key.isConnectable()) {
                ((SocketChannel)key.channel()).finishConnect();
            }
            if (key.isAcceptable()) {
                // accept connection
                SocketChannel client = server.accept(); // 接受 TCP 连接
                client.configureBlocking(false);
                client.socket().setTcpNoDelay(true);
                client.register(selector, SelectionKey.OP_READ); // 将 SocketChannel 注册到 Selector 上
            }
            if (key.isReadable()) {
                // ...read messages...
            }
        }
    }          
} catch (Throwable e) {
    throw new RuntimeException("Server failure: "+e.getMessage());
} finally {
    try {
        selector.close();
        server.socket().close();
        server.close();
        stopped();
    } catch (Exception e) {
        // do nothing - server failed
    }
}

上面这段代码介绍了 Java NIO 的基本使用方式。比如,ServerSocketChannelSocketChannel 要注册在 Selector 上,并且这两个 SelectableChannel 要通过 configureBlocking(boolean block) 方法将其设置为非阻塞模式;通过不断轮询 Selector 的方式,通过 Selector.selectedKeys() 获得例如客户端连接、读写消息等事件,并做相应处理。因为在一个 Selector 上可以注册多个 SelectableChannel,所以实现了一个线程处理多个连接的目的。

上面就是 Java NIO 的大致的使用方法。

技术基础

Java 这样编程语言级的 IO 如同线程一样,最终还是基于操作系统的实现支持。在 Linux 中,是靠 select、poll 和 epoll 等技术支持的。

select(poll 类似)

在非阻塞 IO 出现之前,我们是用阻塞的方式使用 IO。当一个流(文件、套接字等)无法读写的时候,操作便被阻塞,线程被挂起。于是,对于一个流的操作,必须独占一个线程。虽然可以用多进程或多线程的方式去并发处理,但是这样所带来的大量的资源使用、线程上下文的切换,都使得这种方式十分低效。所以便出现了非阻塞 IO。

最先出现的非阻塞 IO 技术是 select 和 poll。它们的原理简单来说不断地轮询多个流,看是否有流可以被操作,从而实现了非阻塞的 IO 操作。

但这两种方式也有明显的缺点。其缺点在于,select 和 poll 对流的轮询的方式很“傻”。它俩在轮询时并不会区分一个流上是否真的有数据,而是一股脑地轮询所有的流。因此这两者的性能会随着流的增加而线性下降。

epoll

epoll 的意思是 event poll。epoll 相较 select 和 poll 的改进在于不像前者去轮询所有的流,而是只去轮询有实际事件发生的流。这一点的实现是基于硬件中断实现的。

组成介绍

selector-channel

Channel

Channel 代表了 IO 操作的通道。Channel 通过后面提到的 Buffer 进行数据的读写。Channel 有一类我们后面会经常提到的 SelectableChannel。配合 Selector,能够实现非阻塞 IO。后续将被经常提到的 ServerSocketChannelSocketChannel 都是 SelectableChannel 的子类。

Selector

选择器,也可被称为多路复用器,是实现非阻塞 IO 的关键。通过调用 Selector.open(),便可得到一个当前操作系统下的默认 Selector 实现。通过 SelectorProvider 修改这一行为,自定义实现。

SelectableChannel 可以通过 register(Selector, int) 方法将自己注册在 Selector 上,并提供其所关注的事件类型。

通过地调用 Selectorselect()(阻塞)或 selectNow()(非阻塞)方法,Selector 会将从上次 select() 方法调用之后的所有就绪状态通道上的 SelectionKey 放入一个集合中。同时,select() 方法所返回的 int 值代表了有多少 Channel 自上次之后便为就绪状态。

在调用 select() 之后,通过调用 SelectorselectedKeys() 得到就绪状态通道的 SelectionKey。通过遍历这一集合,再通过 SelectionKey 便可得到所有就绪状态的 SelectableChannel,进一步便可以做相应的操作。

Buffer

Java NIO 以 Buffer 最为数据传输的载体。通过使用 Buffer,Java NIO 提高了数据传输的效率。而 Netty 的很多改进也是围绕 Buffer 进行的,比如 Buffer 的池化。

后面会提到的 SocketChannel,其使用了 Buffer 的一个子类 ByteBuffer 来进行数据的读写。

接下来

接下来会介绍 Netty 中一些主要的类,以及它们的作用以及关系。

© 著作权归作者所有

共有 人打赏支持
编走编想
粉丝 148
博文 126
码字总数 107958
作品 0
海淀
程序员
私信 提问
加载中

评论(2)

编走编想
编走编想

引用来自“GwMyna”的评论

坐等二
二发出来了,不过还在补充内容
GwMyna
GwMyna
坐等二
Qzone 微信 Java高级——dubbo源码分析之远程通信 netty

Java高级——dubbo源码分析之远程通信 netty dubbo 底层通信选择了 netty 这个 nio 框架做为默认的网络通信框架并且通过自定义协议进行通信。dubbo 支持以下网络通信框架: Netty(默认) Min...

Java架构师那些事
08/29
0
0
Netty5源码分析--4.总结

JAVA NIO 复习 请先参考我之前的博文JAVA学习笔记--3.Network IO的 NIO(NonBlocking IO) SOCKET 章节。这里主要讲下JAVA NIO其中几个比较被忽略的细节,不求全,欢迎补充。 API Select 当调...

geecoodeer
2014/01/17
0
18
Netty浅析 - 2. 实现

前言 本篇文章主要分析Netty的系统结构以及其如何实现其对外宣称的特色,如果还未了解Netty的基础知识,最好先阅读本系列的第一篇文章Netty浅析 - 1. 基础 Netty总体结构 这张图摘自Netty官网...

简xiaoyao
12/02
0
0
Java网络编程框架

自从JDK1.4中有了NIO以后,这个方面越来越活跃,也为java赢得更多开发者的支持。做java网络编程需要掌握一些基本的知识和技能: 套接字编程、阻塞/非阻塞通信、创建HTTP服务器与客户程序、数...

长平狐
2012/08/29
2.3K
0
Java NIO原理 图文分析及代码实现

Java NIO原理图文分析及代码实现 前言: 最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术...

囚兔
2015/04/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

IC-CAD Methodology企业实战之openlava

在云计算解决安全问题并成为IC界主流运算平台之前,私有的服务器集群系统仍然是各大IC公司的计算资源平台首选。 现在主流的服务器集群管理系统包括lsf,openlava,SkyForm,三者都属于lsf一系...

李艳青1987
18分钟前
0
0
http response stream 字节流 接收与解码

在接收图片、音频、视频的时候,需要用到二进制流。 浏览器会发给客户端 字节Byte流,一串串的发过来_int8格式 -128~127(十进制),也就是8bit(位)。 客户端接收的时候,对接收到的字节收集,...

大灰狼wow
18分钟前
0
0
配置Tomcat监听80端口...

12月13日任务 16.4 配置Tomcat监听80端口 16.5/16.6/16.7 配置Tomcat虚拟主机 16.8 Tomcat日志 1.配置Tomcat监听80端口 示例一:自定义监听端口 vim /usr/local/tomcat/conf/server.xml 编辑...

hhpuppy
18分钟前
0
0
在ubuntu中配置java环境

先在官网下载一个jdk 进入root权限,避免之后出现创建文件失败或者修改文本失败的问题 sudo i 创建一个文件夹来放置jdk解压后的文件 mkdir 文件夹mv jdk1.9(你下载的jdk文件) 你创建 的文...

无极之岚
19分钟前
0
0
程序中设置MySQL的默认值

import com.alibaba.fastjson.JSON;import java.beans.PropertyDescriptor;import java.lang.annotation.*;import java.lang.reflect.Field;import java.lang.reflect.Method;impo......

laolin23
42分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部