10.6 Java EE网络API
我们前面介绍过的几种数据交换技术——XML 解析、JSON 处理和对象序列化——可以在不同的应用中使用,但主要是在 JAX-WS、JAX- RS 和 IIOP/RMI 这三个 Java EE 网络 API 中使用。
这些 API 的协议差别很大,特性也有很大的不同。你在决定为何使用它们以及何时使用它们时,考虑的首要因素就是它们的特性。关于它们有许多争论,比如 JAX-RS 是否比 JAX-WS 快,不过这些争论都假设有这样一个通用型应用,这个应用可以用两种框架编写。如果确定需要安全特性,那就应该选择 JAX-WS,而不管它与 JAX-RS 相比性能如何。如果应用必须和已有的输出 IIOP 接口的服务器通信,那么选择也就显而易见了。
但对于这几种网络 API 来说,它们需要克服的性能问题类似。本节讨论其中的一些困难以及如何处理这些困难。
调整传输数据的大小
影响这些技术性能的首要因素是数据交换,这是为什么本章花那么多时间讨论它的原因之一。传输的数据量应该尽量小,无论是压缩或去冗,或者其他技术。
另一方面,发起一次网络调用会有很显著的网络开销。设计网络 API 时,应该设计成“粗粒度”的——也就是说,最好一次调用返回大量数据,使得客户端发起的网络调用总数最小。这个原则与减少数据交换量相违背,所以必须做些权衡。
可以在前面股票 RESTful Web 服务平均响应时间的测试中观察到这种平衡。可以将服务设计成只返回一段时期内的基本数据(最高价、最低价、平均价和标准差),也可以设计成在返回这些基本数据的同时再加上这期间每天的日数据。
如果预先知道客户端要如何使用数据,就能很容易且精确地知道会返回哪些数据。但是,实际情况并不总是这样。在这个例子中,比如说客户端请求某只股票 5 年的历史数据,那开始时客户端应用只会向用户展示概要数据。如果用户想深入了解数据,查看单个股票的日数据,那会发生什么?是否所有数据都应该在第一次调用中返回给客户端,使得查看详情时无需再次发起网络调用?是否应该只返回概要数据,而在用户想要了解某年详情时,程序才再次发起调用获得那年的日数据?是否第二次调用应该获取整个 5 年的历史数据,即便此时用户只想看第三年的数据?
为了确定此处该用哪种策略,可以比较一下返回全部数据的时间和多次网络调用的时间。表 10-14 显示了在不同场景下获取数据的平均响应时间。
(1) 客户端请求 1 年的数据。
(2) 客户端请求 1 年的概要数据。
(3) 客户端请求 5 年的数据。
(4) 客户端发起 2 次请求:先请求概要数据,然后进一步请求特定时间的详细数据。
(5) 客户端发起 10 个请求:1 个概要请求,加上 9 个特定时间详细数据的请求。
这些测试是在我的宽带连接下进行的。毫无疑问,网络速度对报告的时间有巨大影响(参见表 10-14)。
表10-14:各种场景下RESTful的平均响应时间
| 场景 | 平均响应时间(毫秒) | 数据量(字节) |
|---|---|---|
| 1 年数据 | 90 | 30 K |
| 1 年概要数据 | 30 | 60 |
| 5 年数据 | 300 | 186 K |
| 2 次概要请求 | 60 | 2 次调用;每次 60 |
| 10 次概要请求 | 280 | 10 次调用;每次 60 |
获取一整年完整数据的时间并不比只获取概要数据长太多,所以如果用户只需要获取其中的三个数据片段,那一次性返回整个数据集总是更好的选择。
不过 5 年的概要数据有点不同:数据编组和发送所需的时间要长得多,所以在总时间达到平等前,用户需要发起 11 个获取详情的请求。
本例中的时间包括将 RESTful 服务返回的 JSON 数据编组的时间,这个时间取决于数据有多少年。但是多个客户端可能会请求相同的数据集,这种情况可以重用之前编好组的数据。如果编好组的数据已经计算好,响应时间的拐点就会有很大的不同,如表 10-15 所示。
表10-15:各种场景下(响应有缓存)RESTful的平均响应时间
| 场景 | 平均响应时间(毫秒) |
|---|---|
| 1 年数据 | 50 |
| 1 年概要数据 | 30 |
| 5 年数据 | 90 |
| 2 次概要请求 | 60 |
| 10 次概要请求 | 270 |
由于调用一次概要数据的开销基本不变,所以概要数据的响应时间差别很小。而获取 1 年和 5 年完整数据的时间只是在传输数据上,明显少于之前数据需要计算和编组的情况。一般来说,可以返回给客户端大量可能并不需要的数据,而没有太多性能上的损耗。
网络已今非昔比
虽然我看起来不太像老顽固,但我确实曾经用 300 波特的调制解调器连接过远程网络。(幸运的是,我不必穿过 4 英尺厚的雪步行 5 英里到学校。)对于这种情况,传统上的偏见会认为有许多的网络调用。
如今,我打开浏览器,每次敲入一些字符时,都会调用远程的 Google 服务器,然后它会返回给我一些文字,辅助我进行一些搜索。Google 已经算出,完成调用大约需要 200 毫秒或更少,这种延迟时间并不会引起我的注意。
随着网络速度越来越快,本节我所讨论的权宜之计会变得越来越不重要。另一方面,我的智能手机大多数时候都有幸能使用快速的 3G 信号,一年中少数时候我在海洋上,我也很有幸可以通过卫星连接互联网。更快的硬件(或网络)总是有助于性能的提升,但却不能以此替代性能设计。如果应用可以检测出正处于快速网络,就能进行快速细致的网络调用,这会是一个很好的特性。要确保应用可以正常工作,无论它部署在哪里。
