在过去的几年里,Airbnb工程从一个整体红宝石上搬到了铁路架构上的一个面向服务的架构。在我们的Rails架构中,我们拥有每个资源的API来访问底层数据。这些API有权检查以保护敏感数据。由于有一种方法来访问资源的数据,因此管理这些检查很容易。在向SOA的转换中,我们移动到分层架构,其中有些数据服务包括从多个数据服务中保湿的数据库和演示服务。将许可检查从单块子移动到SOA的初始方法是将这些检查转移到演示服务。然而,这导致了几个问题:
重复且难以管理授权检查:通常提供对同一底层数据的访问的多个演示服务具有授权检查的重复代码。在某些情况下,这些检查变得不同步并且难以管理。
扇出多种服务:大多数这些授权检查需要调用其他服务。这很慢,负载难以维持,并影响整体性能和可靠性。
我们将授权检查移动到数据服务,而不是仅在演示服务中执行授权检查。这有助于我们减轻了复制和不一致的检查问题。
我们创建了基于桑给巴尔的集中授权系统,从数据层调用了HiMeji。它存储权限数据并执行检查作为中央源的源。当资源突变时,我们在读取时间写下所有权限数据而不是扇动。考虑到我们的读取沉重的工作量,我们在写入写作而不是读取。
HiMeji公开检查API以进行数据服务以执行授权检查。 API签名如下:
权限检查将如下所示,它的状态“可以用户123写入列出10的描述?”:
这是由HiMeji解释的,因为声明“是可以写入列出10的描述中的用户123中的用户123?”。
与Zanzibar类似,HIMEJI的基本存储单元是表单实体#relation @主体中的元组。
实体是三重(实体类型:实体ID:实体部分);这来自自然语言方法:列表:10:描述→“清单10的描述”。
关系描述了关系,如所有者,读写,但可以是特定的使用情况;有些示例包括用于拒绝访问列表的预留和拒绝访问的主机的主机。
校长是经过身份验证的用户身份,如用户(123),或者其他实体,如引用(清单:15)。
如果我们不得不为检查的每个确切许可编写元组,则数据和非规范化的量将是指数增长的。例如,我们必须在列表中写入:10#Write @ User(123)和清单:10#读取@ User(123)对于列表所有者能够读写。
基于Zanzibar配置,我们使用基于yaml的配置语言,允许通过Set Algebra解决权限检查,允许开发人员映射到集合操作:
假设用户123是清单10的所有者。然后,数据库将具有元组列表:10#所有者@用户(123)。
当我们请求检查时(实体:"列表:10"关系:写入,Userid:123),HiMeji解释列表#作为读取的联盟读取。写作和过境列出#写作写作&所有者。因此,它将从其数据库中获取以下内容,其中任何属于列表#写入的匹配项
查询列表:10#WRITE @ USER(123)=>空查询列表:10#所有者@ User(123)=>匹配用户(123)
例如,用户123只需要列表:10#所有者@用户(123)在列表中:10#写入集。
我们观察到,由于其存在,Airbnb的实体经常批准对其他实体的访问。例如,预订的客人可以访问列表的位置,以及列表的其他信息。我们代表这个用例用一个元组,其中校长是对实体的引用,即列表:$ ID#预留@参考(预留:$预留)。这允许我们表达在列表中的“预留”集中的“客户”设置中的用户在列表中的预留中的概念在列表中:Location#Lead Set,最大限度地减少需要存储的数据量:
清单:地点:'#read&#39 ;:联盟: - #owner - 列表:$ ID#预订@参考(预订:$ reservationId#guest)
从桑给巴尔不同的这种方法不同,这种元组不包含主体中的关系(即参考(预订:$ ID#guest))。引用实体之后的关系是静态的并从配置中检索。采用上市示例然后检查其他用例,我们发现通常将遵循多个关系。在我们的产品中,在两个实体类型之间使用的关系集没有方差;该集合的变化意味着产品更改并应用于所有实体类型。如果两个实体类型之间的关系集(即引用(预留:$ ID#guest),参考(预留:$ ID #Cotraveller),参考(预留:$ ID#Booker),......)具有尺寸m,编写元组对于这些导致N * M元组。通过将关系拉到配置中,我们将存储数据的大小减少到N.
清单:10#所有者@ User(123)列表:10#预订@参考(预订:500)预订:500#guest @ User(456)
然后基于配置,HIMYJI基于来自请求的信息和上述配置发出第一个DB获取:
查询列表:10#Reservation =>匹配参考(预留:500)查询列表:10#所有者@ User(456)=>空的
HiMeji将发出第2个DB获取,替换在找到的预留的ID中,其中匹配指示用户456在允许读取列表10位置的用户集中。
Orchestration层根据配置逻辑接收来自客户端的请求,并负责向数据发出获取数据,并解析结果。 Orchestration层与一致散列的缓存层路由到缓存层。
缓存层被分析和复制(每个碎片每个AZ的一个实例),负责在未命中的数据库中过滤内存和重复数据库。每个碎片都会通过一致散列分配一组数据。我们针对缓存中的达到98%的击中率。
我们对HiMeji的最重要的变化,Zanzibar的设置是:
从缓存层分开请求编排层,以便在不重新启动缓存的情况下更轻松地更新编排层。
使用Amazon Aurora进行数据库存储,作为我们云旅程的一部分,与桑给巴尔的扳手不同。
我们实现了与Zanzibar的相同可靠性(对冲,分层缓存)和负载脱落功能以进行可用性。
HiMeji一直在生产的支票大约一年,其吞吐量从3月2020年3月20日到850K实体/秒的扩大量,同时在去年的情况下保持其可用性和延迟目标:
为了减少集成时间和和驱动开发人员采用,我们建立了一些工具,如:
基于配置的回填:将现有的权限检查迁移到HIMYJI中,要求我们回填现有实体的权限元组。而不是构建自己的回填流的每个数据服务所有者,我们基于Apache Airflow和Apache Spark建立了一款通用解决方案。服务所有者必须只提供一个小型配置,指示他们的元组应该如何由其数据库导出形成。
自动代码生成:要使船舶更容易,我们为自动生成Java和Scala代码提供了脚本。
厚客户端:我们提供了一个厚厚的HTTP客户端,具有记录,度量标准和迁移卷展栏控件。
UI工具用于调试和一次性任务:调查一次性权限问题可以繁琐,并且需要检查在系统中写入的权限数据,因此我们构建了一个UI来分析数据并修复权限问题。
基于桑给巴尔的HiMeji授权系统统一授权数据和Airbnb的逻辑。在引言之前,难以保持逻辑缺陷逻辑的一致性和性能。 HiMeji利用一个简单的数据模型,具有灵活的逻辑配置来集中所有产品和数据授权。 HiMeji扩展了桑给巴尔的可扩展性和性能属性,并通过其高击中率分层分布式缓存降低延迟。所有这些在一起结果在HiMeji存储了数十亿的关系,并在维持低延迟和高可用性的同时提供近一百万个实体授权。
HiMeji通过Airbnb团队中许多成员的贡献成为可能。我们感谢上次和当前的团队成员 - Max Burkhardt,Alex Rosenblatt,Jefferson Lee,Divya Gupta,Clare Liu,Houkun Li,Leelakrishna Nukala,Karen Kim,Gary Leung,Ryan Flood,Tony Tran和Gurer Kiratli。额外归功于我们的当前和以前的管理层非常支持这项工作 - Anish Das Sarma,Vijaya Kaza,Jason Sobel,Bipin Suresh,Marc Blanchou,Raymie Stata和Aristotle Balogh。
这项工作,以及许多令人兴奋的事情总是在Airbnb上发生。如果您想加入我们,请查看我们的Airbnb职业页面。
“Rails”和“Ruby On Rails”是David Heinemeier Hansson的注册商标。
Apache Kafka,Apache Airflow,Apache Spark和Apache是美国和/或其他国家Apache软件基金会的注册商标或商标。
AWS和Amazon Aurora是美国和/或其他国家的Amazon.com,Inc。或其附属公司的商标。 所有商标都是其各自所有者的属性。 任何使用这些都是用于识别目的,并不意味着赞助或认可。 媒体是一个开放的平台,17亿读者来寻找有洞察力和动态的思维。 在这里,专家和未被发现的声音相似地潜入任何主题的核心,并将新的想法带到表面上。 学到更多 遵循对您有关的作家,出版物和主题,您将在您的主页和收件箱中看到它们。 探索 如果您有一个故事来讲述,知识分享,或者提供提供的视角 - 欢迎回家。 很容易和免费发布您对任何主题的思考。 写下媒介