内容


基于 Java 的工作负载在云中的透明网络加速

Java Sockets over RDMA (JSOR) 库简介

Comments

目前,部署在云中的分布式 Java 应用程序组件之间的通信逻辑是通过 TCP/IP 套接字编程技术实现的。随着云数据中心开始采用更快速的网络(比如 10/40/100Gbps 以太网),现在我们可以使用更快的网络通信技术,比如 Remote Direct Memory Access (RDMA)。RDMA 程序通常用 C/C++ 编写,使用低级别的 API,比如 OpenFabrics Alliance (OFA) 动词,或使用高性能计算工具,比如 Message Passing Interface (MPI)。通过 Java Native Interface (JNI) 访问基于 Java 的应用程序中的低级别 API,增加了编程的复杂度和性能开销。Sockets Direct Protocol (SDP) 是在 Java 7 中提供的类似方法,对许多工作负载都没有表现出性能优势。RDMA Sockets (R-Sockets) 是另一个类似的方法,只适用于 C/C++ 程序。

在本文中,我们将介绍一个新的 Java 特定的、兼容 Linux 套接字的 RDMA 通信库,其名称为 Java Sockets over RDMA (JSOR),是 Linux/AMD64 和 Linux/Intel 平台上的 IBM Java SDK 7SR6 的一部分。我们将通过一个简单的 Java 客户端-服务器程序(参见 下载 部分)演示 JSOR 的用法和优势,该程序使用 Java 套接字接口编写,在支持 RDMA 的云基础架构上不需要更改任何代码就可以执行它。

背景

在一个典型的 Java 客户端-服务器场景中部署的云计算环境,服务请求的响应时间往往受到请求者和服务提供主机之间的网络连接的响应时间限制。实现远程端点之间的交互的网络通信逻辑通常使用 Java 套接字接口来建立连接和传输数据。默认情况下,Java 套接字接口是基于 POSIX 套接字 API 实现的。每个网络操作都必须通过底层操作系统,然后再到达网络接口。此要求导致软件层之间需要昂贵的操作系统上下文切换和多次缓冲区复制。

作为专用网络接口卡 (NIC) 的一部分的专用 TCP/IP 协议卸载引擎可以用来降低这些网络处理开销。然而,此类卸载技术仍然需要一些缓冲区复制步骤。由于云计算得到普遍采用,许多企业的数据中心已经开始将其网络链路从 10Gbps 以太网迁移到 40Gbps 以太网,以解决云计算不断增加的带宽需求。

RDMA 是一种基于硬件的协议卸载技术(最初针对 InfiniBand 和高速以太网等高性能网络结构提出)直接在两个远程应用程序内存之间传输数据,不涉及任何主机处理器。RDMA 可消除昂贵的操作系统上下文切换,从而节省大量 CPU 周期。由于这个基于消息的协议是专门为高性能网络定义的,应用程序可以利用更高网络速度来实现低于 10 微秒的延迟。

随着 RDMA over Converged Ethernet (RoCE) 标准的出现,现在可以直接在现有的高速 10/40Gbps 以太网基础架构之上使用 RDMA 协议。所以,通过从传统的 TCP/IP 堆栈迁移到基于 RDMA 的网络处理,一些基于云的应用程序可以获得延迟和吞吐量方面的优势,同时使用更少的 CPU 资源。

SDP 是一个为 InfiniBand 和 RoCE 等支持 RDMA 的网络结构定义的标准有线协议,可以透明地加速基于套接字的应用程序的流式传输。从 Java 7 开始,JDK 在 Linux 和 Solaris 平台上支持 SDP。 然而,SDP 是一个基于内核的实现,因缓冲区复制和上下文切换开销而会对性能产生负面影响。

在以下各节中,我们将介绍并描述 JSOR,它是完整的用户空间解决方案,可以绕过内核实现与类似的基于 RDMA 的原生解决方案相媲美的性能。

关于 JSOR

