读书笔记《building-a-restful-web-service-with-spring》性能
为了在商业环境中部署 RESTful Web 服务,必须满足许多条件。这些标准之一是性能。除了产生正确的结果外,RESTful 端点还必须及时这样做。本章讨论如何在现实世界的 Web 服务中解决这些问题。性能优化技术可以应用于 Web 应用程序的不同方面。但是,在本章中,我们将重点关注 RESTful(Web)层。 第 10 章,扩展 RESTful Web 服务,探索适用于 Web 应用程序其他方面的技术。以下主题将在接下来的几页中介绍:
使用 HTTP 压缩
使用 HTTP Cache-Control 指令
使用 HTTP ETag 标头
使用 HTTP
Last-Modified
/If-Modified-Since
标头
为了说明这些技术,我们将构建示例物业管理系统 Web 服务的房间可用性组件
在与远程服务通信 时,不可避免地会花费大量时间通过网络发送和接收数据。从应用程序的角度来看,为了减少网络延迟,服务设计人员可以确保将往返次数保持在最低限度。这是本章其余部分的主题。然而,现在让我们看一下另一种可用于减少通过线路发送的数据量的技术。 HTTP 规范定义了一种机制,用于在响应传输到客户端之前对响应应用压缩算法。
HTTP 压缩围绕两方(服务器和客户端)之间的内容协商展开。 客户端必须通知服务器它支持的压缩算法。通常,这些将是 deflate 和 gzip。客户端通过将以下标头添加到请求中来做到这一点:
如果服务器支持这些压缩方案之一,它可以将该方案应用于传出数据。如果数据被压缩,服务器应在响应中添加以下标头:
有了这些信息,客户端就能够适当地处理响应数据。
其他压缩方案可以与 HTTP 一起使用,但 gzip 和 deflate 是最常见的。那么,问题是服务设计师应该更喜欢哪一个?不幸的是,HTTP 规范中的命名存在一些混淆。 Deflate 和 gzip 实际上使用相同的 压缩算法。 gzip 是一种利用 deflate(算法)进行压缩的数据格式。在 HTTP 的上下文 中,deflate 指的是 Zlib,它是另一种使用 deflate 的数据格式。在技术方面,deflate 方案提供了更好的性能,但不如 gzip 得到广泛支持。因此,gzip 是更普遍的 HTTP 压缩选择。
当提到 性能优化的话题时,缓存往往是首先想到的技术。这种技术可以应用于 Web 服务的不同层。在本节中,我们将专注于利用 HTTP 缓存来提高性能。 第 10 章,扩展 RESTful Web 服务,讨论其他形式的缓存。
HTTP 支持缓存控制指令,以防止客户端和服务器之间不必要的往返。毕竟,减少请求延迟的最佳方法是不必联系服务器来获取响应。这些 指令定义了谁可以缓存响应、在什么条件下以及缓存多长时间。
例如,如果一个资源 可以安全地在客户端缓存一段时间,服务设计者可以选择设置 Cache-Control 标头来指示这样的行为。
服务设计者必须选择缓存是私有的还是公共的。他们可以通过在 Cache-Control 标头中设置适当的值来做到这一点:
Public
表示响应可以被任何中间缓存缓存。这也意味着可以缓存通常不可缓存的内容,例如具有 HTTP 身份验证的响应。
另一方面,private
响应可以被 Web 浏览器缓存,但它们通常只与单个用户相关。在这种模式下,中间体不缓存内容。
包含此标头的响应可以由用户的 Web 浏览器缓存长达 5 分钟(300 秒)。
对于动态资源,缓存 可能不合适。在这种情况下,响应应包含以下标头:
这将指示浏览器/客户端在每次发出请求时与服务器进行检查。该标头通常与 ETag 标头结合使用,本章稍后将对此进行讨论。
在必须完全禁用缓存的情况下,应该使用 no-store
值:
此标头通知代理和客户端不要保留响应缓存,并在发出新请求时始终返回服务器。
Tip
Cache-Control 标头是 HTTP/1.1 规范的一部分。较旧的浏览器和代理可能不完全支持它。 A 常见的解决方法是使用 Pragma 标头(HTTP 的一部分/1.0 规范),而不是缓存控制。
虽然在某些情况下禁用 HTTP 缓存是相关的,例如,当响应中包含敏感数据时,应在适用的情况下启用它。除了使用 max-age
指令外,还可以使用 ETag 标头创建更复杂的解决方案。下一节将讨论此标头的使用。
在前面的章节中,我们讨论了我们的物业管理服务的库存组件。我们将实现的下一个组件是可用性服务。
这个 组件的目的是为最终用户和第三方系统提供一种查询房间在给定时间段内是否可用的方法。
我们将通过查找示例属性中的所有房间并覆盖给定时间段内的现有预订来实现此功能,以计算可用性。在大型酒店使用的实际系统中,使用更复杂的算法来优化房间分配。然而,在我们的例子中,更简单的方法就足够了。因此,让我们考虑以下服务接口:
现在让我们考虑我们需要创建的端点以通过 RESTful API 公开此功能。在继续实施此端点之前,要回答的一个基本问题是,该资源将在哪个 URL 下可用?一种可能的方法是向我们现有的房间资源添加一个新端点(参见 第 3 章, 第一个端点)。但是,由于我们不会只返回房间列表,因此它不是最合乎逻辑的 解决方案。此外,出于可扩展性的目的,我们可能希望将此组件部署在与 Inventory Service 不同的架构上(这些问题将在 第 10 章、扩展 RESTful Web 服务)。因此,我们将使用在新 URL 下公开的新资源:
使用这个新的 Spring RestController
,可以使用诸如 http://localhost:8080/availability?from=2016-12 之类的 URL 检索房间可用性-01&until=2016-12-01
。
此 URL 将返回 2016 年 12 月 1 日的可用房间列表。
Note
由开发人员决定以哪种格式表示日期。例如,日期可以接受 UNIX 时间(https://en.wikipedia.org/wiki/Unix_time)。在我们的例子中,我们选择以 ISO 8601 格式(https://en.wikipedia.org/wiki/ISO_8601),因为它更易于阅读。
此外,我们还定义了一个名为 roomCategoryId
的查询参数,它是可选的。使用 Spring,我们可以使用以下注解来做到这一点:
正如我们在 第 5 章 中看到的,REST 中的 CRUD 操作< /span>,这个注解指示 Spring 将查询参数 roomCategoryId
映射到我们的 Java 方法参数(如果存在)。
请求给定日期的可用性将返回如下数据:
根据我们的简报,此回复列出了请求时间段内每个日期的可用房间。在这种情况下,ID 为 1 的 房间在 2016 年 12 月 1 日可用。
定义 RESTful 端点后,让我们看看如何添加对 HTTP 缓存的支持。
正如本章开头所讨论的,HTTP 提供了几种缓存方法。在本节中,我们将使用 Last-Modified
/If-Modified-Since
标头来防止通过网络发送不必要的数据.为此,我们需要访问 HTTP 响应。让我们修改我们的端点:
Spring 会将新属性映射到表示请求的 org.springframework.web.context.request.WebRequest
对象。
下一步是能够计算出捕获响应当前状态的日期。在我们的例子中,我们可以使用在请求期间最近更新的预订的更新日期。
然后,在我们端点的方法体中,我们可以利用 Spring 对缓存的支持,如下所示:
通过使用 WebRequest.checkNotModified()
,我们将 If-Modified-Since
请求标头值与我们计算的值进行比较.如果值匹配,我们不需要处理请求,Spring 将返回 304 响应。否则,我们可以继续处理可用性请求并包含我们生成的 Last-Modified
标头。
Note
调用 checkNotModified()
将确保在响应中设置相关的 HTTP 标头。因此,如果数据没有发生变化,则不需要进一步处理,开发者可以安全地返回 null
,而不是手动构造 304 响应。
例如,如果一个 新用户第一次请求可用性,服务器将响应以下标头:
重新发出相同的请求后,用户的浏览器将包含以下标头:
然后,服务器将能够将请求中提供的时间戳与我们上次修改的日期进行比较,并发出 304 响应:
当生成最后修改日期不合适时,可以使用ETags代替。 Spring 以可以添加到 REST servlet 的过滤器 (org.springframework.web.filter.ShallowEtagHeaderFilter
) 的形式对 ETag 提供透明支持。此过滤器会自动生成响应的 MD5 哈希。
可以在 Web 应用程序描述符 (web.xml) 中添加过滤器:
或者,在使用 Spring Boot 时,可以使用配置类添加过滤器:
当客户端发出第一个可用性请求时,服务器会返回以下标头:
重新发出相同的请求后,Web 浏览器将包含以下标头:
The server will issue the following response:
实际的响应正文 未发送,并指示浏览器使用其本地缓存副本。虽然这种方法提供了一种透明机制来缓存未更改的数据,但它无助于提高服务器端的性能。
本章让我们有机会了解如何利用 HTTP 优化方法来提高 RESTful Web 服务的性能。在减少客户端和服务器之间的往返行程以及通过网络发送的数据量时,服务设计人员可以确保将请求延迟保持在最低限度。 HTTP 优化的其他好处包括由于带宽减少和服务消费者的功耗降低而降低了运营费用。当消费者是电池寿命有限的移动设备时,最后一点非常重要。
除了与 HTTP 相关的优化之外,第 10 章、扩展 RESTful Web 中讨论的其他技术服务,可用于进一步管理网络服务性能。创建 RESTful 服务时的下一个重要主题是安全性。在下一章中,我们将看看如何使用 Spring 处理安全性。