【51CTO.com快译】近来,Urban Airship受到期望与移动技术一同增长的成千上万家公司的信赖。Urban Airship是一家成立七年的SaaS公司,它采用了一种免费增值的商业模式。
Urban Airship现在每天发送的推送通知平均超过10亿个。本文介绍了2016年美国大选期间Urban Airship的通知使用情况,探讨其Core Delivery Pipeline系统架构,该系统为新闻媒体发送了数十亿的实时通知。
2016年美国大选
在选举日前后的24小时内,Urban Airship发送了25亿个通知――这是其迄今为止***日发送量。这相当于美国每人收到8个通知或世界上每部活跃智能手机收到1个通知。虽然Urban Airship支持45000多个应用程序,覆盖每个行业垂直领域,但是分析选举使用数据后显示,400多个媒体应用程序在这创记录的发送量中占到了60%,大家在跟踪报告选举结果时,它在单单一天内就发送了15亿个通知。
总统选举结束时,通知量趋于稳定,并达到峰值。
Urban Airship API的HTTPS入站流量在选举期间达到每秒近75K的峰值。大部分流量来自与Urban Airship API通信的Urban Airship软件开发工具包(SDK)。
推送通知量一直在快速增长。最近的主要驱动因素是英国退欧、奥运会和美国大选。2016年10月的月通知量同比猛增了150%。
Core Delivery Pipeline 架构解密
Core Delivery Pipeline(CDP)是Urban Airship的核心系统,负责利用audience selector工具来确定设备地址,并发送通知。其发送的所有通知都要做到低延迟,无论它们同时推送到数千万用户,发送给多个复杂的子群,含有个性化内容,还是其他什么。下面概述了这套系统的架构以及该公司汲取的若干经验。
他们是如何开始入手的
2009 年,CDP最初只是一个Web应用程序,一些worker模块转变成了面向服务的架构(SOA)。由于旧系统的一些部分开始遇到规模问题后,我们将它们提取为一个或多个新服务,这些服务旨在提供同样的功能集,但是支持更大的规模,并拥有更好的性能。我们的许多原始API和worker是用Python编写的,将它们提取为高并发的Java服务。最初,是将设备数据存储在一组Postgres数据库分片中,但公司规模的增长速度超过了添加新数据库分片的能力,于是系统迁移到了使用HBase和Cassandra的多数据库架构。
CDP 是一系列负责处理分段和推送通知的服务。这些服务提供了响应请求的同一种类型的数据,但出于性能原因,每个服务都以全然不同的方式索引该数据。比如,我们有一个系统负责处理广播消息,将同样的通知内容推送到已向相关应用程序注册的每个设备。该服务及底层的数据存储区其设计方式与我们拥有的,负责基于位置或用户概况属性来发送通知的服务全然不同。
我们把任何长时间运行的进程都看作服务。这些长时间运行的进程密切关注一个通用模板,涉及度量指标、配置和日志,以便易于部署和操作。通常,我们的服务属于RPC服务或消费者服务这两个组中的一个。RPC服务提供这些使用内部库与服务同步交互的命令,非常类似GRPC,而消费者服务处理来自Kafka数据流的消息,并对那些消息执行针对特定服务的操作。
为了满足我们的性能和规模要求,我们高度依赖HBase和Cassandra以满足数据存储要求。虽然HBase和Cassandra都是列式NoSQL存储库系统,但是它们有着全然不同的取舍,影响着我们使用哪种存储系统、派什么用场。
HBase非常擅长高吞吐量扫描,响应的预期基数(cardinality)非常高,而Cassandra擅长较低基数的查询,预计响应只含有少数结果。两者都允许大量的写入吞吐量,这是我们的一个要求,因为来自用户手机的所有元数据更新都是实时的。
它们的故障特点也不一样。一旦遇到失败,HBase倾向于一致性和分区容错性,而Cassandra倾向于可用性和分区容错性。每个CDP服务都有非常具体的使用场合,因此有一种非常专用的数据库模式(schema),旨在便于所需的访问模式以及限制存储占用空间。一般说来,每个数据库仅由单个服务来访问,该服务负责通过一个不大专用的接口,满足数据库访问其他服务的要求。
服务及其后端数据库之间实现这种1:1的关系有许多优点:
·通过将服务的后端数据存储区当作一个实现细节,而不是共享资源,我们获得了灵活性。
·我们只要改变服务的代码,就可以调整该服务的数据模型。
·使用情况跟踪起来更简单直观,因而容量规划来得更容易。
·故障排除更容易。 有时问题出在服务代码上,有时问题出在后端数据库上。让服务和数据库成为一个逻辑单元极大地简化了故障排除过程。我们没必要搞清楚“还有谁可以访问这个数据库,让它以这种方式来运行?”相反,我们可以依赖来自服务本身的应用程序层度量指标,只要为一组访问模式操心。
·由于只有一个服务与数据库交互,我们可以执行几乎所有的维护活动,而不会停机。 繁重的维护任务成为服务级别问题:数据修复、数据库模式迁移甚至改用完全不同的数据库,这些都可以在不中断服务的前提下执行。
诚然,我们将应用程序分解为较小的服务时,性能方面可能会有一些下降。然而我们发现,我们可灵活地满足高扩展性和高可用性方面的要求,性能下降一点也是完全值得的。
我们的服务大多数处理同样的数据,只是它们使用不同的格式。一切都要保持一致性。为了保持所有这些服务的数据更新,我们高度依赖Kafka。Kafka速度极快,也很牢靠。速度快带来了某些缺点。只可保证Kafka消息至少发送一次,但不可保证它们按顺序到达。
我们如何处理这个问题呢?我们将所有可变路径建模为可交换的:操作可以按任何顺序来进行,***获得同样的结果。它们也是幂等的。这有一个很好的附带影响:我们可以重放Kafka数据流,进行一次性的数据修复工作、回填甚至迁移。
为此,我们充分利用了“单元版本”概念,HBase和Cassandra中都有这个概念。它通常是一个时间戳,但也可以是你喜欢的任何数字(有一些例外;比如MAX_LONG会导致一些奇怪的行为,这取决于HBase或Cassandra的版本以及你的数据库模式如何处理删除)。
对我们来说,这些单元的一般规则是,它们可以有多个版本,我们按照提供的时间戳对版本进行排序。考虑到这种行为,我们可以将入站消息分解为一组特定的列,并将该布局与自定义的应用程序逻辑合并起来,用于逻辑删除,并考虑到了时间戳。这样就可以对底层数据存储区进行盲写,同时保持数据的完整性。
将变化的部分盲写到Cassandra和HBase并非没有问题。一个典型例子就是在重放的情况下,重复写入同样数据。虽然由于我们努力确保记录具有幂等性,数据状态因而不会改变,但是重复数据必须压缩去掉。在最极端的情况下,这些额外记录可能导致显著的压缩延迟和备份。由于这个细节,我们密切关注压缩时间和队列深度,因为在 Cassandra和HBase中,压缩操作都会导致严重问题。
通过确保来自数据流的消息遵循一系列严格的规则,并且设计的消费服务能够处理无序和重复的消息,我们就可以确保大量的异步服务同步,更新时只有一两秒的滞后。
我们的大多数服务是用Java编写的,但采用了一种非常独特而现代的风格。 我们在设计Java服务时考虑到了一系列基本的指导原则:
·只做一件事,并且把它做好 ――设计服务时,它应该只有一项责任。实施者决定这是什么样的责任,但是她或他需要准备好在代码审查时予以说明。
·没有共享操作状态 ――设计服务时,假设总是至少有三个实例在运行。服务需要能够处理其他任何实例能够处理的同一个请求,没有任何外部协调。熟悉Kafka的那些人会特别指出,Kafka消费者从外部协调topic:group对的分区所有权。这个指导原则面向针对特定服务的外部协调,而不是利用可能从外部协调的库或客户端。
·限制队列 ――我们在所有服务中使用队列,这是分批处理请求的好方法。所有队列都应该有限制。 不过,限制队列确实带来了许多问题:
△队列满时生产者会发生什么?它们会阻塞吗?例外处理?还是丢弃?
△我的队列应该多大?要回答这个问题,假设队列总是满的有所帮助。
△如何干净地关闭队列?
△针对这些问题,每个服务会给出不同的答案,这取决于具体的使用场合。
·命名自定义线程池,并注册UncaughtExceptionHandler ――如果我们***创建自己的线程池,我们使用来自Executors的构造函数或帮助方法,让我们得以提供ThreadFactory。有了这个ThreadFactory,我们就可以正确命名线程,设置守护进程状态,并注册UncaughtExceptionHandler来处理异常让它进入到堆栈顶部的情况。这些措施将大大简化服务的调试,并且让我们不必在深夜备感沮丧。
·青睐不可变数据对象,而不是可变状态 ――在高度并发环境中,可变状态可能很危险。一般来说,我们使用可以在内部子系统和队列之间传递的不可变数据对象。让不可变对象成为子系统之间的主要通信形式,这样一来,为并发使用进行设计要直观简单得多,还让故障排除更容易。
下一步何去何从?
由于Urban Airship能够通过移动钱包passes发送通知,新增对Web通知和苹果新闻通知的支持,及其开放频道能够将通知发送到任何平台、设备或营销渠道,我们预计通知推送量会呈指数增长。为了满足这种需求,我们继续大力投资于Core Delivery Pipeline架构、服务、数据库和基础设施。
原文标题:How Urban Airship Scaled To 2.5 Billion Notifications During The U.S. Election,作者:Todd Hoff
【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】