JSOR 有着云网络加速的特性,当底层的基础架构支持 RDMA 时,该特性能够透明地实现 Java 流套接字的 RDMA 通信。JSOR 将高性能 RDMA 有线协议纳入标准 Java 套接字库。目前,为 java.net.Socketjava.net.ServerSocket API 以及相关的输入和输出流提供支持,使大多数现有的 Java 客户端-服务器应用程序可以受益于开箱即用的性能改进。(参见本文后面的 选择 JSOR。)

JSOR 设计简介

在传统的云网络场景中,访问和服务节点之间的任何交互最终成为数据包,在线缆上流过一个或多个以太网交换机,如图 1 所示。

图 1. 传统的云网络
显示传统云网络的图
显示传统云网络的图

每个网络操作(无论与连接还是数据传输相关)产生一个或多个 Java 套接字调用。在 Java 级别执行的任何套接字操作通过 JNI 层调用相应的原生(C 或 C++)库操作。在 JNI 层执行调用之前和之后,在 Java 级别会发生一定量的预处理和后处理。由于 TCP/IP 协议由操作系统内核栈处理,最终所有 JNI 套接字特定的方法都会导致上下文切换。传输或接收也需要在 Java、操作系统和 NIC 级别进行多个缓冲区复制。多个缓冲区复制和 CPU 上下文切换等网络处理开销导致较高的网络延迟和较差的吞吐量。

JSOR 库兼容 R-Sockets 协议,后者由包含在 Open Fabric Enterprise Distribution (OFED) 中的 R-Sockets 库提供。JSOR 库包括一些修改,使其适用于常见的 Java 应用程序需求。它提供了重要的可扩展性、可靠性和可服务性改进。

相较于 SDP 和 TCP/IP over InfiniBand (IPoIB),JSOR 通常产生更高的性能。在我们的微基准试验中,JSOR 可以提供比 SDP 高出 50% 的吞吐量,比 IPoIB 高出 100% 的吞吐量。更好的性能主要归因于一个事实,即作为标准 Java 类库的一部分,JSOR 可以优化 Java 套接字实现。例如,JSOR 避免跨 JNI 边界的数据复制,更好地支持各种套接字语义,并根据套接字使用模式自动调整 RDMA 参数。而 IPoIB 和 SDP 是基于内核的传输解决方案,但 JSOR 完全在用户空间中,所以它可以缩短数据路径,并减少内核中的开销。

如图 2 所示,JSOR 在 Java 级别阻截 Java 套接字调用,并通过底层 RDMA 基础架构路由它们。在 Java 执行过程中,必须指定一个 JSOR 启用属性,指向一个相应的配置文件。当发生从 TCP/IP 到 RDMA 的切换时,应用程​​序与其远程对应组件的所有交互都流经底层的 RDMA 硬件。

图 2. 加速的云网络
图中显示加速的云网络
图中显示加速的云网络

使用 JSOR

在使用 JSOR 之前,在云执行环境中必须满足几个先决条件:

  • 底层的主机应该有一个适当的主机通道适配器 (HCA) 或 RDMA NIC,并且通过一个高性能 InfiniBand 或以太网交换结构与远程主机互连。
  • 每个参与的主机都应该已安装 OFED 1.5.1 或更高版本的基础运行时库。具体而言,JSOR 在执行时查找 libibverbs.so 和 librdmacm.so 库,以动态加载函数指针。
  • 根据应用程序的需要,您的用户帐户应该获得足够的(最好是无限的)可锁定内存授权。默认情况下,JSOR 套接字缓冲区是内存固定的,所以在数据传输的关键阶段,操作系统不能将它们交换出去。在 Linux 上,使用
    ulimit -l shell 命令显示最大锁定内存设置。

当这些基本要求得到满足时,在客户端和服务器端点都需要一个纯文本格式的配置文件。配置文件中的每条记录或每一行指定一个 acceptbindconnect 规则,并应至少包含由空格分隔的四个字段:

  • 第一个字段表示网络提供商的类型。目前,只有 rdma 提供商是可用的。
  • 第二个字段指定一个 acceptbindconnect 关键字,具体取决于要指定哪个规则。
  • 如果指定的规则是 acceptbind,则第三个字段指定一个本地 IP 地址,或者如果指定的规则是 connect,则第三个字段指定一个远程 IP 地址。
  • 第四个字段指定一个或一组端口,在其上允许 RDMA 通信。基本上,第三和第四个字段共同限定了一组套接字端点,用于 RDMA 特定的连接建立和数据传输。
  • 第五个和后面的字段都仅适用于 accept 规则,指定在接受传入的 RDMA 连接请求时的客户端 IP 地址列表。

