学习笔记|Pulsar-概念与结构

Apache Pulsar是一个高性能的多用户消息传递系统,支持跨集群复制、低延迟发布、百万主题扩展及多种编程语言的客户端API。它基于pub-sub模式,消息持久化由BookKeeper实现,提供多租户、多种订阅模式和无服务器连接器框架Pulsar IO。Pulsar具有同步/异步生产与消费模式,支持消息压缩和分区topic。消费者可通过单条或累积确认处理消息,处理失败时可触发重试机制。此外,Pulsar还提供了死信主题和多租户管理。
摘要由CSDN通过智能技术生成

该篇文章是Pulsar官方文档第二节的翻译和笔记
内容、图片来源:http://pulsar.apache.org/docs/en/concepts-overview/

概述

Pulsar是一个多用户、高性能的服务器到服务器的消息传递解决方案,主要特点如下:
• 本地支持多个集群跨集群无缝复制消息
• 发布延迟低
• 可无缝扩展到百万个主题
• 客户端API支持Java、Go、Python和C++
• Topic支持多种订阅模式
• 使用BookKeeper提供的持久性消息存储保证消息传递
• 基于Pulsar函数的无服务器连接器框架Pulsar IO使数据进出Apache Pulsar更加容易
• 对数据进行分层存储

消息传递

Pulsar基于publish-subscribe(pub-sub)模式,生产者将消息发布到Topic,消费者可以订阅这些主题处理消息,并在处理完成后发送确认消息
在这里插入图片描述
消息的持久化是通过BookKeeper实现的,一旦创建了订阅关系,Pulsar将保留所有的消息(即使消费者断开了链接),只有当消费者确认已经成功处理保留的消息时,才会将这些消息丢弃消息。消息的保留分为两种:1.在保留策略内的消息即使消费者已发送了确认也可以持久地存储在Pulsar中,保留策略未涵盖的已确认消息将被删除,如果没有保留策略所有已确认的消息都将被删除;2.设置消息到期时间,会根据应用于namespace的TTL过期时间,如果到期了,即使消息没有被确认也会被删除
当有某条消息被重复发送时,可以选择两种持久化策略,1是将重复的消息也持久化到BookKeeper中,2是判断如果是重复消息,则不再进行持久化操作
在这里插入图片描述

生产

生产者将消息发布到Topic上,发送消息分可为同步发送和异步发送两种模式:
同步发送:生产者每次发送完消息后需要等到broker的ack确认,如果没有接收到确认信息生产者就认为发送消息失败
异步发送:生产者将消息放入到阻塞队列中就直接返回。Pulsar的客户端通过后台线程将消息发送给broker,如果队列满了,生产者再放入消息时会被告知推送失败
生产者发布消息在传输过程中会对数据进行压缩,目前Pulsar支持的压缩方式有LZ4,ZLIB, ZSTD, SNAPPY。如果启用了批处理,生产者将在单个请求中累积一批消息进行发送,批处理大小可以由最大消息数和最大发布延迟定义

消费

消费者从Topic上接收消息进行数据处理,同样,消息接收也分为同步接收和异步接收两种模式:
同步接收:同步接收一直处于阻塞状态,直到有消息传入
异步接收:异步接收立即返回一个未来值,一旦有新消息,它就直接完成,类似于Java中的completablefuture
当消费者成功处理完一条消息后,会发送一个确认请求给broker,告诉broker可以删除这条消息了,否则broker会一直存储这条消息。消息可以逐个确认也可以累积确认,消费者只需要确认收到的最后一条消息,这个流中所涉及到的所有消息都不会再重新传递给这个消费者(备注:因为共享模式涉及多个消费者同时订阅用一个Topic,所以累计确认不能与共享订阅模式一起使用,在共享订阅模式下只能单条确认消息)。
当消费者处理消息失败时,会给broker发送一个失败确认,这个时候broker就会给消费者重新发送这条消息,失败确认可以逐条发送,也可以累积发送,这取决于消费订阅模式。在exclusive和failover订阅模式中,消费者只会对收到的最后一条消息进行失败确认。在Pulsar客户端可以通过设置timeout的方式触发broker自动重新传递消息,如果在timeout范围内消费者都没有发送确认请求,那么broker就会自动重新发送这条消息给消费者。
如果某条消息一直处理失败就会触发broker一直重发这条消息给消费者,使消费者无法处理其他消息,Dead letter topic机制可以让消费者在无法成功消费某些消息时接收新的消息进行消费,在这种机制下,无法消费的消息存储在一个单独的topic中(Dead letter topic),用户可以决定如何处理这个topic中的消息。

