本文将简单介绍 Java 7 SDK 里引入的 Java 套接字直接协议(Sockets Direct Protocol,SDP),这项新技术是个非常激动人心的突破。如果要对 InfiniBand 的远程直接内存存取(Remote Direct Memory Access,RDMA)进行native 访问,SDP 就能让超高性能计算(Ultra High Performance Computing,UHPC)社区在这种不常见的场景下使用Java 通用的特性和优势。RDMA 为低延迟应用提供了一种协议,可以直接访问其他计算机的内存,而不需要操作系统的参与。UHPC 社区对低延迟和高吞吐量的要求最为严格,而且不容妥协,UHPC 自然就需要使用最好的RDMA。随着Java 7 引入SDP,UHPC 社区就可以利用Java 平台编写能直接利用InfiniBand RDMA 功能的应用代码了。
在深入了解新的Java SDP 之前,让我们简要回顾一下Java 网络编程和套接字API 的历史。
Sun Microsystems 于 1995 年推出 Java,并宣扬 Java 能“一次编写,到处运行”,这句口号现在已然广为人知。我们都知道这背后的思想其实很简单:用 C++ 代码编写的应用要在所有环境下构建 / 部署是极其困难的,追求接近“到处运行”的可移植性就更难了,但现在你可以用一种叫 Java 的语言编写应用代码,应用可以在虚拟机(并不是底层的操作系统执行环境)上构建 / 部署。这极大地减少了 Java 应用程序员对可移植性的关注,而是把可移植性完全交给 Java 虚拟机(JVM)去处理。JVM 承诺:如果你的 Java 代码可以在针对某个特定底层操作系统的 JVM 上构建 / 部署,只要其他操作系统有可用、兼容的 JVM,平台就能确保完全相同的代码可以在上面运行。而不需要额外的编译和预处理器宏指令。(有人记得 C++ 里的#ifdef 么?JVM 可以让应用程序员摆脱这种痛苦。)
这种思想非常有用,得到了应用编程社区的广泛认可。正如我们所知,Java 非常迅速地流行起来——以前所未有的速度被全面接受,计算机历史上的很多编程语言平台都没有这么迅速地普及过。
Sun 一开始提供的 JVM 只能运行在三个操作系统上:1、Solaris,2、Linux,3、Windows。微软在 1993 年发布的 Windows 就带有 WinSOCK 协议栈,所以 Windows 可以做 TCP/IP 网络编程,而且完全支持 API。各种 *nix 系统从二十世纪七十年代起就一直在支持 TCP/IP。微软引入 WinSOCK 对 Java 的成形来说绝对必要。没有 WinSOCK,就不能发布支持 java.net.* 和 java.io.* 这些 APIs 的 Windows VM。没有 WinSOCK,Java 构建的 VM 在垄断世界桌面的操作系统上就不具备完整的网络功能了。随着 Windows 完全支持 TCP/IP,运行 Java 的桌面数量可能增加了上千倍。
事情发生了变化
当然,Java 仍然是“一次编写,到处运行”。可移植性仍然是首要任务。但现在用 Java 7 和 SDP,JVM 能做更多的事情。可移植性不再是唯一的重点;对 JVM 来说,满足超高性能用例的需求也是很重要的任务。借助 SDP,JVM 不需要修改网络、套接字 API,就可以直接利用 InfiniBand 天生的能力。InfiniBand 要比以太网快很多。UHPC 社区往往会选择 InfiniBand 作为物理网络层的提供者。
后面我们会介绍 InfiniBand 是什么,以及 Java 7 VM 怎么让应用利用 InfiniBand 原生的功能。
有件有趣的事情要注意(特别是从历史的角度来看):Java 决定在两个操作系统上提供 SDP 支持,而微软 Windows 并不在其中。Java 7 SDP 支持的两个操作系统是 Solaris 和 Linux。Solaris 从版本 10 开始就对 SDP 提供了规范的支持。只要你有物理 InfiniBand 网卡,Java 7 SDP 就可以立即工作。Linux 则通过 Open Fabrics Enterprise Distribution(OFED)包支持 SDP。要确认 Linux 版本有没有配置 OFED 设备驱动器,只需要在安装物理 InfiniBand 网卡适配器之后简单键入下面的命令:
<b>egrep "^[ \t]+ib" /proc/net/dev</b>
如果命令有输出,就表明你可以在这个操作系统上使用 Java 7 SDP。
所有的 java.net.* 和 java.io.* 应用代码仍然能在微软 Windows 上用 Java 7 VM 运行……但它用不了 SDP,运行时的物理层提供者仍然是以太网。即使你在通过 WinSOCK Direct 子系统提供 InfiniBand 支持的 Windows Server 版本上运行,也用不了 SDP。所有的内容仍然能在 Windows 上运行,但速度会比在非 Windows(即 *nix)上运行得慢。
事情确实发生了变化
现在让我们谈谈 Java 里面连接操作系统网络协议栈的 API。首先,下表显示了网络层标准的开放系统互连(OSI)模型。
#
层
协议
Java SDK 的核心 APIs
7.
应用层
HTTP、FTP、SSL 等
java.net.HttpURLConnection、javax.servlet.HttpServlet
6.
表示层
\# 在 Java 里,OSI 应用层和表示层没有真正区别
5.
会话层
NetBios、RCP
# Java SDK 核心对 OSI 会话层没有支持
4.
传输层
TCP、UDP
java.net.Socket、java.net.ServerSocket、java.net.Datagram
3.
网络层
IP
Java.net.InetAddress
2.
数据链路层
PPP
# Java SDK 核心对 OSI 数据链路层没有支持
1.
物理层
以太网、InfiniBand
# Java SDK 核心对 OSI 物理层没有支持
不过……
现在有了Java 7 SDP(VM 连接 InfiniBand 和 java.net.*、java.io.* 核心 APIs 的桥梁)
从 OSI 网络层的视角看,Java 7 SDP 能让 Java 应用代码“更接近金属”(物理层)。Java SDP 提供了一种直接(SDP 里的 D)的方式,借助 VM 就可以连接应用代码和 native、物理的 InfiniBand。Java 7 对 SDP 的支持不需要应用修改使用 java.net.* 和 java.io.* APIs 的代码。此外,只要配置好 JVM 到 InfiniBand 操作系统设备和库(即 InfiniBand 的 VERBs 层 API)的特定连接点,使用 java.net.* 和 java.io.* 的应用代码就能绕过传统的网络协议栈“直接”访问 InfiniBand(也就是使用 Java 针对 OSI 第四层传输层的 API,就可以绕过 OSI 第三层网络层和第二层数据链路层,直接访问 OSI 第一层物理层)。对性能的影响和收到的回报都非常显著。
借助 Java 7 和 SDP,Java 现在可以支持远程直接内存存取(RDMA)
RDMA 是跨网络在两个 JVM 进程(在 *nix 用户地址空间中执行)之间移动应用缓冲区的一种方式。RDMA 不同于传统的网络接口,因为它绕过了操作系统。这允许 Java SDP 通过 RDMA 提供:1、绝对最低的延迟,2、最高的吞吐量,3、最少的 CPU 占用率。
借助 Java 到 RDMA 的连接点,SDP 也能让 Java 具备非常有力的“零拷贝”能力。“零拷贝”操作指 CPU 不用将一个内存区域的数据拷贝到另一个内存区域。网络协议栈的零拷贝版本大大提升了特定应用程序的性能,也能更有效地利用系统资源。CPU 可以继续处理其他任务,数据拷贝则由机器的另一部分并行处理,这样就提升了性能。此外,零拷贝操作减少了在用户空间和内核空间之间切换所消耗的时间。零拷贝也能更有效地利用系统资源,因为大量的拷贝操作是相对简单的任务,如果其他简单的系统组件就能做拷贝,那让复杂的 CPU 去执行就太浪费了。需要注意的是,我们讨论的零拷贝并不是指 java.nio.channels.FileChannel 的 transferTo() 实现的零拷贝。这里的零拷贝性能更高。借助 Java 7 SDP,你可以直接使用原生的 InfiniBand 零拷贝协议实现。
在典型的 Java 部署情况下,SDP 直观上是什么样的?
下图中,Node 1(java.net.Socket 写入者)和 Node 2(java.net.ServerSocket 监听器)部署在配置支持了 SDP 的 Java 7 VM 上,两个 JVMs 可以跨 InfiniBand 网络互相交换应用数据缓冲区,而不需要任何 OS 系统调用或服务调用。令人难以置信的是,Java 数据传输完全绕过了两个操作系统。
- Java 7 应用 Node 1(JVM 启动时使用 SDP)使用 java.net.Socket API 把应用数据块跨网络写给 java.net.ServerSocket 监听器。
- JVM 启动时使用 SDP,所以会完全绕过操作系统的 TCP/IP 栈——应用数据会直接写到 InfiniBand 的 RDMA(要求网卡的物理提供者是 InfiniBand)。
- Java 7 应用 Node 2(JVM 启动时也使用 SDP)使用 java.net.ServerSocket API 监听应用数据块,应用数据块由 java.net.Socket 写入者经 RDMA 跨网络写入。(要求网卡的物理提供者是 InfiniBand)
- 数据会直接写入 Java 7 VM 的应用缓冲区!不需要操作系统或服务调用——既不需要 Node 1 的操作系统,也不需要 Node 2 的操作系统。这就是 Java 7 SDP 的功能。
同样的应用运行在支持 SDP 的 Java 7 上和运行在 Java 6 上,为什么会有性能差异?
下图深入、详细地解释了上图里的 Node 2 在两种场景下的情形:
- 使用配置了 SDP 的 Java 7(下图左边):Node 2 要接收 Node 1 传输的数据,经过 InfiniBand 的 VERBS/RDMA 协议栈、到达 Java 应用需要几个步骤?只需要一个!(这对 UHPC Java 应用来说是个好消息;UHPC 社区现在可以用 Java 7 完成他们需要的功能了)。
- 使用没有 SDP 的 Java 6(下图右边):Node 2 要接收 Node 1 传输的数据,是怎样经过 OSI 网络层协议栈、到达 Java 应用的呢?又需要几步呢?需要五步。(这是我们熟悉的 TCP/IP 协议栈,而不是 SDP。它适用于大多数情景,但不能解决 UHPC 社区的问题。UHPC 社区使用 Java 6 可是枉然)。
怎样管理、配置 Java 7 VM,让它支持 SDP?
下面的配置部分摘取自 Oracle 介绍 Java 7 SDP 的教程。
SDP 配置文件是个文本文件,JVM 启动时会从本地文件系统读取该文件。SDP 配置文件有两种不同类型的条目。不论哪种类型,每个条目都写成一行:
- 一种是 SDP 配置注释行
- 一种是 SDP 配置规则行
注释行以#字符开头,#字符后面的所有内容都会被忽略。
至于规则行,有两种类型:
- bind 规则
- connect 规则
bind 规则表示,只要 TCP 套接字绑定到与规则匹配的地址和端口,就会使用 SDP 协议进行传输。connect 规则则表示,没有绑定的 TCP 套接字尝试连接匹配规则的地址和端口时,就会使用 SDP 协议进行传输。
在 SDP 配置文件里指定规则后,JVM 就能明确知道什么时候用 InfiniBand 的 VERBS/RDMA 协议栈去替换普通的 TCP/IP 协议栈。
第一个关键字用来表明规则是bind还是connect。第二部分指定主机名或 IP 地址。当指定为 IP 地址时,你也可以指定表示 IP 地址范围的前缀。第三部分即最后一个部分是端口号或端口号范围。
我们看看示例配置文件里的如下部分:
# 绑定到 192.0.2.1 时使用 SDP
bind 192.0.2.1 *
# 连接到 192.0.2.* 上的所有应用服务时都使用 SDP
connect 192.0.2.0/24 1024-*
示例文件里的第一条规则指定,本地 IP 地址 192.0.2.1 上的所有端口(*)都会使用 SDP。你应该为分配到 InfiniBand 适配器的每个本地地址都添加一条 bind 规则,其中 InfiniBand 适配器相当于支持 InfiniBand 的网卡。如果你有多个 InfiniBand 适配器,你应该为分配到这些适配器的每个地址都指定一条 bind 规则。
示例文件里的第二条规则表明,只要连接到 192.0.2.*,而且目标端口大于等于 1024,就会使用 SDP。IP 地址里的前缀 /24 表示,32 位 IP 地址的前 24 位都符合指定的地址。IP 地址的每一部分都占 8 位,所以 24 位就表明 IP 地址应该符合 192.0.2,而且最后一个字节可以是任意值。端口部分的 -* 表示“及以上”。端口范围(比如 1024-2056)也是有效的,而且指定的范围是闭区间。
如何启动使用 SDP 的 Java 7 VM?
&> java \ -Dcom.sun.sdp.conf=sdp.conf \ -Djava.net.preferIPv4Stack=true \ <i>Application.class</i>
需要注意的是,启动时要指定网络格式为 IPv4Stack。尽管 Java 7 和 InfiniBand 都支持 IPv6 网络格式,但 Solaris 和 Linux 都不支持两者之间的映射。所以启动支持 SDP 的 Java 7 VM 时,还是要使用基础、可靠的 IPv4 网络格式。
在支持 SDP 的 Java 7 VM 上运行应用,预计性能能提升多少?
这才是终极问题!使用 Java 7 SDP 到底能收获什么?本文显然给不出确定的答案。性能的提升取决于多方面的因素。在本文快结束的时候,让我们了解一下确定的内容:
InfiniBand 要比以太网快很多。高性能计算咨询委员会发布的研究给出了具体的指标,这些指标表明InfiniBand 在低延迟方面比10G 以太网好6 倍,在吞吐量性能上也是10G 以太网的3.7 倍。
此外,Java 7 SDP 使用RDMS 和最好的零拷贝实现。数据传输完全绕过了操作系统的TCP/IP 栈和上下文切换,数据传输如果经过TCP/IP 栈,就需要在内核地址空间里的系统调用和用户地址空间里的应用代码缓冲区之间进行上下文切换。
所有这些,Java SDK API 都做到了百分百的透明。使用java.net.* 和java.io.* 的Java 应用代码不需要修改任何内容。
尽管事情已经发生了很大的变化,但Java 的核心精神仍然没有变。最开始的时候,JVM 负责将应用代码和可移植性隔离开来,一如往昔,JVM 再次独自交付了重要功能:这次是SDP。事实上,Java 原先的口号仍然适用,我们稍作修改就能体现现在令人激动的内容: Java 7 SDP——一次编写,到处运行,有时还运行得超炫!
作者简介
Ben D. Cotton 三世是摩根大通的 IT 顾问,主要工作是在 UHPC Linux 平台上用 Java 数据网格技术呈现、汇总实时的流动性风险数据。他还是 JCP(Java Community Process)成员,目前主要参与两个 JCP 专家组,参与定义 Java 缓存 API(JSR-107)和分布式数据网格(JSR-347)。Ben 于 1985 年 5 月毕业于美国罗格斯大学,获得计算机科学学士学位。毕业后的前十一年,他在 AT&T 贝尔实验室编写支持大量专有通信网络分析和配置协议的 C++ 代码;接下来的十四年,他开始用 Java 代码编写支持低延迟和有事务处理的固定收入 / 衍生电子交易、清算、定价和风险系统。
查看英文原文: Java 7 Sockets Direct Protocol – Write Once, Run Everywhere …. and Run (Some Places) Blazingly
评论