Initial Pulsar Binder Docs

This commit is contained in:
Soby Chacko
2023-02-23 12:42:43 -05:00
parent 44d13742ec
commit 6a1dfdeda8
3 changed files with 132 additions and 0 deletions

View File

@@ -12,6 +12,8 @@
:github: https://github.com/spring-projects-experimental/spring-pulsar
:javadocs: https://docs.spring.io/spring-pulsar/docs/{spring-pulsar-version}/api
:spring-boot-docs: https://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/htmlsingle
:spring-cloud-stream-docs: https://docs.spring.io/spring-cloud-stream/docs/current/reference/html/
:spring-cloud-function: https://spring.io/projects/spring-cloud-function
:apache-pulsar-docs: https://pulsar.apache.org/docs/2.11.x
:apache-pulsar-io-docs: {apache-pulsar-docs}/io-connectors

View File

@@ -25,6 +25,8 @@ include::pulsar-function.adoc[leveloffset=+2]
include::observability.adoc[leveloffset=+2]
include::pulsar-binder.adoc[leveloffset=+2]
:sectnums!:
[[other-resources]]
== Other Resources

View File

@@ -0,0 +1,128 @@
[[pulsar-binder]]
= Spring Cloud Stream Binder for Apache Pulsar
include::attributes.adoc[]
Spring for Apache Pulsar provides a binder for Spring Cloud Stream that we can use for building event driven microservices using pub-sub paradigms.
In this section, we will go through the basic details of this binder.
TIP: For those who are unfamiliar with the concepts in Spring Cloud Stream, you may want to go through the main {spring-cloud-stream-docs}[reference docs] for Spring Cloud Stream to gain an understanding of the core concepts.
== Usage
To use Apache Pulsar binder for Spring Cloud Stream, we need to include the following dependency to your application.
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
<dependencies>
<dependency>
<groupId>org.springframework.pulsar</groupId>
<artifactId>spring-pulsar-spring-cloud-stream-binder</artifactId>
<version>{spring-pulsar-version}</version>
</dependency>
</dependencies>
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
dependencies {
implementation 'org.springframework.pulsar:spring-pulsar-spring-cloud-stream-binder:{spring-pulsar-version}'
}
----
== Overview
In a nutshell, the Spring Cloud Stream binder for Apache Pulsar allows the applications to focus on the business logic rather than dealing with the lower-level details of managing and maintaining Pulsar.
The binder takes care of all those details for the application developer.
Spring Cloud Stream brings a powerful programming model that is based on {spring-cloud-function}[Spring Cloud Function] that allows the app developer to write complex event driven application using a functional style.
Applications can start from a middleware neutral manner and then map Pulsar topics as destinations in Spring Cloud Stream through Spring Boot configuration properties.
Spring Cloud Stream is built on top of Spring Boot and when writing an event driven microservice using Spring Cloud Stream, you are essentially writing a Boot application.
Here is a very simple Spring Cloud Stream application.
====
[source, java]
----
@SpringBootApplication
public class SpringPulsarBinderSampleApp {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public static void main(String[] args) {
SpringApplication.run(SpringPulsarBinderSampleApp.class, args);
}
@Bean
public Supplier<Time> timeSupplier() {
return () -> new Time(String.valueOf(System.currentTimeMillis()));
}
@Bean
public Function<Time, EnhancedTime> timeProcessor() {
return (time) -> {
EnhancedTime enhancedTime = new EnhancedTime(time, "5150");
this.logger.info("PROCESSOR: {} --> {}", time, enhancedTime);
return enhancedTime;
};
}
@Bean
public Consumer<EnhancedTime> timeLogger() {
return (time) -> this.logger.info("SINK: {}", time);
}
record Time(String time) {
}
record EnhancedTime(Time time, String extra) {
}
}
----
====
The above sample application, which is a full-blown Spring Boot application deserves a few explanations. However, on a first pass you can see that this is nothing more than plain Java and a few Spring and Spring Boot annotations.
We have three `Bean` methods here - a `java.util.function.Supplier`, a `java.util.function.Function` and finally a `java.util.function.Consumer`.
The supplier produces the current time in milliseconds, the function takes this time and then enhances it by adding some random data, and then the consumer simply logs the enhanced time.
We omitted all the imports for brevity, but there is nothing Spring Cloud Stream specific here in the entire application.
How does it become a Spring Cloud Stream application that interacts with Apache Pulsar?
For that, you need to include the above dependency for the binder in the application.
Once that dependency is added, you need to provide the following configuration properties.
[source,yaml,indent=0,subs="verbatim"]
----
spring:
cloud:
function:
definition: timeSupplier;timeProcessor;timeLogger;
stream:
bindings:
timeProcessor-in-0:
destination: timeSupplier-out-0
timeProcessor-out-0:
destination: timeProcessor-out-0
timeLogger-in-0:
destination: timeProcessor-out-0
----
With this, the above Spring Boot application has become an end-to-end event driven application that is based on Spring Cloud Stream.
Because we have the Pulsar binder on the classpath, the application interacts with Apache Pulsar.
If there is only one function in the application, then we don't need to tell Spring Cloud Stream to activate the function for execution, since it does that by default.
If there are more than one such functions present in the application, as in our example, we need to instruct Spring Cloud Stream which of those functions that we would like to activate.
In our case, we need all of them to be activated, and we do that through the `spring.cloud.function.definition` property.
By default, the bean name becomes part of the Spring Cloud Stream binding name.
A binding is a fundamental abstract concept in Spring Cloud Stream, using which the framework communicates with the middleware destination.
Almost everything that Spring Cloud Stream does occurs over a concrete binding.
A supplier function has only an output binding, functions have both input and output bindings and consumers have only input binding.
Let's take as an example our supplier bean - `timeSupplier`.
The default binding name for this supplier is going be `timeSupplier-out-0`.
Similarly, the default binding names for the `timeProcessor` function is going to be `timeProcessor-in-0` on the inbound and `timeProcessor-out-0` on the outbound.
Please refer to the Spring Cloud Stream reference docs for details on how you can change the default binding names.
In most situations, using the default binding names is simply enough.
We set the destination on the binding names as shown above.
If a destination is not provided, the binding name becomes the value for the destination as in the case of `timeSupplier-out-0`.
When running the above app, you should see that the supplier executes every second which is then consumed by the function and enhances the time which in turn consumed by the logger consumer.