你是否想过,在月底结账时,你才发现自己的账户有错误交易?也许你遇到过这种事,并且印象深刻。数字化革命让你可以随时随地获取自己的信息。无疑,我们离未来世界不远了。在这个世界,相关信息可以在有需要时出现,你无需去寻找。
Capital One正致力于使我们做的几乎所有事实时化。通过改进云计算和大数据工程工具,它不断为解决方案增加实时特性。我们的团队一直专注于为客户带来与其相关的、实时的个性化洞察。我们试图找出客户消费行为中非常特殊的交易,例如餐厅消费高得离谱、重复账单的增加、全新免费试用的开始和多次重复交易等等。
经过多年发展,Spark框架逐渐发展成大规模实时流和批处理需求的首选技术。但是,伴随强大计算能力而来的是更高的操作和维护成本,我们开始体会到为满足实时流需求而运营Spark基础设施带来的痛苦。
所有,我们团队接受挑战,找到一个更简单、维护少且高度可伸缩的模式,并且围绕其设计一个无服务器流解决方案。
据Databricks的博客,Apache Spark是处理大规模批处理和流数据的最快开源引擎之一。这点显而易见。既然Spark性能如此出色,为什么我们还要考虑使用其他的东西?
图片来自Databricks blog
然而,让我们根据应用程序的需求评估下,比如:
不要忘记开发成本,这是所有成本中最大的一部分。考虑工程师在开发Spark基础设施时所需的所有特殊技能,比如像Scala或Python这样的编程语言、安装和管理Spark基础设施的脚本知识以及Spark缓存等。
尽管Apache Spark有着令人印象深刻的效果,但是如果你关心Apache Spark的运营开销,那么无服务器流解决方案可能是更好的选择。事实上,大多数实时流用例的加载速度都低于每秒1000个事务。
不必因为小的工作负载而去应对Spark基础设施的复杂性。相反,使用无服务器流解决方案来简化你的代码,可以极大降低成本和复杂性。
在实现几个与流相关的用例后,我认为理想的流解决方案应满足以下需求:
在现代应用程序架构中,自动伸缩被视为基本的设计考虑因素之一。在云计算时代,你可以根据需求获得无限的计算能力,因此不需要因为峰值负载进行扩展并支付额外费用。
虽然你能规划好主要的周期性工作负载,但是,在传统的基于服务器的基础设施中,你很难在一分钟内对其进行优化。理想情况下,应用程序应该能在请求出现较大峰值时自动修复以及自动伸缩。
通常,流应用程序被设计成每秒接收成千上万个请求,并最终降到一个更易于管理的范围。当出现意料之外的峰值时,流应用程序可以横向扩展,但是下游的阻塞调用(API、DB等)可能无法扩展。
因此,限流成为任何流应用程序的基本需求之一。记住——你系统的好坏取决于最薄弱的环节。
应用程序总是与其他资源(如API、数据库等)相连接。相关系统难免出现故障,但同时,我们也希望保护应用程序不受这些问题的影响。
在流应用程序中,容错是关键需求之一,因为你不希望在后端系统宕机时丢失数据。
与重新创建解决方案相比,我们常常更关注重用。重用的程度取决于组件的模块化和大小,而微服务是重用的最佳示例。通过使流解决方案的构建块更小且可配置,我们可以加强跨多个应用程序的组件重用。
想象一下,数百万条消息/事件流经你的应用程序,你能跟踪每一条消息并了解该消息究竟发生了什么。当你构建面向客户的关键应用程序时,这一点变得更加重要,并且需要查明特定的客户事件究竟发生了什么。
因此,对于同步或异步系统来说,监控都非常重要。
那么,我们如何构建我们的解决方案?
我们的无服务器流架构是基于事件驱动的微服务架构建模的,其中每个微服务使用消息总线彼此连接。
本质上,事件驱动的架构提供了我们需要的流解决方案的所有功能。基于云服务商提供的托管服务实现事件驱动架构,就可以构建无服务器的流解决方案。
对于上述模式,如果你将托管服务(如AWS Lambda)作为微服务,AWS Kinesis作为消息总线,就可以使用无服务器技术栈实现事件驱动的架构。
我们将整个架构分为三层——源、接收(Sink )和处理。
下面是从消息驱动架构到AWS服务的映射。
在上图中,你可能会觉得有很多重复动作,特别是从Lambda到从Kinesis写/读的动作。你可以发挥创造力,针对重复的功能构建某种类型的库。
在Capital One,我们正是这样做的。我们构建了内部SDK来抽象重复任务。SDK有以下特点:
正如我们前面所讨论的,任何无服务器流解决方案都需要解决伸缩、节流、重用、容错和监控等问题。这是如何实现的呢?
这种架构模式与天生可伸缩的云和服务相结合,让这一切成为可能。
限流的基本功能是,如果你的输入请求速率远远高于下游所能支持的速率,则需要保存你的请求。在这里,消息总线持久化特性能帮助我们,因为你只能选择一次可以处理的消息数量,并保存其他消息。
例如:如果你使用Kinesis作为消息总线,则能指定你在函数中处理的批次大小。
如果我们可以构建source微服务和sink微服务,让它们不具有任何业务功能,并且是基于配置的,那么就可以多个团队都使用它们来消费事件。
例如:如果你能构建源函数来消费来自Kafka的事件,这些事件可以对主题名称、代理地址等进行配置,那么任何团队都可以根据需要使用该函数并将其部署到他们的栈中,而无需更改任何代码。
以上可以帮助我们实现代码级的重用。另一种重用是流本身的重用。如果你为自己架构选择的消息总线是基于发布/订阅的总线,那么你能有多个订阅者来访问相同事件。例如:你可以将事件fan out到两个微服务,而无需单独编写额外代码。
同样,消息总线在这里也可以对我们提供帮助。考虑一下,如果你的后端服务出现错误,你可以将所有/失败的消息保存到消息总线中,然后重试,直到后端调用开始成功。
作为SDK的一部分,记录元数据有效负载能帮助我们实现跨不同功能的日志一致性。你还可以构建一个可重用的函数,该函数能将你的日志转发到首选的监控解决方案。
这听起来就像说无服务器流解决方案是银弹,我不需要Spark
并非如此。Apache Spark是一个分布式计算平台,在大规模分布式数据处理负载上表现出色。当涉及高容量计算和批处理时,数据和计算功能可以采用分布式,并且能并行执行,Spark仍然是首选工具。典型例子包括机器学习用例的重量级计算需求,涉及几百个文件的Map/Reduce范式,或者处理PB级数据的长时间运行的进程等等。Spark也能作为实时流领域的首选工具,但前提是流量非常大,每秒执行数十万个事务。
在Capital One,我们使用多种多样的大数据工程工具。在我的团队中,我使用了无服务器流来处理大容量的用例,比如根据每秒数千个事件的客户交易生成有意义的警报,以及处理每秒数十个事件的小容量用例,比如补卡。我还使用Spark来处理大型交易文件,使用机器学习模型生成客户的消费档案。这完全取决于具体的需要。
英文原文:
Scaling to Billions of Requests-The Serverless Way at Capital One
领取专属 10元无门槛券
私享最新 技术干货