Topic

与其他pub-sub系统一样,Pulsar中的topic被命名为从生产者向消费者传输消息的通道:

{persistent|non-persistent}://tenant/namespace/topic

在这里插入图片描述
用户不需要在Pulsar中明确地创建主题,如果客户端尝试往不存在的主题中写入/接收信息,Pulsar将在topic提供的namespace下自动创建该主题
消费者可以订阅多个topic:
通过名称配置:persistent://public/default/finance-.*
配置topic订阅列表
常规topic只能由单个broker提供,这限制了topic的最大吞吐量,分区topic是由多个broker处理的一种特殊类型的topic,它允许更高的吞吐量。分区topic和普通topic在订阅模式的工作方式上没有区别,在创建主题时可以指定分区数。
在这里插入图片描述
消息发布到分区topic时,需要指定路由模式,路由模式决定每个消息应该发布到哪个内部topic:
RoundRobinPartition:如果没有key,生产者循环在所有分区之间发布消息以实现最大吞吐量,如果有key,则生产者对key进行hash处理分配给给特定的分区(默认模式)
SinglePartition:如果没有key,生产者随机选择一个分区将所有消息发布到该分区中,如果有key,则生产者对key进行hash处理分配给给特定的分区
CustomPartition:调用自定义消息路由器来确定消息发送到哪个分区

订阅模式

Pulsar具有exclusive,shared,failover三种订阅模式
exclusive模式:一个topic只允许一个消费者订阅,否者会报错
failover模式:多个消费者订阅同一个topic,按照消费者名称进行排序,第一个消费者时唯一接收到消息的消费者(主消费者),当主消费者断开连接时,所有的后续消息都将发给下一个消费者
shared模式:多个消费者订阅同一个topic,消息在消费者之间以循环的方式发送,并且给定的某条消息只能发送给一个消费者,当消费者断开连接时,所有发送给它但没有确认的消息将重新安排发送给其他消费者
key_shared模式:多个消费者订阅同一个topic,消息以分布方式在消费者之间传递(<key, value>),具有相同key的消息传递给同一个消费者,当这个消费者断开连接时,将导致key对应的消费者更改

体系结构

在最高级别中,一个Pulsar实例有一个或多个Pulsar集群组成,实例中的集群可以彼此复制数据。在Pulsar集群中,一个或多个broker处理和加载来自生产者传入的消息,将消息发送给消费者,与Pulsar配置存储通信以处理各种协调任务,Pulsar集群架构如下所示,包括一个或多个broker,用于集群级配置和协调的Zookeeper,用于持久存储消息的BookKeeper,集群可以使用地理复制在集群间进行复制
在这里插入图片描述
Broker是一个无状态组件,主要有两部分组成:HTTP服务器,想生产者和消费者公开,用于管理任务和topic查找端的REST API;调度程序,异步TCP服务器,通过用于所有数据传输的自定义二进制协议
每个集群都有自己的本地Zookeeper用于存储集群特定的配置和协调,如所有权元数据、代理加载报告、簿记员分类帐元数据等等。
Pulsar使用BookKeeper进行持久消息存储,BookKeeper是一个分布式预写日志(WAL)系统,它的优势为:
• 使Pulsar利用多个独立日志,成为ledgers,随着时间推移,可以为topic创建多个ledger
• 为处复制的顺序数据提供了非常有效的存储
• 保证在出现各种系统故障时ledger的读取一致性
• 提供多个Bookies的I/O分布
• 在容量和吞吐量方面都是水平扩展的,可以通过向集群中添加更多的bookies来增加容量
• Bookies用于处理数千个同事读写的ledger,通过使用多个磁盘设备(一个用于日志,一个用于存储),Bookies能够将读写操作的延迟隔离开
• 除了消息数据外,消费者的订阅位置cursor也可以持久地存储在BookKeeper中

