mirror of
https://github.com/jlengrand/helidon.git
synced 2026-03-10 08:21:17 +00:00
SE Messaging doc (#2029)
* SE Messaging doc Signed-off-by: Daniel Kec <daniel.kec@oracle.com>
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
:spec-name: MicroProfile Reactive Messaging
|
||||
:description: {spec-name} support in Helidon MP
|
||||
:keywords: helidon, mp, microprofile, messaging
|
||||
:h1Prefix: MP
|
||||
|
||||
== Reactive Messaging
|
||||
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
= Message
|
||||
:toc:
|
||||
:toc-placement: preamble
|
||||
:description: Reactive Messaging Message in Helidon MP
|
||||
:keywords: helidon, mp, messaging, message
|
||||
:h1Prefix: MP
|
||||
|
||||
== Message
|
||||
The Reactive Messaging
|
||||
@@ -69,7 +74,7 @@ https://download.eclipse.org/microprofile/microprofile-reactive-messaging-1.0/mi
|
||||
@Outgoing("consume-and-ack")
|
||||
public PublisherBuilder<Integer> streamOfMessages() {
|
||||
return ReactiveStreams.of(Message.of("This is Payload", () -> {
|
||||
System.out.println("This articular message was acked!");
|
||||
System.out.println("This particular message was acked!");
|
||||
return CompletableFuture.completedFuture(null);
|
||||
})).buildRs();
|
||||
}
|
||||
@@ -77,10 +82,10 @@ public PublisherBuilder<Integer> streamOfMessages() {
|
||||
@Incoming("consume-and-ack")
|
||||
@Acknowledgment(Acknowledgment.Strategy.MANUAL)
|
||||
public void receiveAndAckMessage(Message<String> msg) {
|
||||
// Calling ack() will print "This articular message was acked!" to System.out
|
||||
msg.ack();
|
||||
msg.ack();<1>
|
||||
}
|
||||
----
|
||||
<1> Calling ack() will print "This particular message was acked!" to System.out
|
||||
|
||||
[source,java]
|
||||
.Example of manual acknowledgment
|
||||
@@ -88,7 +93,7 @@ public void receiveAndAckMessage(Message<String> msg) {
|
||||
@Outgoing("consume-and-ack")
|
||||
public PublisherBuilder<Integer> streamOfMessages() {
|
||||
return ReactiveStreams.of(Message.of("This is Payload", () -> {
|
||||
System.out.println("This articular message was acked!");
|
||||
System.out.println("This particular message was acked!");
|
||||
return CompletableFuture.completedFuture(null);
|
||||
})).buildRs();
|
||||
}
|
||||
@@ -96,24 +101,25 @@ public PublisherBuilder<Integer> streamOfMessages() {
|
||||
@Incoming("consume-and-ack")
|
||||
@Acknowledgment(Acknowledgment.Strategy.MANUAL)
|
||||
public void receiveAndAckMessage(Message<String> msg) {
|
||||
// Calling ack() will print "This articular message was acked!" to System.out
|
||||
msg.ack();
|
||||
msg.ack();<1>
|
||||
}
|
||||
----
|
||||
<1> Calling ack() will print "This particular message was acked!" to System.out
|
||||
|
||||
[source,java]
|
||||
.Example of explicit pre-process acknowledgment
|
||||
----
|
||||
@Outgoing("consume-and-ack")
|
||||
public PublisherBuilder<Integer> streamOfMessages() {
|
||||
return ReactiveStreams.of(Message.of("This is Payload", () -> {
|
||||
System.out.println("This articular message was acked!");
|
||||
System.out.println("This particular message was acked!");
|
||||
return CompletableFuture.completedFuture(null);
|
||||
})).buildRs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints to the console:
|
||||
* > This articular message was acked!
|
||||
* > This particular message was acked!
|
||||
* > Method invocation!
|
||||
*/
|
||||
@Incoming("consume-and-ack")
|
||||
@@ -128,7 +134,7 @@ public void receiveAndAckMessage(Message<String> msg) {
|
||||
@Outgoing("consume-and-ack")
|
||||
public PublisherBuilder<Integer> streamOfMessages() {
|
||||
return ReactiveStreams.of(Message.of("This is Payload", () -> {
|
||||
System.out.println("This articular message was acked!");
|
||||
System.out.println("This particular message was acked!");
|
||||
return CompletableFuture.completedFuture(null);
|
||||
})).buildRs();
|
||||
}
|
||||
@@ -136,7 +142,7 @@ public PublisherBuilder<Integer> streamOfMessages() {
|
||||
/**
|
||||
* Prints to the console:
|
||||
* > Method invocation!
|
||||
* > This articular message was acked!
|
||||
* > This particular message was acked!
|
||||
*/
|
||||
@Incoming("consume-and-ack")
|
||||
@Acknowledgment(Acknowledgment.Strategy.POST_PROCESSING)
|
||||
|
||||
@@ -17,8 +17,13 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
= Connector
|
||||
:toc:
|
||||
:toc-placement: preamble
|
||||
:description: Reactive Messaging Connector in Helidon MP
|
||||
:keywords: helidon, mp, messaging, connector
|
||||
:h1Prefix: MP
|
||||
|
||||
== Connector Bean
|
||||
== Messaging Connector Bean
|
||||
|
||||
Messaging connector is just an application scoped bean which implements
|
||||
`IncomingConnectorFactory`, `OutgoingConnectorFactory` or both.
|
||||
@@ -89,8 +94,7 @@ public class ExampleConnector implements IncomingConnectorFactory {
|
||||
@Override
|
||||
public PublisherBuilder<? extends Message<?>> getPublisherBuilder(final Config config) {
|
||||
|
||||
// Config context is merged from channel and connector contexts
|
||||
String firstPropValue = config.getValue("first-test-prop", String.class);
|
||||
String firstPropValue = config.getValue("first-test-prop", String.class);<1>
|
||||
String secondPropValue = config.getValue("second-test-prop", String.class);
|
||||
|
||||
return ReactiveStreams.of(firstPropValue, secondPropValue)
|
||||
@@ -98,17 +102,18 @@ public class ExampleConnector implements IncomingConnectorFactory {
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Config context is merged from channel and connector contexts
|
||||
|
||||
[source,yaml]
|
||||
.Example of channel to connector mapping config with custom properties:
|
||||
----
|
||||
# Channel -> Connector mapping
|
||||
mp.messaging.incoming.from-connector-channel.connector: example-connector
|
||||
# Channel configuration properties
|
||||
mp.messaging.incoming.from-connector-channel.first-test-prop: foo
|
||||
# Connector configuration properties
|
||||
mp.messaging.connector.example-connector.second-test-prop: bar
|
||||
mp.messaging.incoming.from-connector-channel.connector: example-connector<1>
|
||||
mp.messaging.incoming.from-connector-channel.first-test-prop: foo<2>
|
||||
mp.messaging.connector.example-connector.second-test-prop: bar<3>
|
||||
----
|
||||
<1> Channel -> Connector mapping
|
||||
<2> Channel configuration properties
|
||||
<3> Connector configuration properties
|
||||
|
||||
[source,java]
|
||||
.Example consuming from connector:
|
||||
|
||||
88
docs/mp/reactivemessaging/04_kafka.adoc
Normal file
88
docs/mp/reactivemessaging/04_kafka.adoc
Normal file
@@ -0,0 +1,88 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Copyright (c) 2020 Oracle and/or its affiliates.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
= Kafka Connector
|
||||
:toc:
|
||||
:toc-placement: preamble
|
||||
:description: Reactive Messaging support for Kafka in Helidon MP
|
||||
:keywords: helidon, mp, messaging, kafka
|
||||
:h1Prefix: MP
|
||||
|
||||
== Reactive Kafka Connector
|
||||
Connecting streams to Kafka with Reactive Messaging couldn't be easier.
|
||||
|
||||
[source,xml]
|
||||
.Dependencies needed:
|
||||
----
|
||||
<dependency>
|
||||
<groupId>io.helidon.microprofile.messaging</groupId>
|
||||
<artifactId>helidon-microprofile-messaging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.helidon.messaging.kafka</groupId>
|
||||
<artifactId>helidon-messaging-kafka</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
[source,yaml]
|
||||
.Example of connector config:
|
||||
----
|
||||
mp.messaging:
|
||||
|
||||
incoming.from-kafka:
|
||||
connector: helidon-kafka
|
||||
topic: messaging-test-topic-1
|
||||
auto.offset.reset: latest
|
||||
enable.auto.commit: true
|
||||
group.id: example-group-id
|
||||
|
||||
outgoing.to-kafka:
|
||||
connector: helidon-kafka
|
||||
topic: messaging-test-topic-1
|
||||
|
||||
connector:
|
||||
helidon-kafka:
|
||||
bootstrap.servers: localhost:9092
|
||||
key.serializer: org.apache.kafka.common.serialization.StringSerializer
|
||||
value.serializer: org.apache.kafka.common.serialization.StringSerializer
|
||||
key.deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
||||
value.deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
||||
----
|
||||
|
||||
[source,java]
|
||||
.Example of consuming from Kafka:
|
||||
----
|
||||
@Incoming("from-kafka")
|
||||
public void consumeKafka(String msg) {
|
||||
System.out.println("Kafka says: " + msg);
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
[source,java]
|
||||
.Example of producing to Kafka:
|
||||
----
|
||||
@Outgoing("to-kafka")
|
||||
public PublisherBuilder<String> produceToKafka() {
|
||||
return ReactiveStreams.of("test1", "test2");
|
||||
}
|
||||
----
|
||||
|
||||
Don't forget to check out the examples with pre-configured Kafka docker image, for easy testing:
|
||||
|
||||
* https://github.com/oracle/helidon/tree/master/examples/messaging
|
||||
@@ -20,6 +20,113 @@
|
||||
:toc:
|
||||
:toc-placement: preamble
|
||||
:description: Reactive Messaging support in Helidon SE
|
||||
:keywords: helidon, mp, messaging
|
||||
:keywords: helidon, se, messaging
|
||||
:h1Prefix: SE
|
||||
|
||||
== This page is Under Construction and will be available soon
|
||||
== Reactive Messaging
|
||||
|
||||
Asynchronous messaging is a commonly used form of communication in the world of microservices.
|
||||
While its possible to start building your reactive streams directly by combining operators and
|
||||
connecting them to reactive APIs, with Helidon SE Reactive Messaging, you can now use prepared
|
||||
tools for repetitive use case scenarios .
|
||||
|
||||
For example connecting your streams to external services usually requires a lot of boiler-plate
|
||||
code for configuration handling, backpressure propagation, acknowledgement and more.
|
||||
|
||||
For such tasks there is a system of connectors, emitters and means to orchestrate them in Helidon,
|
||||
called *Reactive Messaging*. It's basically an API for connecting and configuring
|
||||
Connectors and Emitters with your reactive streams thru so called <<Channel,Channels>>.
|
||||
|
||||
You may wonder how *Reactive Messaging* relates to
|
||||
<<mp/reactivemessaging/01_introduction.adoc,MicroProfile Reactive Messaging>>.
|
||||
As the making of connectors or even configuring them can be repetitive task leading to
|
||||
the same results, Helidon SE Reactive Messaging supports very same configuration format
|
||||
for connectors as its MicroProfile counterpart does. Also, MP Connectors are reusable in
|
||||
Helidon SE Messaging with some limitation(there is no CDI in Helidon SE).
|
||||
All Messaging connectors in Helidon are made to be universally usable by Helidon MP and SE.
|
||||
|
||||
=== Channel
|
||||
Channel is a named pair of `Publisher` and `Subscriber`. Channels can be connected together by
|
||||
<<Processor,processors>>. Registering of `Publisher` or `Subscriber` for a channel can be done
|
||||
by Messaging API, or configured implicitly for using registered <<se/reactivemessaging/03_connector.adoc,connector>>
|
||||
for generating such `Publisher` or `Subscriber`.
|
||||
|
||||
[source,java]
|
||||
.Example of simple channel:
|
||||
----
|
||||
Channel<String> channel1 = Channel.create("channel1");
|
||||
|
||||
Messaging.builder()
|
||||
.publisher(channel1, Multi.just("message 1", "message 2")
|
||||
.map(Message::of))
|
||||
.listener(channel1, s -> System.out.println("Intecepted message " + s))
|
||||
.build()
|
||||
.start();
|
||||
----
|
||||
|
||||
=== Processor
|
||||
Processor is a typical reactive processor acting as a `Subscriber` to upstream and as a `Publisher`
|
||||
to downstream. In terms of reactive messaging it is able to connect two <<Channel,channels>> to one
|
||||
reactive stream.
|
||||
|
||||
[source,java]
|
||||
.Example of processor usage:
|
||||
----
|
||||
Channel<String> firstChannel = Channel.create("first-channel");
|
||||
Channel<String> secondChannel = Channel.create("second-channel");
|
||||
|
||||
Messaging.builder()
|
||||
.publisher(secondChannel, ReactiveStreams.of("test1", "test2", "test3")
|
||||
.map(Message::of))
|
||||
.processor(secondChannel, firstChannel, ReactiveStreams.<Message<String>>builder()
|
||||
.map(Message::getPayload)
|
||||
.map(String::toUpperCase)
|
||||
.map(Message::of)
|
||||
)
|
||||
.subscriber(firstChannel, ReactiveStreams.<Message<String>>builder()
|
||||
.peek(Message::ack)
|
||||
.map(Message::getPayload)
|
||||
.forEach(s -> System.out.println("Consuming message " + s)))
|
||||
.build()
|
||||
.start();
|
||||
|
||||
>Consuming message TEST1
|
||||
>Consuming message TEST2
|
||||
>Consuming message TEST3
|
||||
----
|
||||
|
||||
=== Message
|
||||
Reactive Messaging in Helidon SE uses the same concept of
|
||||
<<mp/reactivemessaging/02_message.adoc,message wrapping>> as MicroProfile messaging.
|
||||
The only notable difference is that SE Messaging does almost no implicit or automatic
|
||||
acknowledgement due to _no magic_ philosophy of Helidon SE.
|
||||
|
||||
Only exception to this are variants of methods `Messaging.Builder#listener` and
|
||||
`Messaging.Builder#processor` with consumer or function params, conveniently unwrapping payload
|
||||
for you. After such implicit unwrapping is not possible to do a manual acknowledgement, therefore
|
||||
implicit ack before callback is executed is necessary.
|
||||
|
||||
=== Connector
|
||||
Connector concept is a way for connecting <<Channel,channels>> to external sources.
|
||||
To make <<se/reactivemessaging/03_connector.adoc,creation and usage of connectors>>
|
||||
as easy and versatile as possible, Helidon SE Messaging uses same API for connectors
|
||||
like <<mp/reactivemessaging/01_introduction.adoc,MicroProfile Reactive Messaging>> does.
|
||||
This allows connectors to be usable in both flavors of Helidon with one limitation which is
|
||||
that connector has to be able to work without CDI.
|
||||
|
||||
Example of such a versatile connector is Helidon's own:
|
||||
|
||||
* <<se/reactivemessaging/04_kafka.adoc,Kafka connector>>
|
||||
|
||||
|
||||
=== Dependency
|
||||
|
||||
Declare the following dependency in your project:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>io.helidon.messaging</groupId>
|
||||
<artifactId>helidon-messaging</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
198
docs/se/reactivemessaging/03_connector.adoc
Normal file
198
docs/se/reactivemessaging/03_connector.adoc
Normal file
@@ -0,0 +1,198 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Copyright (c) 2020 Oracle and/or its affiliates.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
= Connector
|
||||
:toc:
|
||||
:toc-placement: preamble
|
||||
:description: Reactive Messaging Connector in Helidon SE
|
||||
:keywords: helidon, se, messaging, connector
|
||||
:h1Prefix: SE
|
||||
|
||||
== Messaging Connector
|
||||
Connector for Reactive Messaging is a factory producing Publishers and Subscribers for
|
||||
Channels in Reactive Messaging. Messaging connector is just an implementation of
|
||||
`IncomingConnectorFactory`, `OutgoingConnectorFactory` or both.
|
||||
|
||||
[source,java]
|
||||
.Example connector `example-connector`:
|
||||
----
|
||||
@Connector("example-connector")
|
||||
public class ExampleConnector implements IncomingConnectorFactory, OutgoingConnectorFactory {
|
||||
|
||||
@Override
|
||||
public PublisherBuilder<? extends Message<?>> getPublisherBuilder(Config config) {
|
||||
return ReactiveStreams.of("foo", "bar")
|
||||
.map(Message::of);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubscriberBuilder<? extends Message<?>, Void> getSubscriberBuilder(Config config) {
|
||||
return ReactiveStreams.<Message<?>>builder()
|
||||
.map(Message::getPayload)
|
||||
.forEach(o -> System.out.println("Connector says: " + o));
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[source,yaml]
|
||||
.Example of channel to connector mapping config:
|
||||
----
|
||||
mp.messaging.outgoing.to-connector-channel.connector: example-connector
|
||||
mp.messaging.incoming.from-connector-channel.connector: example-connector
|
||||
----
|
||||
|
||||
[source,java]
|
||||
.Example producing to connector:
|
||||
----
|
||||
Messaging.builder()
|
||||
.connector(new ExampleConnector())
|
||||
.publisher(Channel.create("to-connector-channel"),
|
||||
ReactiveStreams.of("fee", "fie")
|
||||
.map(Message::of)
|
||||
)
|
||||
.build()
|
||||
.start();
|
||||
|
||||
> Connector says: fee
|
||||
> Connector says: fie
|
||||
----
|
||||
|
||||
|
||||
[source,java]
|
||||
.Example consuming from connector:
|
||||
----
|
||||
Messaging.builder()
|
||||
.connector(new ExampleConnector())
|
||||
.subscriber(Channel.create("from-connector-channel"),
|
||||
ReactiveStreams.<Message<String>>builder()
|
||||
.peek(Message::ack)
|
||||
.map(Message::getPayload)
|
||||
.forEach(s -> System.out.println("Consuming: " + s))
|
||||
)
|
||||
.build()
|
||||
.start();
|
||||
|
||||
> Consuming: foo
|
||||
> Consuming: bar
|
||||
----
|
||||
|
||||
=== Configuration
|
||||
Messaging connector in Helidon SE can be configured explicitly by API or implicitly
|
||||
by config following notation of link:https://download.eclipse.org/microprofile/microprofile-reactive-messaging-1.0/microprofile-reactive-messaging-spec.html#_configuration[MicroProfile Reactive Messaging].
|
||||
|
||||
Configuration is being supplied to connector by Messaging implementation,
|
||||
two mandatory attributes are always present:
|
||||
|
||||
* `channel-name` name of the channel which has this connector configured as Publisher or Subscriber, `Channel.create('name-of-channel')` in case of explicit configuration or `mp.messaging.incoming.name-of-channel.connector: connector-name` in case of implicit config
|
||||
* `connector` name of the connector `@Connector("connector-name")`
|
||||
|
||||
[source,java]
|
||||
.Example connector accessing configuration:
|
||||
----
|
||||
@Connector("example-connector")
|
||||
public class ExampleConnector implements IncomingConnectorFactory {
|
||||
|
||||
@Override
|
||||
public PublisherBuilder<? extends Message<?>> getPublisherBuilder(final Config config) {
|
||||
|
||||
String firstPropValue = config.getValue("first-test-prop", String.class);<1>
|
||||
String secondPropValue = config.getValue("second-test-prop", String.class);
|
||||
|
||||
return ReactiveStreams.of(firstPropValue, secondPropValue)
|
||||
.map(Message::of);
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Config context is merged from channel and connector contexts
|
||||
|
||||
==== Explicit Config
|
||||
|
||||
An explicit config for channel's publisher is possible with `Channel.Builder#publisherConfig(Config config)`
|
||||
and for subscriber with `Channel.Builder#subscriberConfig(Config config)`.
|
||||
Supplied <<se/config/01_introduction.adoc,Heldion Config>> is merged with
|
||||
mandatory attributes and any implicit config found. Resulting config is served to Connector.
|
||||
|
||||
[source,java]
|
||||
.Example consuming from Kafka connector with explicit config:
|
||||
----
|
||||
String kafkaServer = config.get("app.kafka.bootstrap.servers").asString().get();
|
||||
String topic = config.get("app.kafka.topic").asString().get();
|
||||
|
||||
Channel<String> fromKafka = Channel.<String>builder()<1><2>
|
||||
.name("from-kafka")
|
||||
.publisherConfig(KafkaConnector.configBuilder()
|
||||
.bootstrapServers(kafkaServer)
|
||||
.groupId("example-group-" + session.getId())
|
||||
.topic(topic)
|
||||
.autoOffsetReset(KafkaConfigBuilder.AutoOffsetReset.LATEST)
|
||||
.enableAutoCommit(true)
|
||||
.keyDeserializer(StringDeserializer.class)
|
||||
.valueDeserializer(StringDeserializer.class)
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
KafkaConnector kafkaConnector = KafkaConnector.create();<3>
|
||||
|
||||
Messaging messaging = Messaging.builder()
|
||||
.connector(kafkaConnector)
|
||||
.listener(fromKafka, payload -> {
|
||||
System.out.println("Kafka says: " + payload);
|
||||
})
|
||||
.build()
|
||||
.start();
|
||||
----
|
||||
<1> Prepare channel for connecting kafka connector with specific publisher configuration -> listener,
|
||||
<2> Channel -> connector mapping is automatic when using `KafkaConnector.configBuilder()`
|
||||
<3> Prepare Kafka connector, can be used by any channel
|
||||
|
||||
==== Implicit Config
|
||||
Implicit config without any hard-coding is possible with <<se/config/01_introduction.adoc,Heldion Config>> following notation of link:https://download.eclipse.org/microprofile/microprofile-reactive-messaging-1.0/microprofile-reactive-messaging-spec.html#_configuration[MicroProfile Reactive Messaging].
|
||||
|
||||
[source,yaml]
|
||||
.Example of channel to connector mapping config with custom properties:
|
||||
----
|
||||
mp.messaging.incoming.from-connector-channel.connector: example-connector<1>
|
||||
mp.messaging.incoming.from-connector-channel.first-test-prop: foo<2>
|
||||
mp.messaging.connector.example-connector.second-test-prop: bar<3>
|
||||
----
|
||||
<1> Channel -> Connector mapping
|
||||
<2> Channel configuration properties
|
||||
<3> Connector configuration properties
|
||||
|
||||
[source,java]
|
||||
.Example consuming from connector:
|
||||
----
|
||||
Messaging.builder()
|
||||
.connector(new ExampleConnector())
|
||||
.listener(Channel.create("from-connector-channel"),
|
||||
s -> System.out.println("Consuming: " + s))
|
||||
.build()
|
||||
.start();
|
||||
|
||||
> Consuming: foo
|
||||
> Consuming: bar
|
||||
----
|
||||
|
||||
=== Reusability in MP Messaging
|
||||
As the API is the same for <<mp/reactivemessaging/01_introduction.adoc,MicroProfile Reactive Messaging>>
|
||||
connectors, all that is needed to make connector work in both ways is annotating it with
|
||||
`@ApplicationScoped`. Such connector is treated as a bean in Helidon MP.
|
||||
|
||||
For specific informations about creating messaging connectors for Helidon MP visit
|
||||
<<mp/reactivemessaging/03_connector.adoc,Messaging Connector Bean>>.
|
||||
166
docs/se/reactivemessaging/04_kafka.adoc
Normal file
166
docs/se/reactivemessaging/04_kafka.adoc
Normal file
@@ -0,0 +1,166 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Copyright (c) 2020 Oracle and/or its affiliates.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
= Kafka Connector
|
||||
:toc:
|
||||
:toc-placement: preamble
|
||||
:description: Reactive Messaging support for Kafka in Helidon SE
|
||||
:keywords: helidon, se, messaging, kafka
|
||||
:h1Prefix: SE
|
||||
|
||||
== Reactive Kafka Connector
|
||||
Connecting streams to Kafka with Reactive Messaging couldn't be easier.
|
||||
|
||||
[source,xml]
|
||||
.Dependencies needed:
|
||||
----
|
||||
<dependency>
|
||||
<groupId>io.helidon.messaging</groupId>
|
||||
<artifactId>helidon-messaging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.helidon.messaging.kafka</groupId>
|
||||
<artifactId>helidon-messaging-kafka</artifactId>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
=== Explicit config with config builder
|
||||
|
||||
[source,java]
|
||||
.Example of consuming from Kafka:
|
||||
----
|
||||
String kafkaServer = config.get("app.kafka.bootstrap.servers").asString().get();
|
||||
String topic = config.get("app.kafka.topic").asString().get();
|
||||
|
||||
Channel<String> fromKafka = Channel.<String>builder()<1><2>
|
||||
.name("from-kafka")
|
||||
.publisherConfig(KafkaConnector.configBuilder()
|
||||
.bootstrapServers(kafkaServer)
|
||||
.groupId("example-group-" + session.getId())
|
||||
.topic(topic)
|
||||
.autoOffsetReset(KafkaConfigBuilder.AutoOffsetReset.LATEST)
|
||||
.enableAutoCommit(true)
|
||||
.keyDeserializer(StringDeserializer.class)
|
||||
.valueDeserializer(StringDeserializer.class)
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
|
||||
KafkaConnector kafkaConnector = KafkaConnector.create();<3>
|
||||
|
||||
Messaging messaging = Messaging.builder()
|
||||
.connector(kafkaConnector)
|
||||
.listener(fromKafka, payload -> {
|
||||
System.out.println("Kafka says: " + payload);
|
||||
})
|
||||
.build()
|
||||
.start();
|
||||
----
|
||||
<1> Prepare a channel for connecting kafka connector with specific publisher configuration -> listener
|
||||
<2> Channel -> connector mapping is automatic when using KafkaConnector.configBuilder()
|
||||
<3> Prepare Kafka connector, can be used by any channel
|
||||
|
||||
[source,java]
|
||||
.Example of producing to Kafka:
|
||||
----
|
||||
String kafkaServer = config.get("app.kafka.bootstrap.servers").asString().get();
|
||||
String topic = config.get("app.kafka.topic").asString().get();
|
||||
|
||||
Channel<String> toKafka = Channel.<String>builder()<1><2>
|
||||
.subscriberConfig(KafkaConnector.configBuilder()
|
||||
.bootstrapServers(kafkaServer)
|
||||
.topic(topic)
|
||||
.keySerializer(StringSerializer.class)
|
||||
.valueSerializer(StringSerializer.class)
|
||||
.build()
|
||||
).build();
|
||||
|
||||
KafkaConnector kafkaConnector = KafkaConnector.create();<3>
|
||||
|
||||
messaging = Messaging.builder()
|
||||
.publisher(toKafka, Multi.just("test1", "test2").map(Message::of))
|
||||
.connector(kafkaConnector)
|
||||
.build()
|
||||
.start();
|
||||
----
|
||||
<1> Prepare a channel for connecting kafka connector with specific publisher configuration -> listener
|
||||
<2> Channel -> connector mapping is automatic when using KafkaConnector.configBuilder()
|
||||
<3> Prepare Kafka connector, can be used by any channel
|
||||
|
||||
=== Implicit Helidon Config
|
||||
|
||||
[source,yaml]
|
||||
.Example of connector config:
|
||||
----
|
||||
mp.messaging:
|
||||
|
||||
incoming.from-kafka:
|
||||
connector: helidon-kafka
|
||||
topic: messaging-test-topic-1
|
||||
auto.offset.reset: latest
|
||||
enable.auto.commit: true
|
||||
group.id: example-group-id
|
||||
|
||||
outgoing.to-kafka:
|
||||
connector: helidon-kafka
|
||||
topic: messaging-test-topic-1
|
||||
|
||||
connector:
|
||||
helidon-kafka:
|
||||
bootstrap.servers: localhost:9092
|
||||
key.serializer: org.apache.kafka.common.serialization.StringSerializer
|
||||
value.serializer: org.apache.kafka.common.serialization.StringSerializer
|
||||
key.deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
||||
value.deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
||||
----
|
||||
|
||||
[source,java]
|
||||
.Example of consuming from Kafka:
|
||||
----
|
||||
Channel<String> fromKafka = Channel.create("from-kafka");
|
||||
|
||||
KafkaConnector kafkaConnector = KafkaConnector.create();<1>
|
||||
|
||||
Messaging messaging = Messaging.builder()
|
||||
.connector(kafkaConnector)
|
||||
.listener(fromKafka, payload -> {
|
||||
System.out.println("Kafka says: " + payload);
|
||||
})
|
||||
.build()
|
||||
.start();
|
||||
----
|
||||
<1> Prepare Kafka connector, can be used by any channel
|
||||
|
||||
[source,java]
|
||||
.Example of producing to Kafka:
|
||||
----
|
||||
Channel<String> toKafka = Channel.create("to-kafka");
|
||||
|
||||
KafkaConnector kafkaConnector = KafkaConnector.create();<1>
|
||||
|
||||
messaging = Messaging.builder()
|
||||
.publisher(toKafka, Multi.just("test1", "test2").map(Message::of))
|
||||
.connector(kafkaConnector)
|
||||
.build()
|
||||
.start();
|
||||
----
|
||||
<1> Prepare Kafka connector, can be used by any channel
|
||||
|
||||
Don't forget to check out the examples with pre-configured Kafka docker image, for easy testing:
|
||||
|
||||
* https://github.com/oracle/helidon/tree/master/examples/messaging
|
||||
@@ -22,9 +22,6 @@ Helidon has its own set of reactive operators that have no dependencies outside
|
||||
These operators can be used with `java.util.concurrent.Flow` based reactive streams.
|
||||
Stream processing operator chain can be easily constructed by `io.helidon.common.reactive.Multi`, or
|
||||
`io.helidon.common.reactive.Single` for streams with single value.
|
||||
Implementation was contributed to Helidon by the world-renown reactive programming expert,
|
||||
project lead of RxJava and co-father of project Reactor,
|
||||
https://twitter.com/akarnokd[Dr. David Karnok].
|
||||
|
||||
[source,java]
|
||||
.Example of Multi usage:
|
||||
|
||||
Reference in New Issue
Block a user