读书笔记《building-a-restful-web-service-with-spring》扩展REST风格的Web服务
在我们了解 聚类技术之前,让我们先讨论一下纵向扩展和横向扩展之间的概念和区别。
处理 流量增加的一种方法是垂直或向上扩展 Web 服务的底层基础架构。通过利用具有强大 CPU 和充足内存的最新服务器架构,服务设计人员可以提高其系统的吞吐量。但是,这种方法受到服务器物理限制的限制。一旦达到这些限制,就无法再提高吞吐量。此外,这种方法可能没有经济意义。事实上,最新一代服务器的成本可能会令人望而却步。
相比之下,水平扩展或向外扩展,系统围绕着大量利用商品服务器展开。集群基础设施将依赖多台服务器,而不是让一台服务器处理所有流量,每台服务器只处理一小部分传入流量。这就是我们所说的 聚类。
这种方法只有在使用负载平衡解决方案的情况下才有可能,本章稍后将对此进行讨论。
集群系统明显的 主要优势在于它能够随着流量的增加而扩展。也就是说,其他好处包括:
另一方面,服务设计者面对集群的挑战之一是状态管理。如果一个系统是无状态的,那么集群就很简单了。但是,如果系统是有状态的,则必须设计某种形式的状态共享,并且集群的复杂性会显着增加。因此,REST 的原则之一是无状态性也就不足为奇了。
服务设计人员支持集群的最有用的工具是负载平衡器。使用 各种算法(例如,循环或最少连接),负载平衡器组件将传入请求分发到多个后端服务器进行处理。下图说明了这样一个基础设施:
负载均衡解决方案涵盖企业级商用设备,例如 F5 的 BIG-IP (https://f5.com/products/big-ip),开源软件包,如 HAProxy (http://www.haproxy.org)。负载均衡的核心是在后端服务器队列中分配负载的直接(尽管很关键)作用。它通常以健康监控的形式这样做,以便只将流量转发到健康的后端服务器。这种动态性还有一个额外的好处,即允许根据请求量快速扩展或缩减后端基础设施。
除了分配传入的流量外,负载均衡器还在前端和后端基础设施之间提供了一定程度的隔离。前端服务器只需要知道负载均衡器就可以传递流量。
线性可扩展性是指向集群添加节点将以恒定速率增加容量的想法。换句话说,线性可扩展性意味着集群管理开销 可以忽略不计。这是一个理想的情况,但在现实世界的部署中很难实现。实际上,维护服务器集群可能需要服务器之间的某种形式的协调。添加到集群的服务器越多,需要的管理就越多。在大型部署中,这种协调可能会导致网络饱和。
扩展应用层的另一个副作用是性能瓶颈可以向下移动到数据层。例如,让我们考虑对服务的一个外部请求会导致两个数据库查询:一个用于验证安全凭证的查询,另一个用于检索请求的数据。现在,让我们假设应用服务器每秒可以处理 100 个请求。在满负荷情况下,数据层需要每秒处理 200 个查询。集群应用层将导致该层更高的吞吐量。如果集群由五个节点组成,数据库将需要每秒处理 1,000 个查询,以使整个系统线性扩展。然而,扩展数据库并不是一个简单的练习。
幸运的是,缓存技术可以应用于数据层(以及应用层),以帮助减少持久层的负载。这些技术将在下一节中讨论。
除了我们在第6章中讨论的缓存技术之外, 性能,这有助于改善 RESTful Web 服务的整体延迟,可以采用其他缓存方法来提高此类服务的可扩展性。
服务设计者可以选择在数据库之上添加一个缓存层。这是提高(读取)吞吐量的常用策略。在我们的示例物业管理系统中,我们使用 休眠以访问数据库。 Hibernate 提供了两种不同级别的缓存。接下来讨论它们。
第二级 缓存是可选的且完全可配置。可以为每个实体定义要使用的缓存策略,并且开箱即用地支持多个提供程序 :
Ehcache (http://ehcache .org):这是一个 Java 开源缓存,支持在内存和磁盘中缓存 数据。 Ehcache 可以设置为分布式缓存。它是使用 Hibernate 进行二级缓存的流行选择。
OSCache (https ://java.net/projects/oscache):这是一个鲜为人知的 Java 缓存框架。这个框架是不再在开发中,这里列出是为了完成。
JBoss 缓存 (http: //jbosscache.jboss.org): 这个缓存框架的目的是提供企业级的集群解决方案。它基于 JGroups 多播库。它是完全事务性的和集群的。
除了前面讨论的数据层缓存,API 设计者还可以选择在应用层应用一些缓存。例如,耗时计算的结果可以存储在缓存中,而不是在每个请求上执行。为此,存在许多流行的选项:
Memcached (http://memcached .org):当涉及到 分布式缓存时,这是最常见的选择。 Memcached 是一个免费、开源、高性能的对象缓存解决方案,用于许多大型 系统。它提供了一个简单的通用 API 以及大多数流行语言的绑定。
Redis (http://redis .io):这是一种新的、更现代的 Memcached 替代方案。它是一个 高级键值缓存。除了缓存 功能外,Redis 还提供计算功能。它的速度和高级功能通常比 Memcached 更受欢迎。
Hazelcast (http://hazelcast .org):这是一个开源的内存数据网格。它通过提供简单的 API 和直接的 部署策略来简化分布式计算。 Hazelcast 还提供了 Memcached 客户端库,因此使用 Memcached 集群的应用程序可以轻松地写入 Hazelcast 集群。
Riak ( http://basho.com/products/#riak):这是一个分布式 NoSQL 键值数据存储,提供高可用性和容错性。数据 可以存储在内存、磁盘或两者的组合中。 Riak 是用 Erlang 编写的。它以性能为代价换取强大的数据完整性保证。
Aerospike (http:// /www.aerospike.com):这是一个开源的实时 NoSQL 数据库和键值存储。这种用 C 语言编写的闪存优化内存缓存解决方案在性能和成本之间提供了良好的折衷。
您应该选择哪种解决方案取决于许多因素,例如性能要求、数据完整性、容错性和成本。然而,无论采用何种解决方案,将有效的分布式缓存层添加到 Web 服务的应用层将为设计人员提供必要的工具来集群他们的服务。
帮助扩展性的另一种方法是使用系统组件之间的异步通信形式。异步通信是一种通信形式,其中在发出请求和响应可用之间经过可测量的时间量。例如,摄影师的作品集系统通常需要生成照片的缩略图以及为原件添加水印。这些过程可能很耗时,因此不适合同步通信方式。该系统的图像处理组件将受益于提供通知您任务完成的回调机制。
此外,通过在组件之间添加这种抽象级别,我们使服务设计人员能够独立处理可伸缩性问题。在前面的示例中,我们可能需要计算优化的服务器来处理图像处理任务,而其他组件可能 只需要通用商品服务器。
一些可能的异步通信方法包括:
面向消息的中间件(MOM):这是一个基础设施,支持分布式系统之间的消息收发,如下图所示:
RabbitMQ (https:// /www.rabbitmq.com) 和 Apache ActiveMQ (http://activemq.apache.org) 是 妈妈们。
异步 RESTful API:通过使用
@org.springframework.scheduling.annotation.Async
注释端点并返回java.util.concurrent.Future
包,可以构建异步 RESTful 操作.
现在,让我们详细研究每种技术。
这里提到数据库轮询是因为这种技术用于现实世界(小规模)系统。虽然这种技术适用于低容量、小规模的系统,但它是一种非常有限的消息传递方法,并且在重负载(例如,有许多消费者)下表现不佳。它还缺乏 MOM 解决方案的复杂性。
MOM 的目的是为系统组件之间的消息交换提供专用基础架构。这样的系统通常提供不同的通信模式,例如生产者/消费者,消费者相互竞争消息,以及发布者/订阅者,所有订阅者都接收自己的消息副本。 MOM 解决方案通常还会提供持久性保证(在发生崩溃时消息不会丢失)并提供一些管理工具。
虽然异步 RESTful API 提供了一些异步性,但它缺乏基于 MOM 的解决方案提供的许多优势。此外,在本质上是同步协议 (HTTP) 上进行异步调用有些笨拙,只有在不能选择使用 MOM 时才应考虑。
除了 有助于扩展性之外,异步通信是确保组件之间松散耦合的一种很好的机制。在消息传递中间件解决方案的情况下,每个组件只需要知道将消息发布到哪里或从哪里使用它们。它们不需要事先了解另一个组件的拓扑。此外,如果一个组件出现故障,则消息可以排在队列中,直到消费组件再次可用。
希望本章为读者提供了应用扩展 RESTful Web 服务的思想和技术。最终,扩展系统是一项复杂的工作,没有一种解决方案可以满足所有需求。设计师要记住的最重要的方面是他们应该避免单点故障,并且在可能的范围内更喜欢无状态系统而不是有状态系统(并非所有应用程序都适合无状态)。通过定义良好的接口和异步通信避免系统组件之间的紧密耦合也应该是议程上的重点。
本章还结束了我们使用 Spring 框架构建高性能、安全和可扩展的 RESTful Web 服务的旅程。您现在应该具备足够的知识来在商业环境中构建这样的服务。本书将使您意识到 REST 是一种非常流行的软件架构方法,因为它具有可移植性、简单性以及不依赖于繁重而复杂的框架,这在基于 SOAP 的 Web 服务中更为常见。