Apache Pulsar 發佈訂閱消息系統
Apache Pulsar
Apache Pulsar is an open-source distributed pub-sub messaging system originally created at Yahoo and now part of the Apache Software Foundation
Apache Pulsar是一個開源的分佈式pub-sub消息系統,最初由雅虎開發,現在是Apache軟件基金會的一個孵化器項目。
1. Topic
Topic名稱的URL結構:{persistent|non-persistent}:\tenant\namespace\topic
persistent|non-persistent | 表示數據是否持久化 |
tenant | 表示租戶 |
namespace | 聚合一系列相關的Topic,一個租戶可以有多個namespace |
2.發送模式
producer可以同步或者異步是的方式發佈消息到broker
同步發送 | 發送消息後,producer等待broker的確認,沒有收到確認,會認爲發送失敗。 |
異步發送 | producer把消息放入blocking隊列,立馬返回,客戶端將會在背後把消息發送給broker。如果隊列滿了,根據傳給producer的參數,可能阻塞或者返回失敗。 |
3.接收模式
消息可以通過同步或者異步的方式從broker接收。
同步接收 | 同步接收將會阻塞,直到消息可用。 |
異步接收 | 異步接收立即返回future值,java中的completableFuture。一旦消息可用,即刻完成。 |
3.訂閱模型
(1)Exclusive(獨佔) Exclusive模式爲默認訂閱模式。
獨佔模式,只能有一個消費者綁定到訂閱subscription上,如果多於一個消費者嘗試以同樣的方式訂閱主題,消費者將會收到錯誤。
(2)Shared(共享)
多個消費者可以綁定到同一個訂閱上,消費通過輪詢機制分發給不同的消費者,每個消息僅會被分發給一個消費者。當消費者斷開連接,所有被髮送給他,但沒有被確認的消息將重新被安排,分發給其他存活的消費者。
(3)Failover(災備)
多個consumer可以綁定到同一個 subscription。consumer會按字典順序排序,第一個consumer被初始化爲唯一接收消息的消費者,作爲master consumer。當斷開時,所有的消息(未被確認和後續進入的)將被分發給列中的下一個consumer。
GitHub: link.
項目整合Pulsar
<dependency>
<groupId>org.apache.pulsar</groupId>
<artifactId>pulsar-client</artifactId>
<version>2.2.0</version>
</dependency>
1.PulsarClient(Client創建)
private static final Logger LOG = LoggerFactory.getLogger(PulsarService.class);
private PulsarClient pulsarClient;
private Gson gson;
@Value("${pulsarServiceUrl}")
private String pulsarServiceUrl;
@PostConstruct
public void init() {
gson = new Gson();
try {
pulsarClient = PulsarClient.builder()
.serviceUrl(pulsarServiceUrl)
.build();
} catch (PulsarClientException e) {
LOG.error("PulsarClient build failure!! error={}", e.getMessage());
}
}
2.生產者(發佈消息)
public String produce(String topic, String message) {
try {
Producer<String> producer = pulsarClient.newProducer(Schema.STRING)
.topic("persistent://public/default/" + topic)
.create();
MessageId messageId = producer.send(message);
producer.close();
LOG.info("topic={} message={} messageId={}", topic, message, messageId);
return "topic=" + topic + " message=" + message + " messageId=" + messageId;
} catch (Exception e) {
LOG.error("Pulsar produce failure!! error={}", e.getMessage());
return "Pulsar produce failure!! error=" + e.getMessage();
}
}
3.消費者(消費消息)
public void consume(String topic) {
new Thread(() -> {
try {
Consumer<String> consumer = pulsarClient.newConsumer(Schema.STRING)
.topic(topic)
.subscriptionName(topic)
.subscribe();
while (!Thread.currentThread().isInterrupted()) {
Message<String> message = consumer.receive();
String data = new String(message.getData());
consumer.acknowledge(message);
MessageId messageId = message.getMessageId();
LOG.info("topic={},message={},messageId={}", topic, data, messageId.toString());
Thread.sleep(20);
}
} catch (Exception e) {
LOG.error("Pulsar consume failure!! error={}", e.getMessage());
}
}).start();
}
4.閱讀器(從指定messageId處讀取消息)
public void read(String topic, String offset) {
new Thread(() -> {
try {
String[] offsetSplit = offset.split(":");
MessageId msgId = new BatchMessageIdImpl(Long.parseLong(offsetSplit[0]), Long.parseLong(offsetSplit[1]), Integer.parseInt(offsetSplit[2]), Integer.parseInt(offsetSplit[3]));
Reader<String> reader = pulsarClient.newReader(Schema.STRING)
.topic(topic)
.startMessageId(msgId)
.create();
while (!Thread.currentThread().isInterrupted()) {
Message message = reader.readNext();
String data = new String(message.getData());
MessageId messageId = message.getMessageId();
LOG.info("topic={},message={},messageId={}", topic, data, messageId.toString());
Thread.sleep(20);
}
} catch (Exception e) {
LOG.error("Pulsar read failure!! error={}", e.getMessage());
}
}).start();
}
GitHub: link.