import org.apache.pulsar.client.api.PulsarClientException
import org.springframework.kafka.core.KafkaTemplate
import org.springframework.pulsar.core.PulsarTemplate
import org.springframework.stereotype.Component
@Component
class MyBean(private val pulsarTemplate: PulsarTemplate<String>) {
@Throws(PulsarClientException::class)
fun someMethod() {
pulsarTemplate.send("someTopic", "Hello")
}
}
import org.apache.pulsar.client.api.PulsarClientException
import org.springframework.kafka.core.KafkaTemplate
import org.springframework.pulsar.core.PulsarTemplate
import org.springframework.stereotype.Component
@Component
class MyBean(private val pulsarTemplate: PulsarTemplate<String>) {
@Throws(PulsarClientException::class)
fun someMethod() {
pulsarTemplate.send("someTopic", "Hello")
}
}
import org.springframework.pulsar.reactive.core.ReactivePulsarTemplate
import org.springframework.stereotype.Component
@Component
class MyBean(private val pulsarTemplate: ReactivePulsarTemplate<String>) {
fun someMethod() {
pulsarTemplate.send("someTopic", "Hello").subscribe()
}
}
import org.springframework.pulsar.annotation.PulsarListener
import org.springframework.stereotype.Component
@Component
class MyBean {
@PulsarListener(topics = ["someTopic"])
fun processMessage(content: String?) {
// ...
}
}
@Component
class MyBean {
@ReactivePulsarListener(topics = ["someTopic"])
fun processMessage(content: String?): Mono<Void> {
// ...
return Mono.empty()
}
}
All significant improvements in system scalability come from parallelism.
CPU cores don't go much faster these days. We are getting more cores these days. Hence, we need to find a way to use all the CPUs cores and CPU resources that we get with the cloud.
One idea/approach of parallelism is pipelining.
String subscriptionName = "testSubscription" + UUID.randomUUID();
ReactiveMessageConsumer<TelemetryEvent> messageConsumer = reactivePulsarClient
.messageConsumer(Schema.JSON(TelemetryEvent.class))
.topic(topicNameResolver.resolveTopicName(TelemetryProcessor.TELEMETRY_MEDIAN_TOPIC_NAME))
.subscriptionType(SubscriptionType.Exclusive)
.subscriptionName(subscriptionName)
.subscriptionInitialPosition(SubscriptionInitialPosition.Latest)
.acknowledgeAsynchronously(false)
.build();
// create a subscription to the result topic before executing the operation
String subscriptionName = "testSubscription" + UUID.randomUUID();
ReactiveMessageConsumer<TelemetryEvent> messageConsumer = reactivePulsarClient
.messageConsumer(Schema.JSON(TelemetryEvent.class))
.topic(topicNameResolver.resolveTopicName(TelemetryProcessor.TELEMETRY_MEDIAN_TOPIC_NAME))
.subscriptionType(SubscriptionType.Exclusive)
.subscriptionName(subscriptionName)
.subscriptionInitialPosition(SubscriptionInitialPosition.Latest)
.acknowledgeAsynchronously(false)
.build();
// create the consumer and close it immediately. This is just to create the Pulsar subscription
messageConsumer.consumeNothing().block();
ReactiveMessageSender<TelemetryEvent> messageSender = reactivePulsarClient
.messageSender(Schema.JSON(TelemetryEvent.class))
.topic(topicNameResolver.resolveTopicName(IngestController.TELEMETRY_INGEST_TOPIC_NAME))
.build();
// when
// 100 values for 100 devices are sent to the ingest topic
messageSender
.sendMany(
Flux
.range(1, DEVICE_COUNT)
.flatMap(value -> {
String name = "device" + value + "/sensor1";
return Flux
.range(1, 100)
.map(entryCounter -> TelemetryEvent.builder().n(name).v(entryCounter).build());
})
.map(telemetryEvent -> MessageSpec.builder(telemetryEvent).key(telemetryEvent.getN()).build())
)
.blockLast();
// then the TelemetryProcessor should have aggregated a single median value for each sensor in the result topic
Set<String> deviceNames = new HashSet<>();
messageConsumer
.consumeMany(messageFlux -> messageFlux.map(MessageResult::acknowledgeAndReturn))
.as(StepVerifier::create)
.expectSubscription()
.thenConsumeWhile(message -> {
assertThat(deviceNames.add(message.getValue().getN()))
.as("there shouldn't be more than 1 message per device")
.isTrue();
assertThat(message.getValue().getV()).isEqualTo(51.0);
return deviceNames.size() < DEVICE_COUNT;
})
.expectNoEvent(Duration.ofSeconds(1))
.thenCancel()
.verify(Duration.ofSeconds(10));
cast: modifies the key or value schema to a target compatible schema.
drop-fields: drops fields from structured data.
merge-key-value: merges the fields of KeyValue records where both the key and value are structured data with the same schema type.
unwrap-key-value: if the record is a KeyValue, extract the KeyValue's key or value and make it the record value.
flatten: flattens structured data.
drop: drops a record from further processing.
compute: computes new properties, values or field values on the fly or replaces existing ones.
{key={keyField1: key1, keyField2: key2, keyField3: key3},
value={valueField1: value1, valueField2: value2, valueField3: value3}}(KeyValue<AVRO, AVRO>)
|
| ”type": "drop-fields", "fields": "keyField1,keyField2”, "part": "key”
|
{key={keyField3: key3}, value={valueField1: value1,
valueField2: value2, valueField3: value3}} (KeyValue<AVRO, AVRO>)
|
| "type": "merge-key-value"
|
{key={keyField3: key3}, value={keyField3: key3, valueField1: value1,
valueField2: value2, valueField3: value3}} (KeyValue<AVRO, AVRO>)
|
| "type": "unwrap-key-value"
|
{keyField3: key3, valueField1: value1,
valueField2: value2, valueField3: value3} (AVRO)
|
| "type": "cast", "schema-type": "STRING"
|
{"keyField3": "key3", "valueField1": "value1",
"valueField2": "value2", "valueField3": "value3"} (STRING)
- Persistent message store
- Fast, low impact, horizontal scaling
- Reduced long-term and day-to-day expenses
- Broker
- Stateless
- Built-In load balancing
- Instantaneous scaling
- Zero impact disaster recovery
- Bookies
- Scalable, WAL based, fault-tolerant, low latency, storage service
- Tunable consistency from message replication
- Ensemble size, Write Quorum, ACK Quorum
- Fast write guarantee through Journals
- Segment-centric data persistency via Ledgers
- Topic Partitions
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: sn-pulsar
finalizers:
- resources-finalizer.argocd.argoproj.io
namespace: argo
spec:
destination:
namespace: argo
server: https://kubernetes.default.svc
source:
repoURL: 'https://github.com/streamnative/charts'
path: examples/argocd/charts/sn-pulsar
targetRevision: master
helm:
values: |
project: default
syncPolicy:
automated:
prune: true
selfHeal: true