如何快速轻松地加速对API应用程序的访问?

答案很简单:使用行之有效的工具,例如缓存和水平缩放。我们必须马上说,这些并不是唯一的工具,但更多的是,事实证明,即使在现代条件下,经验证的经典方法也是最有效的。让我们看一个实际的例子。



关于原始问题



适应现代资源的PREMIER视频平台已基于机器学习为其客户创建了推荐服务。许多用户转向视频平台-每天大约一百万,PREMIER非常受欢迎-呼叫通过Web表单,移动设备应用程序和Smart TV进行。



我们的服务机器学习所基于的初始数据存储在圆柱型ClickHouse DBMS中。根据时间表,在后台处理数据以构建模型(将用于发布最终建议)。计算结果保存在PostgreSQL关系DBMS中。



在短时间内,应用服务器与客户端之间快速交互问题的解决方案始终是有意义的。为了确保所需的工作速度-响应时间应不超过50毫秒-我们必须优化关系数据库的结构,实施缓存和水平扩展。我们现在将讨论一些技术。







关于缓存



缓存是一种常见的优化技术。我们创建了一个比主存储更快的中间存储,并将最流行的数据放置在那里。大大加快了对缓存数据的访问,这大大提高了应用程序的速度。在开发高负载应用程序时,缓存是在不扩展硬件资源的情况下优化应用程序性能的最常见选项。缓存可以节省一些资源,从而可以为相同的输入重新生成相同的输出。



缓存容量有限-存储速度越快,价格越昂贵-因此需要有效利用。当然,如果您的主存储足够快,从理论上讲您完全可以不进行缓存。但这将在经济上无利可图,因为您将必须进行重大的硬件升级,通常会降低RAM和/或从HDD到SSD替换磁盘。那些。大大增加了基础架构需求,这将影响正在创建的整个应用程序的经济参数。如果没有经过时间检验的缓存,在大多数情况下,创建批量产品是不可能的。



但是,添加缓存层并不能自动解决所有问题。还必须考虑其填充规则,该规则取决于任务的特征,该规则可以根据情况和服务的发展而变化。请记住,这不是万能药,而仅仅是可以缓解应用程序特定部分性能问题症状的补救措施。如果应用程序存在严重的体系结构问题,中等的执行环境,则缓存更有可能增加您的问题。



有几种存储缓存资源的选项:本地-在浏览器的客户端实例上,在应用程序端的第三方CDN服务中。我们将讨论应用内缓存。应用程序进程内存可能是您会遇到的最常见的数据缓存选项。但是,该解决方案有其缺点,因为 内存与执行特定任务的进程相关联。如果您打算扩展应用程序,这将变得尤为重要,因为未在进程之间分配内存,即 它在其他进程(例如负责异步处理的进程)中将不可用。是的,缓存有效,但是我们确实不能从中获得全部好处。



推荐缓存







回到项目,我们知道需要集中式缓存解决方案。对于共享缓存,可以使用例如Memcached:如果应用程序连接到同一实例,则可以在许多进程中使用它。一方面,Memcached是一种简单便捷的解决方案,另一方面,在无效性,数据类型以及对缓存数据存储区的更复杂查询的精确管理方面,它受到了很大的限制。现在,实际上,Redis存储已成为缓存任务的标准,这避免了Memcached的缺点。



Redis是键值的快速存储。它提高了处理数据的效率,因为定义结构成为可能。提供对残障和抢占的精细控制,使您可以从六种不同的策略中进行选择。 Redis支持懒惰和急切抢占以及时间抢占。根据业务实体,Redis数据结构的使用可以提供切实的优化。例如,开发人员可以使用哈希数据结构来存储和操作键的字段和值,而不是将对象存储为序列化的字符串。散列消除了获取整个字符串,将其反序列化以及在每次更新时使用新值替换它在缓存中的需求,这意味着更少的资源消耗和更好的性能。其他数据结构Redis建议的“工作表”,“集合”,“排序集合”,“超日志”,“位图”和“地理索引”)可用于实现更复杂的场景。用于分析时间序列数据的排序集减少了处理和传输数据的复杂性和数量。 HyperLogLog数据结构可用于仅使用少量持久性内存就可以对集合中的唯一元素进行计数,特别是每个HyperLogLog为12 KB(对于密钥本身而言为几个字节)。 Redis可用的约200个命令中有很大一部分专用于数据处理操作,并使用Lua脚本将逻辑嵌入数据库本身。内置命令和脚本功能为直接在Redis中处理数据提供了灵活性,而无需通过网络将数据发送到您的应用程序,从而减少了实现其他缓存逻辑的开销。重新启动后立即可用的缓存数据可以显着减少缓存预热时间,并减轻与从主数据存储重新计算缓存内容相关的负担。在以下文章中,我们将讨论Redis配置的功能以及集群的前景。在以下文章中,我们将讨论Redis配置的功能以及集群的前景。在以下文章中,我们将讨论Redis配置的功能以及集群的前景。