Pulsar proxy

Pulsar客户端和集群进行交互时的一种方法是直接连接到broker,然而在某些情况下是不能直接连接的,Pulsar proxy充当集群中所有broker的单一网关来解决这个问题

$ bin/pulsar proxy \
  --zookeeper-servers zk-0,zk-1,zk-2 \
  --configuration-store-servers zk-0,zk-1,zk-2

使用Pulsar Proxy不需要客户端提供任何特定的配置,只需要更新用于服务URL的IP

客户端

Pulsar为Java、Go、Python、和C++提供了client API,API优化和封装了client-broker通信协议,公开了一个简单直观的API供应用程序使用
当应用程序想要创建生产者/消费者时,Pulsar client的设置如下:
1.客户端尝试通过向broker发送HTTP请求来确定topic的所有者,请求可以到达一个活跃的broker,通过查看Zookeeper(缓存的)元数据,broker确定topic在为谁服务,如果没有,将尝试把topic分配个负载最小的broker
2.client拥有broker地址后,它将创建一个TCP链接并对其进行身份验证,当TCP断开时,客户端将立即重新启动此设置阶段并继续尝试使用指数后退来重新建立生产者/消费者,直到操作成功

在Pulsar中,标准的消费者包括监听topic,处理消息,发送确认请求。无论何时创建新的定于,它最初都定位在topic的末尾(默认)从随后创建的第一条消息开始读取。当消费者使用预先存在的订阅链接到某个主题时,会从该订阅中未确认的最早的消息开始读取,Pulsar会自动管理订阅光标响应消息确认。Pulsar的reader接口可以让应用程序手动管理光标,允许从topic中最早可用消息或者最新可用消息开始读取。如果从其他消息开始读取,需要显式地提供消息ID
在这里插入图片描述
从最早的消息开始:

Reader<byte[]> reader = pulsarClient.newReader()
    .topic("reader-api-test")
    .startMessageId(MessageId.earliest)
    .create();

从最晚的消息开始:

Reader<byte[]> reader = pulsarClient.newReader()
    .topic(topic)
    .startMessageId(MessageId.latest)
    .create();

从之间指定的消息开始:

byte[] msgIdBytes = // Some byte array
MessageId id = MessageId.fromByteArray(msgIdBytes);
Reader<byte[]> reader = pulsarClient.newReader()
    .topic(topic)
    .startMessageId(id)
    .create();
多租户模式

Pulsar从一开始就是一个多租户系统。为了支持多租户,Pulsar提出了租户概念。租户可以跨集群分布,并且每个租户都可以有自己的身份验证和授权方案。它们也是管理存储配额、消息TTL和隔离策略的管理单元
pulsar的多租户特性主要体现在主题URL中,其结构如下:

persistent://tenant/namespace/topic

tenant和namespace是pulsar支持多租户的两个关键概念,Pulsar为指定租户提供,并为tenant分配适当的容量。namespace是租户内的管理单元命名法。在namespace上设置的配置策略应用于该命名空间中创建的所有topic。tenant可以使用RESTAPI和pulsar admin cli工具通过自我管理创建多个命名空间。例如,具有不同应用程序的tenant可以为每个应用程序创建单独的namespace

Topic压缩

