下载安装包
http://pulsar.apache.org/en/download/
使用tar -zxvf
解压下载的资源包,目录结构如下
显示目录:
本地单点运行,使用命令./pulsar standalone
即可,这里使用默认配置,暴露端口为6650
测试消息
发送消息 命令
./pulsar-client produce my-topic --messages "hello-pulsar"
结果
接收消息
./pulsar-client consume my-topic -s "first-subscription"
如果您使用的是Maven,请将其添加到您的pom.xml
:
<pulsar.version>2.4.0pulsar.version>
<dependency>
<groupId>org.apache.pulsargroupId>
<artifactId>pulsar-clientartifactId>
<version>${pulsar.version}version>
dependency>
复制
如果您使用的是Gradle,请将其添加到您的build.gradle
文件中:
def pulsarVersion = '2.4.0'
dependencies {
compile group: 'org.apache.pulsar', name: 'pulsar-client', version: pulsarVersion
}
复制
要使用客户端库连接到Pulsar,您需要指定Pulsar协议 URL。
Pulsar协议URL分配给特定的集群,使用该pulsar
方案并具有6650的默认端口。以下是一个示例localhost
:
pulsar://localhost:6650
复制
如果您有多个代理,则URL可能如下所示:
pulsar://localhost:6550,localhost:6651,localhost:6652
复制
生产Pulsar集群的URL可能如下所示:
pulsar://pulsar.us-west.example.com:6650
复制
如果您正在使用TLS身份验证,则URL将如下所示:
pulsar+ssl://pulsar.us-west.example.com:6651
复制
您可以 仅使用目标Pulsar 集群的URL 来实例化PulsarClient对象,如下所示:
PulsarClient client = PulsarClient.builder()
.serviceUrl("pulsar://localhost:6650")
.build();
复制
如果你有多个经纪人,你可以像这样启动一个PulsarClient:
PulsarClient client = PulsarClient.builder()
.serviceUrl("pulsar://localhost:6650,localhost:6651,localhost:6652")
.build();
复制
独立群集的默认代理URL
如果您在独立模式下运行群集,
pulsar://localhost:6650
默认情况下,代理将在URL 处可用。
查看PulsarClient 类的Javadoc以获取可配置参数的完整列表。
除了客户端级别的配置,您还可以应用生产者和消费者特定的配置,如下面的部分所示。
在Pulsar中,制作人将信息写入主题。一旦您实例化了PulsarClient 对象(如上节所述),您就可 以为特定的Pulsar 主题创建一个Producer。
Producer<byte[]> producer = client.newProducer()
.topic("my-topic")
.create();
// You can then send messages to the broker and topic you specified:
producer.send("My message".getBytes());
复制
默认情况下,生成器生成由字节数组组成的消息。但是,您可以通过指定消息架构来生成不同类型。
Producer stringProducer = client.newProducer(Schema.STRING)
.topic("my-topic")
.create();
stringProducer.send("My message");
复制
您应该始终确保在不再需要时关闭您的生产者,消费者和客户:
producer.close(); consumer.close(); client.close();
复制
关闭操作也可以是异步的:
producer.closeAsync() .thenRun(() -> System.out.println("Producer closed")); .exceptionally((ex) -> { System.err.println("Failed to close producer: " + ex); return ex; });
复制
如果您实例化Producer
仅指定主题名称的对象(如上例所示),则生产者将使用默认配置。要使用非默认配置,可以设置各种可配置参数。有关完整列表,请参阅ProducerBuilder 类的Javadoc 。这是一个例子:
Producer<byte[]> producer = client.newProducer()
.topic("my-topic")
.batchingMaxPublishDelay(10, TimeUnit.MILLISECONDS)
.sendTimeout(10, TimeUnit.SECONDS)
.blockIfQueueFull(true)
.create();
复制
使用分区主题时,只要使用生产者发布消息,就可以指定路由模式。有关使用Java客户端指定路由模式的更多信息,请参阅分区主题菜谱。
您还可以使用Java客户端异步发布消息。使用异步发送,生产者将消息放入阻塞队列并立即返回。然后,客户端库将在后台将消息发送给代理。如果队列已满(最大大小可配置),则在调用API时,生产者可能会被阻塞或立即失败,具体取决于传递给生产者的参数。
这是一个异步发送操作示例:
producer.sendAsync("my-async-message".getBytes()).thenAccept(msgId -> {
System.out.printf("Message with ID %s successfully sent", msgId);
});
复制
从上面的示例中可以看出,异步发送操作返回一个 包含在其中的MessageIdCompletableFuture
。
除了值之外,还可以在给定消息上设置其他项:
producer.newMessage()
.key("my-message-key")
.value("my-async-message".getBytes())
.property("my-key", "my-value")
.property("my-other-key", "my-other-value")
.send();
复制
对于前一种情况,也可以终止构建器链sendAsync()
并返回将来的情况。
在Pulsar中,消费者订阅主题并处理生产者发布到这些主题的消息。您可以通过首先实例化PulsarClient对象并将其传递给Pulsar代理的URL(如上所述)来实例化新的使用者。
一旦实例化了PulsarClient 对象,就可以 通过指定主题和订阅来创建Consumer。
Consumer consumer = client.newConsumer()
.topic("my-topic")
.subscriptionName("my-subscription")
.subscribe();
复制
该subscribe
方法将自动将使用者订阅到指定的主题和订阅。让消费者倾听主题的一种方法是设置while
循环。在此示例循环中,使用者侦听消息,打印所接收的任何消息的内容,然后确认消息已被处理。如果处理逻辑失败,我们使用否定确认 来在稍后的时间点重新传递消息。
while (true) {
// Wait for a message
Message msg = consumer.receive();
try {
// Do something with the message
System.out.printf("Message received: %s", new String(msg.getData()));
// Acknowledge the message so that it can be deleted by the message broker
consumer.acknowledge(msg);
} catch (Exception e) {
// Message failed to process, redeliver later
consumer.negativeAcknowledge(msg);
}
}
复制
如果您实例化Consumer
仅指定主题和订阅名称的对象(如上例所示),则使用者将使用默认配置。要使用非默认配置,可以设置各种可配置参数。有关完整列表,请参阅ConsumerBuilder 类的Javadoc 。这是一个例子:
这是一个示例配置:
Consumer consumer = client.newConsumer()
.topic("my-topic")
.subscriptionName("my-subscription")
.ackTimeout(10, TimeUnit.SECONDS)
.subscriptionType(SubscriptionType.Exclusive)
.subscribe();
复制
该receive
方法将同步接收消息(消息进程将被阻止,直到消息可用)。您还可以使用异步接收,它将立即返回一个CompletableFuture
在新消息可用时完成的对象。
这是一个例子:
CompletableFuture asyncMessage = consumer.receiveAsync();
复制
异步接收操作返回一个 包含在一个内部的消息CompletableFuture
。
除了将消费者订阅到单个Pulsar主题之外,您还可以使用多主题订阅同时订阅多个主题。要使用多主题订阅,您可以提供正则表达式(正则表达式)或List
主题。如果您通过正则表达式选择主题,则所有主题必须位于相同的Pulsar名称空间内。
这里有些例子:
import org.apache.pulsar.client.api.Consumer;
import org.apache.pulsar.client.api.PulsarClient;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
ConsumerBuilder consumerBuilder = pulsarClient.newConsumer()
.subscriptionName(subscription);
// Subscribe to all topics in a namespace
Pattern allTopicsInNamespace = Pattern.compile("persistent://public/default/.*");
Consumer allTopicsConsumer = consumerBuilder
.topicsPattern(allTopicsInNamespace)
.subscribe();
// Subscribe to a subsets of topics in a namespace, based on regex
Pattern someTopicsInNamespace = Pattern.compile("persistent://public/default/foo.*");
Consumer allTopicsConsumer = consumerBuilder
.topicsPattern(someTopicsInNamespace)
.subscribe();
复制
您还可以订阅明确的主题列表(如果您愿意,可以跨命名空间):
List topics = Arrays.asList(
"topic-1",
"topic-2",
"topic-3"
);
Consumer multiTopicConsumer = consumerBuilder
.topics(topics)
.subscribe();
// Alternatively:
Consumer multiTopicConsumer = consumerBuilder
.topics(
"topic-1",
"topic-2",
"topic-3"
)
.subscribe();
复制
您还可以使用subscribeAsync
方法而不是同步subscribe
方法异步订阅多个主题。这是一个例子:
Pattern allTopicsInNamespace = Pattern.compile("persistent://public/default.*");
consumerBuilder
.topics(topics)
.subscribeAsync()
.thenAccept(this::receiveMessageFromConsumer);
private void receiveMessageFromConsumer(Consumer consumer) {
consumer.receiveAsync().thenAccept(message -> {
// Do something with the received message
receiveMessageFromConsumer(consumer);
});
}
复制
Pulsar有各种订阅模式以匹配不同的场景。主题可以具有多个具有不同订阅模式的订阅。但是,订阅一次只能有一种订阅模式。
订阅使用订阅名称标识,订阅名称一次只能指定一种订阅模式。您可以更改订阅模式,但必须首先让此订阅的所有现有使用者脱机。
不同的订阅模式具有不同的消息分发模式 本节介绍订阅模式的差异以及如何使用它们。
为了更好地描述它们之间的差异,假设您有一个名为“my-topic”的主题,并且生产者已发布了10条消息。
Producer producer = client.newProducer(Schema.STRING)
.topic("my-topic")
.enableBatch(false)
.create();
// 3 messages with "key-1", 3 messages with "key-2", 2 messages with "key-3" and 2 messages with "key-4"
producer.newMessage().key("key-1").value("message-1-1").send();
producer.newMessage().key("key-1").value("message-1-2").send();
producer.newMessage().key("key-1").value("message-1-3").send();
producer.newMessage().key("key-2").value("message-2-1").send();
producer.newMessage().key("key-2").value("message-2-2").send();
producer.newMessage().key("key-2").value("message-2-3").send();
producer.newMessage().key("key-3").value("message-3-1").send();
producer.newMessage().key("key-3").value("message-3-2").send();
producer.newMessage().key("key-4").value("message-4-1").send();
producer.newMessage().key("key-4").value("message-4-2").send();
复制
独家
创建新的使用者并使用Exclusive
订阅模式订阅。
Consumer consumer = client.newConsumer()
.topic("my-topic")
.subscriptionName("my-subscription")
.subscriptionType(SubscriptionType.Exclusive)
.subscribe()
复制
只有第一个消费者被允许订阅,其他消费者会收到错误。第一个消费者接收所有10个消息,消费订单与生产订单相同。
注意:
如果topic是分区主题,则第一个使用者订阅所有分区主题,其他使用者未分配分区并收到错误。
故障转移
创建新的消费者并使用Failover
订阅模式订阅。
Consumer consumer1 = client.newConsumer()
.topic("my-topic")
.subscriptionName("my-subscription")
.subscriptionType(SubscriptionType.Failover)
.subscribe()
Consumer consumer2 = client.newConsumer()
.topic("my-topic")
.subscriptionName("my-subscription")
.subscriptionType(SubscriptionType.Failover)
.subscribe()
//conumser1 is the active consumer, consumer2 is the standby consumer.
//consumer1 receives 5 messages and then crashes, consumer2 takes over as an active consumer.
复制
多个消费者可以附加到同一订阅,但只有第一个消费者处于活动状态,而其他消费者处于待机状态。当活动消费者断开连接时,消息将被分派给备用消费者之一,备用消费者成为活动消费者。
如果第一个活动消费者收到5条消息并且已断开连接,则备用消费者将成为活动消费者 消费者1将获得:
("key-1", "message-1-1")
("key-1", "message-1-2")
("key-1", "message-1-3")
("key-2", "message-2-1")
("key-2", "message-2-2")
复制
consumer2将收到:
("key-2", "message-2-3")
("key-3", "message-3-1")
("key-3", "message-3-2")
("key-4", "message-4-1")
("key-4", "message-4-2")
复制
注意:
如果主题是分区主题,则每个分区仅具有一个活动消费者,一个分区的消息仅分发给一个消费者,多个分区的消息分发给多个消费者。
共享
创建新的消费者并订阅Shared
订阅模式:
Consumer consumer1 = client.newConsumer()
.topic("my-topic")
.subscriptionName("my-subscription")
.subscriptionType(SubscriptionType.Shared)
.subscribe()
Consumer consumer2 = client.newConsumer()
.topic("my-topic")
.subscriptionName("my-subscription")
.subscriptionType(SubscriptionType.Shared)
.subscribe()
//Both consumer1 and consumer 2 is active consumers.
复制
在共享订阅模式中,多个消费者可以附加到相同的订阅,并且消息在消费者的循环分发中递送。
如果代理一次只调度一条消息,则consumer1将收到:
("key-1", "message-1-1")
("key-1", "message-1-3")
("key-2", "message-2-2")
("key-3", "message-3-1")
("key-4", "message-4-1")
复制
消费者2将收到:
("key-1", "message-1-2")
("key-2", "message-2-1")
("key-2", "message-2-3")
("key-3", "message-3-2")
("key-4", "message-4-2")
复制
Shared
订阅Exclusive
与Failover
订阅模式不同。Shared
订阅具有更好的灵活性,但不能提供订单保证。
Key_shared
这是自2.4.0发布以来的新订阅模式,创建新的消费者并订阅Key_Shared
订阅模式:
Consumer consumer1 = client.newConsumer()
.topic("my-topic")
.subscriptionName("my-subscription")
.subscriptionType(SubscriptionType.Key_Shared)
.subscribe()
Consumer consumer2 = client.newConsumer()
.topic("my-topic")
.subscriptionName("my-subscription")
.subscriptionType(SubscriptionType.Key_Shared)
.subscribe()
//Both consumer1 and consumer2 are active consumers.
复制
Key_Shared
订阅就像Shared
订阅一样,所有消费者都可以附加到同一订阅。但它与Key_Shared
订阅不同,具有相同密钥的消息按顺序仅传递给一个消费者。消息在不同消费者之间的可能分布(默认情况下,我们事先不知道哪些密钥将分配给消费者,但密钥只会同时分配给消费者。)。
consumer1将收到:
("key-1", "message-1-1")
("key-1", "message-1-2")
("key-1", "message-1-3")
("key-3", "message-3-1")
("key-3", "message-3-2")
复制
消费者2将收到:
("key-2", "message-2-1")
("key-2", "message-2-2")
("key-2", "message-2-3")
("key-4", "message-4-1")
("key-4", "message-4-2")
复制
注意:
如果未指定消息密钥,则默认情况下,不带密钥的消息将按顺序分派给一个使用者。
通过阅读器界面,Pulsar客户端可以在主题中“手动定位”自己,从之后的指定消息中读取所有消息。Pulsar API for Java使您可以 通过指定主题,MessageId 和ReaderConfiguration来创建 Reader对象 。
这是一个例子:
ReaderConfiguration conf = new ReaderConfiguration();
byte[] msgIdBytes = // Some message ID byte array
MessageId id = MessageId.fromByteArray(msgIdBytes);
Reader reader = pulsarClient.newReader()
.topic(topic)
.startMessageId(id)
.create();
while (true) {
Message message = reader.readNext();
// Process message
}
复制
在上面的示例中,Reader
为特定主题和消息(通过ID)实例化对象; 然后,读取器在标识的消息之后迭代主题中的每个消息msgIdBytes
(如何获得该值取决于应用程序)。
上面的代码示例显示将Reader
对象指向特定消息(通过ID),但您也可以使用MessageId.earliest
指向主题上最早的可用消息MessageId.latest
来指向最新的可用消息。
在Pulsar中,所有消息数据都由“引擎盖下”的字节数组组成。消息模式使您可以在构造和处理消息时使用其他类型的数据(从简单类型(如字符串到更复杂的特定于应用程序的类型))。如果你构造一个生产者而没有指定一个模式,那么生产者只能生成类型的消息byte[]
。这是一个例子:
Producer<byte[]> producer = client.newProducer()
.topic(topic)
.create();
复制
上面的生产者等同于a Producer
(事实上,你应该总是明确指定类型)。如果您想将生产者用于不同类型的数据,则需要指定一个模式,通知Pulsar将通过该主题传输哪种数据类型。
假设您有一个SensorReading
类,您希望通过Pulsar主题传输:
public class SensorReading {
public float temperature;
public SensorReading(float temperature) {
this.temperature = temperature;
}
// A no-arg constructor is required
public SensorReading() {
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
}
复制
然后你可以像这样创建一个Producer
(或Consumer
):
Producer producer = client.newProducer(JSONSchema.of(SensorReading.class))
.topic("sensor-readings")
.create();
复制
Java目前可以使用以下模式格式:
没有架构或字节数组架构(可以使用Schema.BYTES
):
Producer<byte[]> bytesProducer = client.newProducer(Schema.BYTES)
.topic("some-raw-bytes-topic")
.create();
复制
或者,等效地:
Producer<byte[]> bytesProducer = client.newProducer()
.topic("some-raw-bytes-topic")
.create();
复制
String
对于正常的UTF-8编码的字符串数据。可以使用Schema.STRING
以下方法应用此架构:
Producer stringProducer = client.newProducer(Schema.STRING)
.topic("some-string-topic")
.create();
复制
可以使用JSONSchema
该类为POJO创建JSON模式。这是一个例子:
Schema pojoSchema = JSONSchema.of(MyPojo.class);
Producer pojoProducer = client.newProducer(pojoSchema)
.topic("some-pojo-topic")
.create();
复制
Pulsar目前支持两种身份验证方案:TLS和Athenz。Pulsar Java客户端可以与两者一起使用。
要使用TLS,您需要将TLS设置为true
使用该setUseTls
方法,将Pulsar客户端指向TLS证书路径,并提供证书和密钥文件的路径。
这是一个示例配置:
Map authParams = new HashMap<>();
authParams.put("tlsCertFile", "/path/to/client-cert.pem");
authParams.put("tlsKeyFile", "/path/to/client-key.pem");
Authentication tlsAuth = AuthenticationFactory
.create(AuthenticationTls.class.getName(), authParams);
PulsarClient client = PulsarClient.builder()
.serviceUrl("pulsar+ssl://my-broker.com:6651")
.enableTls(true)
.tlsTrustCertsFilePath("/path/to/cacert.pem")
.authentication(tlsAuth)
.build();
复制
要将Athenz用作身份验证提供程序,您需要使用TLS并为哈希中的四个参数提供值:
tenantDomain
tenantService
providerDomain
privateKey
您也可以设置一个可选项keyId
。这是一个示例配置:
Map authParams = new HashMap<>();
authParams.put("tenantDomain", "shopping"); // Tenant domain name
authParams.put("tenantService", "some_app"); // Tenant service name
authParams.put("providerDomain", "pulsar"); // Provider domain name
authParams.put("privateKey", "file:///path/to/private.pem"); // Tenant private key path
authParams.put("keyId", "v1"); // Key id for the tenant private key (optional, default: "0")
Authentication athenzAuth = AuthenticationFactory
.create(AuthenticationAthenz.class.getName(), authParams);
PulsarClient client = PulsarClient.builder()
.serviceUrl("pulsar+ssl://my-broker.com:6651")
.enableTls(true)
.tlsTrustCertsFilePath("/path/to/cacert.pem")
.authentication(athenzAuth)
.build();
复制
支持的模式格式
该
privateKey
参数支持以下三种模式格式:
file:///path/to/file
file:/path/to/file
data:application/x-pem-file;base64,
https://github.com/xsm110/demo/tree/master/pulsar-demo-master