服务(被动)端的配置应该有 accept 或 bind 条目,而客户端(主动)的配置应该有 connect 或 bind 条目。

例如,要在服务主机 192.168.1.1 上通过端口 65444 接受来自客户端 192.168.1.3 和 192.168.1.4 的 RDMA 连接,在 Java 应用程序服务器的配置文件(我们称之为 rdma_server.conf)中需要下面的规则:

rdma    accept    192.168.1.1    65444    192.168.1.3    192.168.1.4

同样,要从任一客户端到监听端口 65444 的服务主机 192.168.1.1 请求 RDMA 连接,在 Java 客户端应用程序的配置文件(我们称之为 rdma_client.conf)中需要下面的规则:

rdma    connect    192.168.1.1    65444

除非您显式绑定到特定的本地地址,否则在客户端上将使用一个临时端口,以建立与服务端的连接。在下面的示例中,bind 规则被添加到 rdma_client.conf 文件,以在端口 65333 上建立其 RDMA 连接端:

rdma    connect    192.168.1.1    65444
rdma    bind       0.0.0.0        65333

bind 规则中的第三个字段 (0.0.0.0) 指向空地址,它默认为本地主机上的第一个可用的 InfiniBand 地址。

当准备好配置文件后,在 Java 命令执行过程中将它指定为 com.ibm.net.rdma.conf 属性的值。例如,在被动(服务)端:

java -Dcom.ibm.net.rdma.conf=rdma_server.conf SampleServer args

在主动(客户端)端:

java -Dcom.ibm.net.rdma.conf=rdma_client.conf SampleClient args

清单 1 显示了 SampleServer 类的一部分,它创建了一个服务器套接字,并等待来自远程端的连接。当连接建立后,服务器从客户端接收指定数量的字节,并在单次迭代中将相同数量的字节发送回客户端。此接收/发送步骤会重复指定的次数。

清单 1. SampleServer.java
 // Create server socket to listen on x.x.x.x address and x port
 ServerSocket server = new ServerSocket(Integer.parseInt(args[1]), 0, InetAddress.getByName(args[0]));
 ...
 Socket client = server.accept();
 ...
 // Receive and send message specified number of times
 for (int i = 0; i < xferCount; i++) {
     in.read(msgBuf, 0, msgSize);
     out.write(msgBuf, 0, msgSize);
}

清单 2 显示了 SampleClient 类的一部分,它请求与远程服务主机建立连接。当连接建立后,客户端发送指定数量的字节到服务器,并在单次迭代中接收从服务器返回的相同数量的字节。此发送/接收步骤会重复指定的次数。

清单 2. SampleClient.java
// Create client socket to connect x.x.x.x address and x port
 Socket client = new Socket(InetAddress.getByName(args[0]), Integer.parseInt(args[1]));
 ...
long startTime = System.nanoTime();
for (int i = 0; i < xferCount; i++) {
    out.write(msgBuf, 0, msgSize);
    in.read(msgBuf, 0, msgSize);
}
 long endTime = System.nanoTime();

在 SampleClient.java 中,对整个发送/接收序列进行计时,这样我们就可以计算出总字节数的往返时间 (RTT)。

运行示例程序

我们执行了下面的示例程序,使用 4KB 的消息大小和 1000 次重复计数,比较各种协议的 RTT。运行这些示例程序的测试平台包含两台 IBM HS22 刀片服务器,它们由一个 Voltaire 40Gbps InfiniBand 交换机连接起来。每台服务器都运行 Red Hat Enterprise Linux (RHEL) v61,由一个 8 核 Intel Xeon CPU L5609 @ 1.87GHz 和 148GB 内存以及一块 Mellanox MT26428 ConnectX VPI PCIe 卡驱动。