选择缓存工具后,主要问题是缓存中存储的数据与应用程序中存储的数据的同步。有多种同步数据的方法,具体取决于应用程序的业务逻辑。在我们的案例中,困难在于创建数据无效算法。只要在当前情况下需要使用缓存中的数据,或者至少有这种可能性,就可以将缓存中存储的数据存储在有限的时间内。随着形势的发展,它们必须为在变化的条件下更需要的其他数据腾出空间。在这种情况下,主要任务是选择标准,通过这些标准将数据从缓存中逐出。大多数情况下,这是数据相关的时间,但是值得记住其他参数:关于数量,排名(假设相等,有公差,寿命),类别(主要或辅助数据)等。



保持数据最新的基本和通用方法是过时。给定推荐应用程序数据的定期集中更新,我们也使用了这种方法。但是,一切并不像乍看起来那样简单:在这种情况下,监视排名非常重要,这样只有真正流行的数据才能进入缓存。这要归功于查询统计信息的收集和“数据预热”的实现,即在应用程序启动时将数据预加载到缓存中。缓存大小管理也是缓存的重要方面。在我们的应用程序中,大约生成了数百万条建议,因此,将所有这些数据存储在缓存中是不现实的。缓存大小管理是通过从缓存中删除数据为新数据腾出空间来完成的。有几种标准方法:TTL,FIFO,LIFO,最后一次访问。到目前为止,我们正在使用TTL,因为Redis实例不会超出分配的内存和磁盘资源。



回想一下缓存是不同的。通常,有两类:直写和延迟。在第一种情况下,写入是同时执行到高速缓存和主存储器的。在第二个步骤中,最初,仅在高速缓存中执行写入操作,并且写入主存储的操作将被推迟,直到更改的数据被另一个高速缓存块替换为止。回写式高速缓存更难实现,因为它需要监视高速缓存中的数据,以便在从高速缓存中删除数据时将其随后写入主存储。我们将第一个选项与缓存预热过程结合使用。请注意,“预热”本身是一项重要而艰巨的任务,我们的解决方案将在以下文章中讨论。



在推荐应用中扩展



为了提供对推荐应用程序的PREMIER访问,我们使用HTTP协议,它通常是应用程序交互的主要选项。它适用于组织应用程序之间的交互,特别是如果将Kubernetes和Ingress Controller用作基础架构环境,则尤其如此。使用Kubernetes使扩展变得更容易。该工具能够自动平衡集群中各个Pod之间的请求,以实现均匀的操作,从而使开发人员更容易扩展规模。入口控制器模块对此负责,它定义了与Kubernetes中的应用程序进行外部连接的规则。默认情况下,不能从外部网络访问Kubernetes中的应用程序。要提供对应用程序的外部访问,必须声明一个支持自动平衡的Ingress资源。我们正在使用支持SSL / TLS,URI重写规则以及VirtualServer和VirtualServerRoute的Nginx Ingress Controller,以根据URI和主机标头将请求路由到不同的应用程序。



Ingress Controller中的基本配置仅允许您使用Nginx的基本功能-这是基于主机和路径的路由,并且其他功能(如URI重写规则,其他响应标头,连接超时)不可用。应用于Ingress资源的注释使您可以使用Nginx本身的功能(通常可通过应用程序本身的配置使用)并更改每个Ingress资源的Nginx行为。



我们计划不仅在我们现在正在考虑的项目中使用Nginx Ingress Controller,而且还将在许多其他应用程序中使用Nginx,这将在后面讨论。我们将在以下文章中讨论这一点。







使用缓存的风险和后果



任何致力于优化数据密集型应用程序的团队都会有很多有关如何优化缓存的问题。同时,缓存问题不能“一劳永逸”地解决;随着时间的流逝,出现了各种问题。例如,随着用户群的增长,您可能会遇到有关按类别显示数据受欢迎程度的统计信息,因此需要解决此问题。



虽然缓存是用于加速应用程序的强大工具,但它并不是唯一的解决方案。根据您的情况,您可能需要优化应用程序逻辑,更改堆栈和基础架构环境。在验证非功能性需求时,您也应该小心。与产品负责人讨论后,应用程序要求可能会被夸大。必须记住,每个解决方案都有其自己的特征。



在将任何缓存方法应用于项目之前,必须解决提供过时数据,增加整体解决方案复杂性以及引入潜在错误的可能性的风险。毕竟,在这种情况下,缓存只会使解决问题变得复杂,而只会隐藏性能和可伸缩性问题:数据库查询速度慢吗? -缓存导致快速存储! API调用慢吗? -在客户端缓存结果!这是因为随着业务逻辑的复杂性的增加,管理缓存的代码的复杂性也显着增加。



在应用程序的第一个版本中,缓存在实施后立即确实产生了明显的影响。毕竟,业务逻辑也很简单:保存价值并将其取回。无效很容易,因为业务实体之间的依赖关系很小或根本不存在。但是,随着时间的流逝,为了提高性能,您将需要缓存越来越多的业务实体。



缓存并不是解决性能问题的灵丹妙药。在许多情况下,从长远来看,优化代码和核心存储将对您有所帮助。而且,引入缓存应该是对问题的一种反应,而不是过早的优化。



总之,优化应用程序性能并使其可扩展是一个持续不断的过程,旨在在指定的非功能性要求内实现可预测的行为。需要缓存以减少硬件成本和开发时间,以改善性能和可伸缩性。



链接:






All Articles