Pulsar是以高度可扩展的消息数据持久存储为主要目标构建的。topic能够在保留消息顺序的同时,根据需要持续存储尽可能多的未确认消息。默认情况下,pulsar存储一个topic上生成的所有未确认/未处理的消息。对于许多pulsar用例来说,在一个topic上积累许多未确认的消息是必要的,但是对于消费者来说,在整个消息日志中“倒带”也是非常耗时的。对于某些用例,消费者不需要topic日志的完整“图像”。它们可能只需要几个值就可以构建一个更“浅”的日志图像,甚至可能只是最新的值。对于这些类型的用例,pulsar提供了topic压缩。当您对一个topic进行压缩时,pulsar将遍历该topic的积压工作,并删除被随后的消息所掩盖的消息,即,它按键遍历该topic,只留下与该键相关联的最新消息。topic压缩具有以下特性:
• 允许通过topic日志更快的“倒带”
• 仅适用于持久topic
• 当积压达到一定大小时自动触发,或者通过命令手动触发
当通过CLI触发topic压缩时,pulsar将从头到尾迭代整个topic。对于遇到的每个key,压缩例程将保存该key最近发生的记录。之后,broker将创建一个新的ledger,并通过topic的每条消息进行第二次迭代。对于每条消息,如果key与key的最新消息匹配,则该key的数据有效负载、消息ID和元数据将写入新创建的ledger。如果key与最新版本不匹配,则将跳过该消息并将其保留。如果任何给定消息的有效负载为空,则将跳过该消息并将其视为已删除(类似于key-value数据库中的逻辑删除概念)。在这个主题的第二次迭代结束时,新创建的ledger被关闭,并且有两件事被写入topic的元数据:ledger ID和最后一条压缩消息的消息ID(这被称为topic的压缩范围)。一旦元数据被写入,压缩就完成了。
在初始压缩操作之后,每当对压缩层和压缩积压进行任何未来更改时,都会通知拥有该主题的pulsar代理。当这些变化发生时:
已启用读压缩的客户端(onsumers 和 readers)将尝试从主题中读取消息,并执行以下操作之一:
消息ID >= 压缩范围:从topic中读取
消息ID < 压缩范围:从压缩范围开始会读取

Schema 注册表

类型安全在任何围绕消息总线(如pulsar)构建的应用程序中都非常重要。生产者和消费者需要某种机制来在主题级别协调类型,以免出现各种各样的潜在问题(例如序列化和反序列化问题)。应用程序通常采用两种基本的消息传递类型安全方法之一:
一种“客户端”方法,在这种方法中,消息生产者和消费者不仅负责对消息(由原始字节组成)进行序列化和反序列化,还负责“知道”通过哪些topic传输哪些类型。生产者和消费者可以发送和接收由原始字节数组组成的消息,并在“带外”的基础上将所有类型安全强制留给应用程序。
一种“服务器端”方法,在这种方法中,生产者和消费者通知系统哪些数据类型可以通过topic传输。通过这种方法,消息传递系统加强了类型安全性,并确保生产者和消费者保持同步。pulsar有一个内置的注册表,允许客户端根据每个topic上传数据模式。这些模式指示哪些数据类型对该主题有效。
这两种方法都可以在Pulsar中使用,可以自由地采用其中一种或另一种,或者根据每个主题进行混合和匹配。使用Schema创建类型化生产者时将自动上传Schema,此外Schema还可以通过pulsar的REST API手动上传、获取和更新
Schema只能在topic级别应用(不能在namespace和tenant级别下应用),Schema的数据结构包括名称(对应的topic)和类型

Producer<SensorReading> producer = client.newProducer(JSONSchema.of(SensorReading.class))
        .topic("sensor-data")
        .sendTimeout(3, TimeUnit.SECONDS)
        .create();

新上传一个Schema :Schema传到broker并存储,使用相同Schema的消费者都可以接收到来自topic的消息
上传的SchemaSchema已经存在:broker确定Schema是兼容的,并已经存储,可以直接使用
已经存在Schema又上传一个新的:broker确定Schema是兼容的,将新Schema存储为当前版本(据用新版本号)
Schema的版本号是连续的,一旦为一个Schema分配了一个版本,该生产者产生的所有后续消息都被标记为这个版本

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值