JSOR — SampleClient 日志
$ cat rdma_client.conf
rdma connect 7.7.12.10 65444
$ java - Dcom.ibm.net.rdma.conf=rdma_client.conf SampleClient 7.7.12.10 65444 1000 4096
Client Ready>
Local:/7.7.12.9:40563 Remote:/7.7.12.10:65444
SBuf:32768 bytes RBuf:45056 bytes
Round trip time of 4096000 bytes:27313 usec
JSOR — SampleServer 日志
$ cat rdma_server.conf
rdma accept 7.7.12.10 65444 7.7.12.9
$ java -Dcom.ibm.net.rdma.conf=rdma_server.conf SampleServer 7.7.12.10 65444 1000 4096
Server Ready>
Local:/7.7.12.10:65444 Remote:/7.7.12.9:40563
SBuf:32768 bytes RBuf:45056 bytes
Received/Sent 4096000 bytes
SDP — SampleClient 日志
$ cat sdp_client.conf
bind * *
connect 7.7.12.10 65444
$ java -Dcom.sun.sdp.conf=sdp_client.conf 
-Djava.net.preferIPv4Stack=true SampleClient 7.7.12.10 65444 1000 4096
Client Ready>
Local:/7.7.12.9:39156 Remote:/7.7.12.10:65444
SBuf:8388608 bytes RBuf:8388608 bytes
Round trip time of 4096000 bytes:33836 usec
SDP — SampleServer 日志
$ cat sdp_server.conf
bind * *
connect 7.7.12.10 65444
$ java -Dcom.sun.sdp.conf=sdp_server.conf 
-Djava.net.preferIPv4Stack=true SampleServer 7.7.12.10 65444 1000 4096
Server Ready>
Local:/7.7.12.10:65444 Remote:/7.7.12.9:39156
SBuf:8388608 bytes RBuf:8388608 bytes
Received/Sent 4096000 bytes
IPoIB — SampleClient 日志
$ java SampleClient 7.7.12.10 65444 1000 4096
Client Ready>
Local:/7.7.12.9:40666 Remote:/7.7.12.10:65444
SBuf:99000 bytes RBuf:174752 bytes
Round trip time of 4096000 bytes:98848 usec
IPoIB — SampleServer 日志
$ java SampleServer 7.7.12.10 65444 1000 4096
Server Ready>
Local:/7.7.12.10:65444 Remote:/7.7.12.9:40666
SBuf:99000 bytes RBuf:174752 bytes
Received/Sent 4096000 bytes
TCP/IP over Ethernet — SampleClient 日志
$ java SampleClient 9.42.84.20 65444 1000 4096
Client Ready>
Local:/9.42.84.26:48729 Remote:/9.42.84.20:65444
SBuf:32768 bytes RBuf:43690 bytes
Round trip time of 4096000 bytes:194224 usec
TCP/IP over Ethernet — SampleServer 日志
$ java SampleServer 9.42.84.20 65444 1000 4096
Server Ready>
Local:/9.42.84.20:65444 Remote:/9.42.84.26:48729
SBuf:32768 bytes RBuf:43690 bytes
Received/Sent 4096000 bytes

表 1 显示了我们测试的每一个协议的 RTT。

表 1. 运行实例的往返时间
协议发送/接收的总字节数RTT(微秒)
JSOR4,096,00027,313
SDP4,096,00033,836
IPoIB4,096,00098,848
TCP/IP4,096,000194,224

如表 1 所示,JSOR 的性能优于其他协议。

跟踪 JSOR

当您在 JSOR 模式下运行基于云的 Java 应用程序时,始终重要的是要验证您的应用程序是否选择了 RDMA 路径来建立连接和传输数据。因为 JSOR 启用旨在对应用程序透明,所以在正常模式下没有简单的检查方法。但是,您可以打开 IBM JDK 的跟踪选项,启用服务级别视图。更可取的是,同时打开 Java 方法跟踪和 JSOR/NET 原生跟踪,以了解全貌。在支持 JSOR 的应用程序上调用跟踪选项的典型方法是使用以下调用:

java -Dcom.ibm.net.rdma.conf=config_file 
   -Xtrace:methods={java/net/RDMA*.*},iprint=mt,iprint=NET,iprint=JSOR main_class args

