一、Pulsar的工作原理
1、消息发布和分发
1.1 消息的生命周期
Pulsar 是一个分布式的、高性能的消息系统,具有灵活的消息生命周期管理。
以下是 Pulsar 消息的主要生命周期阶段:
-
生产(Produce): 生产者将消息发布到 Pulsar 的主题(Topic)中。消息可以是文本、二进制数据等形式。
-
存储(Storage): Pulsar 将接收到的消息存储在持久化存储中。这使得即使在消费者尚未处理消息时,消息仍然是可靠和持久的。
-
消费者订阅(Consumer Subscription): 消费者通过订阅主题来接收消息。Pulsar 支持多种订阅模式,如独占订阅、共享订阅和失败订阅。
-
分发(Distribution): Pulsar 使用订阅的模式将消息分发给订阅者。不同的分发策略会影响消息的分发行为。
-
消费(Consume): 消费者从订阅中接收消息,并处理这些消息。处理可以是业务逻辑、存储或其他操作。
-
应答(Acknowledge): 消费者在成功处理消息后向 Pulsar 发送应答,通知 Pulsar 可以安全地从订阅中删除已处理的消息。
-
超时(Timeout): 如果消费者在一定时间内未对消息进行应答,Pulsar 将重新分发消息,确保消息被处理。
1.2 消息分发策略
Pulsar 使用订阅模式来实现消息的分发,而不同的订阅模式决定了消息分发的方式:
-
独占订阅(Exclusive Subscription): 消息只会被订阅的一个消费者接收。适用于需要确保消息按顺序被处理的场景。
-
共享订阅(Shared Subscription): 多个消费者共享订阅,每个消费者接收到的消息数量大致相等。适用于需要水平扩展消费能力的场景。
-
故障订阅(Failover Subscription): 多个消费者,但只有一个消费者接收消息。当这个消费者故障时,其他消费者会接替它继续处理消息。
-
键共享订阅(Key_Shared): 键共享订阅模式允许多个订阅者按照消息的键(Key)进行分区。每个订阅者负责处理特定键的消息,以确保相同键的消息由同一订阅者处理。
2、消息存储
2.1 消息持久化机制
Pulsar 通过持久化消息来确保消息在生产者发布后即使在消费者处理之前也不会丢失。
消息持久化包括以下几个关键方面:
-
写入到 BookKeeper: Pulsar 使用 Apache BookKeeper 作为其持久化存储引擎。当生产者发布消息时,消息首先被写入到 BookKeeper 中的分布式日志中。这确保了消息的可靠性和持久性。
-
多副本机制: BookKeeper 采用多副本机制,将消息数据复制到多个节点上。这提供了容错性,即使某些节点发生故障,仍然能够从其他节点获取消息数据。
-
异步写入: Pulsar 采用异步写入的方式,使得生产者无需等待消息写入完成就可以继续发布下一条消息,提高了生产者的吞吐量。
2.2 存储层优化
Pulsar 在存储层实施了一些优化策略,以提高性能和可伸缩性:
-
分段的存储: Pulsar 将消息划分为不同的段(Segment),每个段都有固定的大小。这种分段的存储方式使得 Pulsar 能够高效地管理存储空间,而不会浪费过多的资源。
-
持久化和缓存: Pulsar 同时支持消息的持久化存储和缓存存储。热门的消息可以被缓存在内存中,加速消息的读取,而不频繁访问的消息则会存储在持久化存储中。
-
压缩算法: Pulsar 使用压缩算法对消息进行压缩,减小存储占用和提高传输效率。常见的压缩算法如 Snappy、LZ4 等都可以用于 Pulsar。
3、消息路由
3.1 Pulsar 负载均衡机制
Pulsar 采用了一种分区和分段的方式来实现负载均衡,确保消息在集群中均匀分布。
具体来说,有两个关键概念:
-
Topic 分区(Topic Partitioning): Pulsar 允许将一个主题(Topic)分为多个分区,每个分区独立管理消息。这使得 Pulsar 可以将消息负载均衡地分布到多个分区,提高了系统的并行处理能力。
-
分段的存储(Segmented Storage): 每个分区被划分为多个连续的存储段。这种分段的存储方式使得 Pulsar 能够更加灵活地管理存储和处理消息。每个存储段都可以独立地进行扩展和维护。
3.2 路由策略
Pulsar 使用路由策略来确定将消息路由到哪个分区,确保消息的均匀分布。
以下是 Pulsar 中常见的路由策略:
-
Hash 策略: 使用消息的关键信息进行哈希计算,将消息路由到对应的分区。这种策略确保相同关键信息的消息总是路由到同一个分区,有利于保持消息的顺序性。
-
Round Robin 策略: 将消息轮流分配到可用的分区。这种策略适用于希望实现简单负载均衡的场景,确保分区的负载基本均衡。
-
Range 策略: 将消息路由到与消息关键信息范围相匹配的分区。这种策略通常用于需要按照某个范围进行数据分区的场景。
4、消息过滤
4.1 消息过滤的实现
Pulsar 提供了一种灵活的消息过滤机制,它基于消息的属性(Attributes)进行过滤。消息的属性是由生产者设置的键值对,可以包含各种元数据,例如消息类型、来源等。Pulsar 使用 SQL-Like 表达式来定义消息过滤条件。
实现步骤
:
-
设置生产者属性: 在生产者端,设置消息的属性。例如,可以设置消息的类型、标签等信息。
-
定义过滤条件: 在消费者端,通过 SQL-Like 表达式定义过滤条件。这些条件将用于过滤出符合条件的消息。
-
创建消费者时应用过滤条件: 在创建消费者时,将过滤条件应用到消费者配置中。这样,消费者将只接收符合条件的消息。
4.2 消息过滤的应用场景
1. 按消息类型过滤
- 实现步骤: 设置消息的类型属性,使用过滤条件选择特定类型的消息。
- 示例代码(Go 语言):
consumer, err := client.Subscribe(pulsar.ConsumerOptions{ Topic: "my-topic", SubscriptionName: "my-subscription", SubscriptionType: pulsar.Exclusive, Type: pulsar.Shared, SubscriptionInitialPosition: pulsar.SubscriptionPositionEarliest, Selector: "type = 'important'", // 过滤条件,仅接收类型为 'important' 的消息 })
2. 按消息标签过滤
- 实现步骤: 设置消息的标签属性,使用过滤条件选择特定标签的消息。
- 示例代码(Go 语言):
consumer, err := client.Subscribe(pulsar.ConsumerOptions{ Topic: "my-topic", SubscriptionName: "my-subscription", SubscriptionType: pulsar.Exclusive, Type: pulsar.Shared, SubscriptionInitialPosition: pulsar.SubscriptionPositionEarliest, Selector: "tag = 'important'", // 过滤条件,仅接收标签为 'important' 的消息 })
3. 按消息属性过滤
- 实现步骤: 设置消息的自定义属性,使用过滤条件选择符合属性条件的消息。
- 示例代码(Go 语言):
consumer, err := client.Subscribe(pulsar.ConsumerOptions{ Topic: "my-topic", SubscriptionName: "my-subscription", SubscriptionType: pulsar.Exclusive, Type: pulsar.Shared, SubscriptionInitialPosition: pulsar.SubscriptionPositionEarliest, Selector: "priority > 5", // 过滤条件,仅接收优先级大于 5 的消息 })
5、示例代码(Go 语言)
package main
import (
"context"
"fmt"
"github.com/apache/pulsar/pulsar-client-go/pulsar"
)
func main() {
client, err := pulsar.NewClient(pulsar.ClientOptions{
URL: "pulsar://localhost:6650",
})
if err != nil {
fmt.Println("Error creating Pulsar client:", err)
return
}
defer client.Close()
// 创建消费者并设置过滤条件
consumer, err := client.Subscribe(pulsar.ConsumerOptions{
Topic: "my-topic",
SubscriptionName: "my-subscription",
SubscriptionType: pulsar.Exclusive,
Type: pulsar.Shared,
SubscriptionInitialPosition: pulsar.SubscriptionPositionEarliest,
Selector: "type = 'important'", // 过滤条件,仅接收类型为 'important' 的消息
})
if err != nil {
fmt.Println("Error creating consumer:", err)
return
}
defer consumer.Close()
// 消费消息
msgReceived, err := consumer.Receive(context.Background())
if err != nil {
fmt.Println("Error receiving message:", err)
return
}
// 处理消息
fmt.Printf("Received message: %s\n", string(msgReceived.Payload()))
// 应答消息
consumer.Ack(msgReceived)
}
二、Pulsar客户端API
1、Java客户端
1.1 API概览
1. Pulsar生产者(Producer)
Pulsar生产者用于将消息发布到主题(Topic)。
import org.apache.pulsar.client.api.*;
public class PulsarProducerExample {
public static void main(String[] args) throws PulsarClientException {
PulsarClient client = PulsarClient.builder()
.serviceUrl("pulsar://pulsar-cluster-url")
.build();
Producer<String> producer = client.newProducer(Schema.STRING)
.topic("my-topic")
.create();
producer.send("Hello, Pulsar!");
producer.close();
client.close();
}
}
2. Pulsar消费者(Consumer)
Pulsar消费者用于从主题订阅消息。
import org.apache.pulsar.client.api.*;
public class PulsarConsumerExample {
public static void main(String[] args) throws PulsarClientException {
PulsarClient client = PulsarClient.builder()
.serviceUrl("pulsar://pulsar-cluster-url")
.build();
Consumer<String> consumer = client.newConsumer(Schema.STRING)
.subscriptionName("my-subscription")
.subscriptionType(SubscriptionType.Exclusive)
.subscribe("my-topic");
Message<String> msg = consumer.receive();
System.out.println("Received message: " + msg.getValue());
consumer.close();
client.close();
}
}
1.2 API高级特性
1. 生产者确认机制
Pulsar支持生产者确认机制,确保消息已经被成功接收。
// 在发送消息后获取确认
MessageId msgId = producer.send("Hello, Pulsar!");
producer.flush();
System.out.println("Message sent with ID: " + msgId);
2. 消费者批量接收
为了提高效率,可以批量接收消息:
Messages<String> messages = consumer.batchReceive();
for (Message<String> msg : messages) {
System.out.println("Received message: " + msg.getValue());
}
consumer.acknowledge(messages);
3. 事务
Pulsar支持事务,确保消息的原子性:
Transaction txn = client.beginTransaction();
Producer<String> producer = client.newProducer(Schema.STRING)
.transaction(txn)
.topic("my-topic")
.create();
try {
producer.send("Message 1");
producer.send("Message 2");
txn.commit();
} catch (Exception e) {
txn.abort();
} finally {
producer.close();
}
2、Python客户端
2.1 API概览
1. Pulsar生产者(Producer)
Pulsar生产者用于将消息发布到主题(Topic)。
from pulsar import Client, MessageId
client = Client('pulsar://pulsar-cluster-url')
producer = client.create_producer('my-topic')
message_id = producer.send('Hello, Pulsar!')
print(f'Message sent with ID: {message_id}')
2. Pulsar消费者(Consumer)
Pulsar消费者用于从主题订阅消息。
from pulsar import Client, ConsumerType
client = Client('pulsar://pulsar-cluster-url')
consumer = client.subscribe('my-topic', 'my-subscription', consumer_type=ConsumerType.Exclusive)
msg = consumer.receive()
print(f'Received message: {msg.data()}')
consumer.acknowledge(msg)
2.2 API高级特性
1. 生产者确认机制
Pulsar支持生产者确认机制,确保消息已经被成功接收。
# 在发送消息后获取确认
message_id = producer.send('Hello, Pulsar!')
producer.flush()
print(f'Message sent with ID: {message_id}')
2. 消费者批量接收
为了提高效率,可以批量接收消息:
messages = consumer.batch_receive()
for msg in messages:
print(f'Received message: {msg.data()}')
consumer.acknowledge(messages)
3. 事务
Pulsar支持事务,确保消息的原子性:
with client.transaction() as txn:
producer = txn.create_producer('my-topic')
producer.send('Message 1')
producer.send('Message 2')
txn.commit()
3、Go客户端
3.1 API概览
1. Pulsar生产者(Producer)
Pulsar生产者用于将消息发布到主题(Topic)。
package main
import (
"log"
"github.com/apache/pulsar/pulsar-client-go/pulsar"
)
func main() {
client, err := pulsar.NewClient(pulsar.ClientOptions{
URL: "pulsar://pulsar-cluster-url",
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
producer, err := client.CreateProducer(pulsar.ProducerOptions{
Topic: "my-topic",
})
if err != nil {
log.Fatal(err)
}
defer producer.Close()
messageID, err := producer.Send(pulsar.ProducerMessage{
Payload: []byte("Hello, Pulsar!"),
})
if err != nil {
log.Fatal(err)
}
log.Printf("Message sent with ID: %v", messageID)
}
2. Pulsar消费者(Consumer)
Pulsar消费者用于从主题订阅消息。
package main
import (
"log"
"github.com/apache/pulsar/pulsar-client-go/pulsar"
)
func main() {
client, err := pulsar.NewClient(pulsar.ClientOptions{
URL: "pulsar://pulsar-cluster-url",
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
consumer, err := client.Subscribe(pulsar.ConsumerOptions{
Topic: "my-topic",
SubscriptionName: "my-subscription",
SubscriptionType: pulsar.Exclusive,
})
if err != nil {
log.Fatal(err)
}
defer consumer.Close()
msg, err := consumer.Receive()
if err != nil {
log.Fatal(err)
}
log.Printf("Received message: %s", msg.Payload())
consumer.Ack(msg)
}
3.2 PAPI高级特性
1. 生产者确认机制
Pulsar支持生产者确认机制,确保消息已经被成功接收。
// 在发送消息后获取确认
messageID, err := producer.Send(pulsar.ProducerMessage{
Payload: []byte("Hello, Pulsar!"),
})
if err != nil {
log.Fatal(err)
}
err = producer.Flush()
if err != nil {
log.Fatal(err)
}
log.Printf("Message sent with ID: %v", messageID)
2. 消费者批量接收
为了提高效率,可以批量接收消息:
messages, err := consumer.BatchReceive()
if err != nil {
log.Fatal(err)
}
for _, msg := range messages {
log.Printf("Received message: %s", msg.Payload())
}
consumer.Ack(messages)
3. 事务
Pulsar支持事务,确保消息的原子性:
txn, err := client.BeginTransaction()
if err != nil {
log.Fatal(err)
}
defer txn.Rollback()
producer, err := txn.CreateProducer(pulsar.ProducerOptions{
Topic: "my-topic",
})
if err != nil {
log.Fatal(err)
}
_, err = producer.Send(pulsar.ProducerMessage{
Payload: []byte("Message 1"),
})
if err != nil {
log.Fatal(err)
}
_, err = producer.Send(pulsar.ProducerMessage{
Payload: []byte("Message 2"),
})
if err != nil {
log.Fatal(err)
}
err = txn.Commit()
if err != nil {
log.Fatal(err)
}