一、Pulsar简介
Pulsar是一个由Apache软件基金会开发和维护的分布式消息系统,它具有高吞吐量、低延迟和可水平扩展性的特点。
1、历史背景
1.1 起源
Pulsar的起源可以追溯到Yahoo时期,最初是由Yahoo开发的一个内部项目。在处理大规模的实时数据流时,Yahoo团队需要一个可靠、可扩展且高性能的消息传递系统。为了满足这些需求,他们创建了Pulsar。
1.2 发展历程
随着时间的推移,Pulsar逐渐成熟并引起了广泛关注。Yahoo将Pulsar捐赠给Apache软件基金会,成为一个开源项目。这使得更多的开发者和组织能够参与其发展,并且Pulsar得以在更广泛的应用场景中发挥作用。
2、系统比较
2.1 Pulsar与Kafka的比较
-
架构设计:
- Pulsar: Pulsar采用分层架构,分离了存储和计算,允许无缝地扩展。这种设计使Pulsar更容易管理和扩展。
- Kafka: Kafka的架构较为简单,采用分布式发布-订阅模型。存储和计算更为紧密集成。
-
多租户支持:
- Pulsar: Pulsar天生支持多租户,可以在同一集群上隔离多个租户的数据。
- Kafka: Kafka在这方面相对较弱,多租户支持需要一些自定义的工作。
-
消息保留策略:
- Pulsar: Pulsar支持更灵活的消息保留策略,可以根据主题设置不同的保留时间和存储策略。
- Kafka: Kafka对消息保留的策略相对较为简单,通常是基于时间或大小的保留。
-
动态扩展:
- Pulsar: Pulsar允许动态地添加和删除主题,而无需中断生产者和消费者。
- Kafka: Kafka的扩展需要更多的手动干预,可能会导致一些中断。
-
协议支持:
- Pulsar: Pulsar支持多种协议,包括Kafka协议,这使得迁移现有Kafka应用程序到Pulsar更为容易。
- Kafka: Kafka使用自己的协议,与其他系统集成可能需要一些额外的工作。
2.2 Pulsar与RabbitMQ的比较
-
消息模型:
- Pulsar: Pulsar是一种分布式消息系统,适用于高吞吐量和低延迟的场景。
- RabbitMQ: RabbitMQ更倾向于传统的消息队列模型,适用于较轻量级的消息传递。
-
分布式支持:
- Pulsar: Pulsar天生支持分布式架构,可水平扩展。
- RabbitMQ: RabbitMQ在分布式方面相对较弱,需要通过集群来实现高可用性和负载均衡。
-
协议支持:
- Pulsar: Pulsar支持多种协议,包括Kafka协议,更容易与其他系统整合。
- RabbitMQ: RabbitMQ使用AMQP协议,与其他系统集成可能需要额外的工作。
-
可靠性:
- Pulsar: Pulsar确保消息的可靠传递,即使在节点故障的情况下也能保证消息不会丢失。
- RabbitMQ: RabbitMQ提供一定程度的可靠性,但在某些情况下可能需要额外的配置。
3、特点优势
3.1 Pulsar的设计理念
-
分层架构: Pulsar采用分层架构,将存储和计算分离开来。这种设计使得Pulsar能够轻松地扩展和管理,同时提供高吞吐量和低延迟。
-
多租户支持: Pulsar天生支持多租户,允许在同一集群上隔离多个租户的数据。这种设计使得Pulsar非常适合在大规模的多组织或多项目环境中使用。
-
持久性和可靠性: Pulsar确保消息的可靠传递,即使在节点故障的情况下也能保证消息不会丢失。这是通过使用持久性存储和复制机制来实现的。
-
灵活的数据保留策略: Pulsar支持多种消息保留策略,可以根据主题设置不同的保留时间和存储策略。这种灵活性使得Pulsar适用于不同类型的应用场景。
-
动态消息路由: Pulsar允许动态地添加和删除主题,而无需中断生产者和消费者。这使得系统更具弹性,能够适应不断变化的需求。
3.2 Pulsar的主要优势
-
高可扩展性: Pulsar的分层架构和多租户支持使其能够轻松地扩展,适应不断增长的数据规模和工作负载。
-
多协议支持: Pulsar支持多种协议,包括自有的Pulsar协议和Kafka协议。这种灵活性使得Pulsar更容易与现有系统集成,同时提供了迁移现有Kafka应用程序的便利性。
-
低延迟和高吞吐量: Pulsar的设计使其能够实现低延迟和高吞吐量,使其非常适用于实时数据处理和分析场景。
-
容错和可靠性: Pulsar的架构和复制机制保证了系统的容错性和可靠性,即使在节点故障的情况下也能保持消息的可靠传递。
-
社区支持和开源: 作为Apache软件基金会的项目,Pulsar得到了广泛的社区支持,有着活跃的开发者和用户社群。这也确保了Pulsar的持续发展和改进。
二、Pulsar的基本概念
1、主题(Topics)
在Pulsar中,主题(Topics)是消息传递的基本单元。它是消息的逻辑容器,用于将消息从生产者传递给消费者。
1.1 持久主题与非持久主题
-
持久主题: 持久主题是指存储在Pulsar中的消息将持久保存,即使没有消费者订阅该主题,消息仍然会被保留。这使得消费者在稍后加入时可以接收到之前发布的消息。
-
非持久主题: 非持久主题则不会保存消息,如果没有活跃的订阅者,消息将会被丢弃。这种类型的主题适用于实时性要求高、不需要历史消息的场景。
1.2 主题的命名规则
主题的命名规则在Pulsar中非常灵活,但通常遵循一些基本原则:
- 主题名称由多个部分组成,使用斜杠(/)进行层级划分,形成一个层次结构。
- 层级结构可以帮助组织和分类主题,使其更易于管理。
- 示例主题名称:
persistent/public/default/my-topic
,其中persistent
表示这是一个持久主题,public/default
是命名空间,my-topic
是主题的名称。 non-persistent
表示非持久主题。
1.3 示例(Go语言)
示例1:创建一个持久主题
package main
import (
"fmt"
"github.com/apache/pulsar/pulsar-client-go/pulsar"
)
func main() {
client, _ := pulsar.NewClient(pulsar.ClientOptions{
URL: "pulsar://localhost:6650",
})
defer client.Close()
topicName := "persistent/public/default/my-persistent-topic"
// 创建持久主题
_, err := client.CreateProducer(pulsar.ProducerOptions{
Topic: topicName,
})
if err != nil {
fmt.Println("Error creating producer:", err)
return
}
fmt.Println("Persistent topic created successfully.")
}
示例2:创建一个非持久主题
package main
import (
"fmt"
"github.com/apache/pulsar/pulsar-client-go/pulsar"
)
func main() {
client, _ := pulsar.NewClient(pulsar.ClientOptions{
URL: "pulsar://localhost:6650",
})
defer client.Close()
topicName := "non-persistent/public/default/my-non-persistent-topic"
// 创建非持久主题
_, err := client.CreateProducer(pulsar.ProducerOptions{
Topic: topicName,
MessageRoutingMode: pulsar.SinglePartition,
MessageRouter: pulsar.RoundRobinDistribution,
BlockIfQueueFull: true,
BatchingMaxPublishDelay: 10,
BatchingMaxPublishDelayMs: 10,
BatchingMaxMessages: 1000,
BatchingMaxBytes: 128 * 1024,
BatchingType: pulsar.BatchingTypeRegular,
BatchingMaxAllowedMsgBytes: 128 * 1024,
BatchingMaxAllowedMessages: 1000,
})
if err != nil {
fmt.Println("Error creating producer:", err)
return
}
fmt.Println("Non-persistent topic created successfully.")
}
2、生产者(Producers)
在Pulsar中,生产者(Producers)负责将消息发布到主题。
2.1 生产者的创建和配置
要创建一个Pulsar生产者,首先需要连接到Pulsar集群,并指定要生产消息的主题。
以下是创建生产者的基本步骤:
package main
import (
"fmt"
"github.com/apache/pulsar/pulsar-client-go/pulsar"
)
func main() {
client, _ := pulsar.NewClient(pulsar.ClientOptions{
URL: "pulsar://localhost:6650",
})
defer client.Close()
topicName := "persistent/public/default/my-topic"
// 创建生产者
producer, err := client.CreateProducer(pulsar.ProducerOptions{
Topic: topicName,
})
if err != nil {
fmt.Println("Error creating producer:", err)
return
}
defer producer.Close()
fmt.Println("Producer created successfully.")
}
在上述示例中,我们使用Pulsar Go客户端创建了一个生产者,连接到本地Pulsar集群,并指定了要生产消息的主题。
创建生产者后,务必使用defer
语句关闭生产者以释放资源。
2.2 消息发送机制
一旦创建了生产者,我们可以使用生产者发送消息到指定的主题。
以下是消息发送的基本步骤:
package main
import (
"fmt"
"github.com/apache/pulsar/pulsar-client-go/pulsar"
)
func main() {
client, _ := pulsar.NewClient(pulsar.ClientOptions{
URL: "pulsar://localhost:6650",
})
defer client.Close()
topicName := "persistent/public/default/my-topic"
// 创建生产者
producer, err := client.CreateProducer(pulsar.ProducerOptions{
Topic: topicName,
})
if err != nil {
fmt.Println("Error creating producer:", err)
return
}
defer producer.Close()
// 发送消息
message := pulsar.ProducerMessage{
Payload: []byte("Hello, Pulsar!"),
}
// 发送消息并获取消息ID
msgID, err := producer.Send(message)
if err != nil {
fmt.Println("Error sending message:", err)
return
}
fmt.Printf("Message sent successfully. Message ID: %v\n", msgID)
}
在上述示例中,我们创建了一个ProducerMessage
结构体,其中包含要发送的消息的有效载荷(Payload)。然后,我们使用producer.Send()
方法发送消息,并获取消息的ID。
上述示例演示了如何使用Go语言创建一个Pulsar生产者,以及如何发送消息到指定的主题。运行这个示例将在指定主题上发布一条消息,可以在消费者端订阅该主题以接收消息。
3、消费者(Consumers)
在Pulsar中,消费者(Consumers)用于订阅主题并接收发布到该主题的消息。
3.1 消费者的创建和配置
要创建一个Pulsar消费者,首先需要连接到Pulsar集群,并指定要订阅的主题。
以下是创建消费者的基本步骤:
package main
import (
"context"
"fmt"
"github.com/apache/pulsar/pulsar-client-go/pulsar"
)
func main() {
client, _ := pulsar.NewClient(pulsar.ClientOptions{
URL: "pulsar://localhost:6650",
})
defer client.Close()
topicName := "persistent/public/default/my-topic"
// 创建消费者
consumer, err := client.Subscribe(pulsar.ConsumerOptions{
Topic: topicName,
SubscriptionName: "my-subscription",
Type: pulsar.Shared,
})
if err != nil {
fmt.Println("Error creating consumer:", err)
return
}
defer consumer.Close()
fmt.Println("Consumer created successfully.")
}
在上述示例中,我们使用Pulsar Go客户端创建了一个消费者,连接到本地Pulsar集群,并指定了要订阅的主题。
创建消费者后,务必使用defer
语句关闭消费者以释放资源。
2.2 消息接收机制
一旦创建了消费者,我们可以使用消费者接收从主题发布的消息。
以下是消息接收的基本步骤:
package main
import (
"context"
"fmt"
"github.com/apache/pulsar/pulsar-client-go/pulsar"
)
func main() {
client, _ := pulsar.NewClient(pulsar.ClientOptions{
URL: "pulsar://localhost:6650",
})
defer client.Close()
topicName := "persistent/public/default/my-topic"
// 创建消费者
consumer, err := client.Subscribe(pulsar.ConsumerOptions{
Topic: topicName,
SubscriptionName: "my-subscription",
Type: pulsar.Shared,
})
if err != nil {
fmt.Println("Error creating consumer:", err)
return
}
defer consumer.Close()
// 接收消息
for {
msg, err := consumer.Receive(context.Background())
if err != nil {
fmt.Println("Error receiving message:", err)
return
}
fmt.Printf("Received message: %s\n", string(msg.Payload()))
// 确认消息已被消费
consumer.Ack(msg)
}
}
在上述示例中,我们使用consumer.Receive()
方法接收消息。处理完消息后,使用consumer.Ack()
方法确认消息已被消费。这是为了确保消息不会再次传递给同一消费者。
上述示例演示了如何使用Go语言创建一个Pulsar消费者,并通过循环接收消息。运行这个示例将使消费者订阅指定主题,然后打印接收到的消息内容。
4、订阅(Subscriptions)
在Pulsar中,订阅(Subscriptions)用于定义如何将消息传递给消费者。
订阅模式包括独占订阅(Exclusive)、共享订阅(Shared)、故障转移订阅(Failover)和键共享订阅(Key_Shared)。
4.1 订阅模式
-
独占订阅(Exclusive): 在独占订阅模式下,一个主题只能有一个活跃的订阅者消费消息。其他订阅者无法接收相同主题的消息。这种模式适用于需要确保每条消息只被一个消费者处理的场景。
-
共享订阅(Shared): 共享订阅模式允许多个订阅者同时消费同一主题的消息。每个订阅者都接收主题的部分消息,以实现负载均衡。适用于需要多个消费者共同处理消息的场景。
-
故障转移订阅(Failover): 故障转移订阅模式是独占订阅的一种变体,但它支持故障转移。如果活跃的订阅者出现故障,Pulsar会将订阅切换到下一个可用的订阅者,确保消息不会丢失。
-
键共享订阅(Key_Shared): 键共享订阅模式允许多个订阅者按照消息的键(Key)进行分区。每个订阅者负责处理特定键的消息,以确保相同键的消息由同一订阅者处理。
4.2. 订阅的管理
在Pulsar中,订阅的管理涉及创建、删除和管理订阅。
示例演示如何创建一个共享订阅:
package main
import (
"context"
"fmt"
"github.com/apache/pulsar/pulsar-client-go/pulsar"
)
func main() {
client, _ := pulsar.NewClient(pulsar.ClientOptions{
URL: "pulsar://localhost:6650",
})
defer client.Close()
topicName := "persistent/public/default/my-topic"
// 创建共享订阅
consumer, err := client.Subscribe(pulsar.ConsumerOptions{
Topic: topicName,
SubscriptionName: "my-shared-subscription",
Type: pulsar.Shared,
})
if err != nil {
fmt.Println("Error creating shared subscription:", err)
return
}
defer consumer.Close()
fmt.Println("Shared subscription created successfully.")
}
在上述示例中,我们使用Pulsar Go客户端创建了一个共享订阅。请根据实际需求选择适当的订阅模式,并确保在创建订阅时指定唯一的订阅名称。
5、命名空间(Namespaces)
在Pulsar中,命名空间(Namespaces)是用于隔离和组织主题(Topics)的逻辑容器。命名空间允许在Pulsar中创建多个独立的主题集合,以便更好地组织和管理消息传递。
5.1 命名空间的作用
-
隔离和组织: 命名空间提供了一种在Pulsar中隔离和组织主题的方式。通过将主题置于特定命名空间中,可以在不同应用、团队或用例之间实现逻辑隔离。
-
资源分配: 命名空间允许对消息传递资源进行更好的管理和分配。不同的命名空间可以具有不同的配额和配置,以满足特定需求。
-
权限控制: Pulsar提供了对命名空间的权限控制,可以通过命名空间级别的权限设置来限制用户对主题的访问和操作。
5.2 命名空间的管理
在Pulsar中,命名空间的管理涉及创建、删除和配置命名空间。
示例演示如何使用Go语言创建一个命名空间:
package main
import (
"fmt"
"github.com/apache/pulsar/pulsar-client-go/pulsar"
)
func main() {
client, _ := pulsar.NewClient(pulsar.ClientOptions{
URL: "pulsar://localhost:6650",
})
defer client.Close()
namespaceName := "public/default"
// 创建命名空间
err := client.CreateNamespace(namespaceName)
if err != nil {
fmt.Println("Error creating namespace:", err)
return
}
fmt.Printf("Namespace '%s' created successfully.\n", namespaceName)
}
在上述示例中,我们使用Pulsar Go客户端创建了一个命名空间。请根据实际需求选择唯一的命名空间名称,并确保在创建主题时将其置于相应的命名空间中。
6、租户(Tenants)
在Pulsar中,租户(Tenants)是用于隔离和管理资源的顶级组织单位。租户是一个逻辑概念,用于将Pulsar的资源划分为不同的组,以便实现多租户的隔离和管理。
6.1 租户的概念
-
隔离和资源管理: 租户允许在Pulsar中创建多个独立的资源集合,包括命名空间、主题和订阅。通过租户,可以实现资源的逻辑隔离,确保不同组织或应用之间的资源不会相互干扰。
-
权限控制: Pulsar提供了对租户的权限控制,可以通过租户级别的权限设置来限制用户对资源的访问和操作。这有助于确保每个租户只能访问其拥有的资源。
6.2 租户的管理
在Pulsar中,租户的管理涉及创建、删除和配置租户。
示例演示如何使用Go语言创建一个租户:
package main
import (
"fmt"
"github.com/apache/pulsar/pulsar-client-go/pulsar"
)
func main() {
client, _ := pulsar.NewClient(pulsar.ClientOptions{
URL: "pulsar://localhost:6650",
})
defer client.Close()
tenantName := "my-tenant"
// 创建租户
err := client.CreateTenant(tenantName)
if err != nil {
fmt.Println("Error creating tenant:", err)
return
}
fmt.Printf("Tenant '%s' created successfully.\n", tenantName)
}
在上述示例中,我们使用Pulsar Go客户端创建了一个租户。根据实际需求选择唯一的租户名称,并确保在创建资源时将其置于相应的租户中。