例如,我们可以在 JSOR 模式下启用跟踪,并重新运行 SampleClient 和 SampleServer 应用程序。

SampleClient 跟踪调用是:

java -Dcom.ibm.net.rdma.conf=rdma_client.conf 
   Xtrace:methods={java/net/RDMA*.*},iprint=mt,iprint=NET,iprint=JSOR 
   SampleClient 7.7.12.10 65444 1000 4096

SampleServer 跟踪调用是:

java -Dcom.ibm.net.rdma.conf=rdma_server.conf 
   -Xtrace:methods={java/net/RDMA*.*},iprint=mt,iprint=NET,iprint=JSOR 
   SampleServer 7.7.12.10 65444 1000 4096

清单 3 显示了两个调用所生成的跟踪日志中的一部分。

清单 3. JSOR 示例跟踪日志
04:26:27.500 0x21e3e100  mt.0 >java/net/RDMANetworkProvider.initialize()V Bytecode method, This=21e02468
04:26:27.500 0x21e3e100  mt.2  >java/net/RDMANetworkProvider.initialize0()I Native method, This=21e02468
04:26:27.501 0x21e3e100   NET.440  >initialize0(env=0000000021E3E100, obj=0000000021E73B40)
04:26:27.502 0x21e3e100  JSOR.0     >RDMA_Init()
04:26:27.502 0x21e3e100  JSOR.39    >initverbs()
04:26:27.502 0x21e3e100  JSOR.43     <initverbs(rc=0)
04:26:27.502 0x21e3e100  JSOR.46    >initjsor()
04:26:27.502 0x21e3e100  JSOR.47    <initjsor(rc=0)
04:26:27.502 0x21e3e100  JSOR.3    <RDMA_Init(rc=0)
04:26:27.502 0x21e3e100  NET.441 <initialize0(rc=0)
04:26:27.502 0x21e3e100  mt.8  <java/net/RDMANetworkProvider.initialize0()I Native method
04:26:27.502 0x21e3e100  mt.6  <java/net/RDMANetworkProvider.initialize()V Bytecode
method

JSOR 拥有超过 200 个原生级别的跟踪 hook,所以即使小应用程序也可能很容易就会生成很大的追踪文件。例如,当 SampleServer 和 SampleClient 在跟踪模式下运行时,跟踪日志约为 7.5MB。

选择 JSOR

需要注意的一点是,只有网络 I/O 密集​​型和延迟关键型工作负载可以从 RDMA 中受益。我们鼓励您在决定使用 JSOR 之前估计工作负载的端到端延迟。两种类型的应用程序有可能会获得更多好处:

  • 在分布式组件之间通过长期运行的连接传输大量数据的应用程序。建立连接所用的时间有点长,而且在 JSOR 中所需的堆外可锁定内存量比传统的 TCP/IP 套接字大得多。
  • 没有动态地为每个网络通信分配数据缓冲区的应用程序。JSOR 需要显式的缓冲区管理,这一点与 TCP/IP 不同,TCP/IP 可以根据需要动态地分配缓冲区。 如果应用程序消息的大小变化是最小的,并且提前知道最大消息的大小,那么 JSOR 可以静态地分配缓冲区。

结束语

本文介绍了一个名为 Java Sockets over RDMA (JSOR) 的 IBM JDK 特性,在面向 Linux/AMD64 和 Linux/Intel 平台的 IBM Java 7SR6 中提供该特性。我们讨论了 JSOR 背后的技术,并将其与基于 TCP/IP 协议和 SDP 协议的现有解决方案进行了比较。我们概述了在基于云的环境中使用 JSOR 的过程,其中使用了示例客户端-服务器程序和相关的配置文件。通过在我们的本地测试平台上运行示例程序,我们证明了 JSOR 可以提供比 SDP、IPoIB 和 TCP/IP 协议更好的往返时间。 我们还描述了服务级别跟踪选项,该选项可用于验证应用程序端点是否使用 RDMA 路径进行通信。最后,我们讨论了选择可以受益于 JSOR 的应用程序的指导准则。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, Cloud computing
ArticleID=977778
ArticleTitle=基于 Java 的工作负载在云中的透明网络加速
publish-date=07152014