Apache Pulsar 最初诞生于雅虎,当时就是为了解决雅虎内部各个部门之间数据的协调,所以多租户特性显得至关重用,Pulsar 从诞生之日起就考虑到多租户这一特性,并在后续的实现过程中,将其不断的完善。 多租户这一特性,使得各个部门之间可以共享同一份数据,不用单独部署独立的系统来操作数据,很好的保证了各部门间数据一致性的问题,同时简化维护成本。
在介绍 Pulsar 多租户之前,先来看一下,正常一个系统要实现一个多租户需要做哪些事情:
- 严格的 SLAs 保证
- 确保租户之间的隔离性
- 允许对租户内的资源进行配额
- 在租户内提供系统级别的安全性
- 运维成本低,易管理
Pulsar 的多租户设计符合上述要求:
- 使用身份验证、授权和 ACL(访问控制列表)确保其安全性
- 为每个租户强制执行存储配额
- 支持在运行时更改隔离机制,从而实现操作成本低和管理简单。
多租户介绍
如下图所示:在一个 Pulsar 集群中,有三个概念:tenant(图中的 property 是之前的一种叫法,现在更习惯将其称为:tenant)、namespace、topic。这一点从 pulsar 中 topic 的命名组成也可以反应出来,例如:persistent://tenant/namespace/topic
,在 Pulsar 中,使用 tenant
,namespace
,topic-name
来唯一标识一个 topic。
tenant 代表的是租户名,它是一个资源的隔离单位,一个 tenant 下可以有多个 namespace。namespace 用来管理其下面所属的 topics,可以在 namespace 级别给 topic 设置相应的策略,比如 retention,backlog,ratelimit。一个 namespace 下又可以有多个 topic,他们的权限大小也是由上到下。Pulsar 的多租户通过 tenant、namespace、topic 形成多级管理系统。
安全性(认证和授权)
开篇中提到,一个多租户系统需要在租户内提供系统级别的安全性,细分来讲,主要可以归类为一下两点:
- 租户只能访问它有权限访问的 topics
- 不允许访问它无法访问的 topics
在 Pulsar 中,多租户的安全性是通过身份验证和授权机制实现的。当 client 连接到 pulsar broker 时,broker 会使用身份验证插件来验证此客户端的身份,然后为其分配一个 string 类型的 role token。role token 主要有如下作用:
- 判断 client 是否有对 topics 进行生产或消费消息的权限
- 管理租户属性的配置
Pulsar 目前支持两种身份认证:
- TLS 客户端身份认证
- 雅虎的身份认证系统:Athenz
当然,用户也可以使用自己实现的身份认证程序。
当身份认证系统识别出客户端的 role token 之后,Pulsar broker 会使用授权系统来告诉客户端当前你可以执行哪些操作。授权操作是在 tenant
级别进行配置的,这意味着在 Pulsar 集群中,允许用户根据不同的角色设定多个授权方案。具体的权限操作是在 namespace 级别进行设置和管理的,例如:针对某一个 topic 是否具有 produce 或 consume 的权限归属于 namespace 这个级别来控制。换句话来说:在 tenant 级别
用户可以配置,什么样的 role 拥有对哪些 tenant 操作的权限,在 namespace
级别用户可以配置,针对某一 topic 当前role拥有什么样的权限,又回到了开头所介绍的,namespace 主要用来管理它所包含的 topics 的属性。
通过认证和授权系统巧妙的将租户与 topics 之间的执行权限等问题剥离开来,从而实现在租户内满足系统级别安全性的目的。
隔离性
隔离性主要分为如下两种:
- 软隔离:通过磁盘配额,流量控制和限制等手段
- 硬隔离
软隔离
在 Pulsar 中,为了保证良好的扩展性,采用了存储和计算分离的架构设计,而存储(bookie)和计算 (broker)又是 produce 和 consume 所共享的资源,为了更好的实现隔离性,Pulsar 分别在存储和计算层做了不同的处理。
存储
在存储方面,Apache Pulsar 使用了另外一个 Apache 的顶级项目 Bookkeeper 来作为其存储层。bookie 是 Bookkeeper 中的一个实例,一个 bookie 拥有多个 ledgers,每个 ledger 对应 Pulsar 中的一个 topic。Bookkeeper 本身的 I/O 分离 能够很好的为此做支撑。在单个 bookie 中,有一个 journal 日志文件(一般有一块专有的 journal 盘用于 journal 文件的写入)(类似于 LSMTree 中的 WAL 文件)会以 append 的形式将所有的写操作追加写入其内部。当写入完成之后,后台会有一个单独的线程来定期将 journal 文件的数据 flush 到磁盘中。这种 I/O 架构的设计实现了写入和读取操作之间的隔离,这样租户可以尽可能快的读取数据,同时写的吞吐量和延迟不会受到读取操作的影响。
除了 I/O 隔离外,不同的租户可以为不同的 namespace 配置不同的存储配额。如果租户内消息的大小达到了存储配额的限制,Pulsar 会采取相应的措施,例如:阻止消息生成,抛出异常或丢弃消息等。
broker
为了满足 SLAs, Pulsar 在 Broker 层面也提供了多种机制。首先,在 Pulsar broker 中的一切操作都是异步进行的。每个 Broker 使用的内存资源是有上限的,当 Broker 达到配置的 CPU 或内存使用的阈值,Pulsar 会迅速的将流量转移到负载较小的 Broker 上来处理。在 Pulsar 中,有一个组件 load manager component 专门用来处理这种情况,这受益于 Pulsar 优秀的架构设计:计算与存储分离。这使得 Broker 近乎是无状态的,Broker 本身不存储任何数据,所以这种流量负载的转移成本很小而且速度很快,所以在这里并不需要担心流量负载时租户的特性是否会打折扣。
其次,在消息的生产和消费方面,Pulsar 都做了流量控制。在生产者方面 ,租户可以配置当前消息发送到 broker 和 bookies 的速度,避免当前用户发送消息的速度大于系统本身处理消息的能力。在消费者方面,租户可以配置当前 broker 可以给 consumer 发送多少未完成的消息。除此之外,Pulsar 还可以限制 Broker 以指定的速率向消费者投递消息,避免消费者的消费能力大于当前 Broker 的处理能力。
硬隔离
软隔离中提到的机制可以确保 Pulsar 在共享 Broker 和 Bookies 资源时能很好的保证隔离性。但是,在某些情况下,应用程序也需要物理资源隔离。Pulsar 允许将某些租户或名称空间与特定 Broker 进行隔离。这可确保这些租户或命名空间可以充分利用该特定 Broker 上的资源。
此选项还可用于测试不同的配置,调试并快速响应生产中发生的任何意外情况。例如,当前有一个用户可能在 Broker 触发可能影响其他租户性能的不良行为。在这种情况下,可以将该特定租户物理隔离出来,然后进行确认或者修复。
除了物理隔离 Broker 上的流量之外,你还可以隔离存储相关的业务。可以在 namespace 级别来配置相关的 placement policy 来达到目的。