下載安裝包
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的
如果您使用的是Maven,請將其添加到您的pom.xml
:
<!-- in your <properties> block -->
<pulsar.version>2.4.0</pulsar.version>
<!-- in your <dependencies> block -->
<dependency>
<groupId>org.apache.pulsar</groupId>
<artifactId>pulsar-client</artifactId>
<version>${pulsar.version}</version>
</dependency>
複製
搖籃
如果您使用的是Gradle,請將其添加到您的build.gradle
文件中:
def pulsarVersion = '2.4.0'
dependencies {
compile group: 'org.apache.pulsar', name: 'pulsar-client', version: pulsarVersion
}
複製
連接URL
要使用客戶端庫連接到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();
複製
查看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<String> 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<Message> 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<String> 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<String> 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
訂閱具有更好的靈活性,但不能提供訂單保證。
這是自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<byte[]>
(事實上,你應該總是明確指定類型)。如果您想將生產者用於不同類型的數據,則需要指定一個模式,通知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<SensorReading>
(或Consumer<SensorReading>
):
Producer<SensorReading> 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<String> stringProducer = client.newProducer(Schema.STRING) .topic("some-string-topic") .create();
複製
-
可以使用
JSONSchema
該類爲POJO創建JSON模式。這是一個例子:Schema<MyPojo> pojoSchema = JSONSchema.of(MyPojo.class); Producer<MyPojo> pojoProducer = client.newProducer(pojoSchema) .topic("some-pojo-topic") .create();
複製
認證
Pulsar目前支持兩種身份驗證方案:TLS和Athenz。Pulsar Java客戶端可以與兩者一起使用。
TLS身份驗證
要使用TLS,您需要將TLS設置爲true
使用該setUseTls
方法,將Pulsar客戶端指向TLS證書路徑,並提供證書和密鑰文件的路徑。
這是一個示例配置:
Map<String, String> 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
要將Athenz用作身份驗證提供程序,您需要使用TLS併爲哈希中的四個參數提供值:
tenantDomain
tenantService
providerDomain
privateKey
您也可以設置一個可選項keyId
。這是一個示例配置:
Map<String, String> 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,<base64-encoded value>
https://github.com/xsm110/demo/tree/master/pulsar-demo-master