From 5d5378b88d2e7fb1e90cad13826fe9872c9b3759 Mon Sep 17 00:00:00 2001 From: Jay Bryant Date: Tue, 20 Mar 2018 09:16:43 -0500 Subject: [PATCH] Full editing pass for Spring Cloud Stream core docs I corrected spelling and grammar and edited for a corporate voice. I also added a few links and an image. --- .../asciidoc/images/stream-initializr.png | Bin 0 -> 215901 bytes .../src/main/asciidoc/index.adoc | 5 +- .../src/main/asciidoc/preface.adoc | 223 +-- .../spring-cloud-stream-overview.adoc | 1390 ++++++++--------- 4 files changed, 824 insertions(+), 794 deletions(-) create mode 100644 spring-cloud-stream-core-docs/src/main/asciidoc/images/stream-initializr.png diff --git a/spring-cloud-stream-core-docs/src/main/asciidoc/images/stream-initializr.png b/spring-cloud-stream-core-docs/src/main/asciidoc/images/stream-initializr.png new file mode 100644 index 0000000000000000000000000000000000000000..5d90d5a9e087aa09b45472d358bb79edc85889c9 GIT binary patch literal 215901 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�^QVcDRg&rM1jUo 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

VqB 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 literal 0 HcmV?d00001 diff --git a/spring-cloud-stream-core-docs/src/main/asciidoc/index.adoc b/spring-cloud-stream-core-docs/src/main/asciidoc/index.adoc index 913c04887..f9706282a 100644 --- a/spring-cloud-stream-core-docs/src/main/asciidoc/index.adoc +++ b/spring-cloud-stream-core-docs/src/main/asciidoc/index.adoc @@ -1,8 +1,8 @@ [[spring-cloud-stream-reference]] = Spring Cloud Stream Reference Guide -Sabby Anandan; Marius Bogoevici; Eric Bottard; Mark Fisher; Ilayaperumal Gopinathan; Gunnar Hillert; Mark Pollack; Patrick Peralta; Glenn Renfro; Thomas Risberg; Dave Syer; David Turanski; Janne Valkealahti; Benjamin Klein; Vinicius Carvalho; Oleg Zhurakousky +Sabby Anandan; Marius Bogoevici; Eric Bottard; Mark Fisher; Ilayaperumal Gopinathan; Gunnar Hillert; Mark Pollack; Patrick Peralta; Glenn Renfro; Thomas Risberg; Dave Syer; David Turanski; Janne Valkealahti; Benjamin Klein; Vinicius Carvalho; Oleg Zhurakousky; Jay Bryant :doctype: book -:toc: +:toc: left :toclevels: 4 :source-highlighter: prettify :numbered: @@ -13,6 +13,7 @@ Sabby Anandan; Marius Bogoevici; Eric Bottard; Mark Fisher; Ilayaperumal Gopinat :spring-cloud-stream-docs-version: current :spring-cloud-stream-docs: http://docs.spring.io/spring-cloud-stream/docs/{spring-cloud-stream-docs-version}/reference :spring-cloud-stream-docs-current: http://docs.spring.io/spring-cloud-stream/docs/current-SNAPSHOT/reference/html/ +:spring-cloud-stream-javadoc-current: https://docs.spring.io/spring-cloud-stream/docs/current-SNAPSHOT/api/ :github-repo: spring-cloud/spring-cloud-stream :github-raw: http://raw.github.com/{github-repo}/{github-tag} :github-code: http://github.com/{github-repo}/tree/{github-tag} diff --git a/spring-cloud-stream-core-docs/src/main/asciidoc/preface.adoc b/spring-cloud-stream-core-docs/src/main/asciidoc/preface.adoc index 928a8951e..dcd32f4f0 100644 --- a/spring-cloud-stream-core-docs/src/main/asciidoc/preface.adoc +++ b/spring-cloud-stream-core-docs/src/main/asciidoc/preface.adoc @@ -1,42 +1,62 @@ - - == Quick Start -You can try Spring Cloud Stream in less then 5 min even before you jump into any details and the following _three-step guide_ will help. +You can try Spring Cloud Stream in less then 5 min even before you jump into any details by following this three-step guide. -We'll create a simple Spring Cloud Stream application which receives messages coming from the messaging middleware of your choice (more on this later) and -logs received messages to the console. We'll call it _LoggingConsumer_. While not very practical it will certainly provide a good introduction to some of the main concepts +We show you how to create a Spring Cloud Stream application that receives messages coming from the messaging middleware of your choice (more on this later) and logs received messages to the console. +We call it `LoggingConsumer`. +While not very practical, it provides a good introduction to some of the main concepts and abstractions, making it easier to digest the rest of this user guide. -So let's get started. . . +The three steps are as follows: -==== Step One - Create sample Application using Spring Initilaizer -Visit the https://start.spring.io[Spring Initializr]. This is where we'll generate our _LoggingConsumer_ application. +. <> +. <> +. <> -In the _Dependencies_ start typing 'stream' and _Cloud Stream_ option should pop up. Select it. Now start typing either 'kafka' or 'rabbit'. Basically this is where you are choosing -what messaging midleware this application will be bound to. Choose the one you have already installed and/or feel more comfortable with installing/running. -Also, as you can see from the Initilaizer screen there are few other options you can choose. For example, you can choose Gradle as your build tool instead of the default Maven. -With the _Dependencies_ selected the only other thing you have to identify is the application name - _logging-consumer_. -Your configuration screeen should now contain the following: +[[spring-cloud-stream-preface-creating-sample-application]] +==== Creating a Sample Application by Using Spring Initializr +To get started, visit the https://start.spring.io[Spring Initializr]. From there, you can generate our `LoggingConsumer` application. To do so: - Dependencies: Cloud Stream, RabbitMQ (or Kafka) - Group: com.example - default - Artifact: logging-consumer - Spring Boot Version: 2.0.0 (or above) - default +. In the *Dependencies* section, start typing `stream`. +When the "`Cloud Stream`" option should appears, select it. +. Start typing either 'kafka' or 'rabbit'. +. Select "`Kafka`" or "`RabbitMQ`". ++ +Basically, you choose the messaging middleware to which your application binds. +We recommend using the one you have already installed or feel more comfortable with installing and running. +Also, as you can see from the Initilaizer screen, there are a few other options you can choose. +For example, you can choose Gradle as your build tool instead of Maven (the default). +. In the *Artifact* field, type 'logging-consumer`. ++ +The value of the *Artifact* field becomes the application name. +If you chose RabbitMQ for the middleware, your Spring Initializr should now be as follows: ++ +image::stream-initializr.png[] +. Click the *Generate Project* button. ++ +Doing so downloads the zipped version of the generated project to your hard drive. +. Unzip the file into the folder you want to use as your project directory. -Click on _Generate Project_ button. This will donwload the zipped version of the generated project to your hard drive. Unzip it and you're ready for Step Two. +TIP: We encourage you to explore the many possibilities available in the Spring Initializr. +It lets you create many different kinds of Spring applications. -==== Step Two - Import project into the IDE -Here you simply import the project into your IDE of choice. -Please keep in mind that dependening on the IDE you may need to follow a specific import procedures. For example depending on how the project was generated (Maven or Gradle) -you may need to follow specific import procedure (e.g., in Eclipse/STS: `File -> Import -> Maven -> Existing Maven Project`). +[[spring-cloud-stream-preface-importing-project]] +==== Importing the Project into Your IDE -Ones imported the project must have no errors of any kind and `src/main/java` should also contain `com.example.loggingconsumer.LoggingConsumerApplication`. +Now you can import the project into your IDE. +Keep in mind that, depending on the IDE, you may need to follow a specific import procedure. +For example, depending on how the project was generated (Maven or Gradle), you may need to follow specific import procedure (for example, in Eclipse or STS, you need to use File -> Import -> Maven -> Existing Maven Project). -Technically at this point you can just run the application's main class since it's already a valid _Spring Boot_ application, but it does not do anything, so let's add some code. +Once imported, the project must have no errors of any kind. Also, `src/main/java` should contain `com.example.loggingconsumer.LoggingConsumerApplication`. -==== Step Three - Add message handler, build and run -Modify the `com.example.loggingconsumer.LoggingConsumerApplication` to look as follows: +Technically, at this point, you can run the application's main class. +It is already a valid Spring Boot application. +However, it does not do anything, so we want to add some code. + +[[spring-cloud-stream-preface-adding-message-handler]] +==== Adding a Message Handler, Building, and Running + +Modify the `com.example.loggingconsumer.LoggingConsumerApplication` class to look as follows: [source, java] ---- @@ -68,18 +88,21 @@ public class LoggingConsumerApplication { } ---- -As you can see from the above: +As you can see from the preceding listing: -* We've enabled `Sink` binding (input-no-output) via `@EnableBinding(Sink.class)`. This will signal to the framework to initiate binding to the messaging middleware where -it will auto-create the destination (i.e., queue, topic) which will be bound to `Sink.INPUT` channel. -* We've added handler method to receive incoming Message as type `Person`. What this means is that here youcan already observe one of the core features of the framework where -it will attempt to automatically convert incoming message's payload to type `Person`. +* We have enabled `Sink` binding (input-no-output) by using `@EnableBinding(Sink.class)`. +Doing so signals to the framework to initiate binding to the messaging middleware, where it automatically creates the destination (that is, queue, topic, and others) that are bound to the `Sink.INPUT` channel. +* We have added a `handler` method to receive incoming messages of type `Person`. +Doing so lets you see one of the core features of the framework: It tries to automatically convert incoming message payloads to type `Person`. -This is it, we now have a fully functional Spring Cloud Stream application that does something. From here for simplicity we'll assume RabbitMQ was selected in _step one_. -Assuming you have RabbitMQ installed and running, start the application by simply running its `main` method. +You now have a fully functional Spring Cloud Stream application that does listens for messages. +From here, for simplicity, we assume you selected RabbitMQ in <>. +Assuming you have RabbitMQ installed and running, you can start the application by running its `main` method in your IDE. You should see following output: +[source] +---- --- [ main] c.s.b.r.p.RabbitExchangeQueueProvisioner : declaring queue for inbound: input.anonymous.CbMIwdkJSBO1ZoPDOtHtCg, bound to: input --- [ main] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [localhost:5672] --- [ main] o.s.a.r.c.CachingConnectionFactory : Created new connection: rabbitConnectionFactory#2a3a299:0/SimpleConnection@66c83fc8. . . @@ -87,50 +110,63 @@ You should see following output: --- [ main] o.s.i.a.i.AmqpInboundChannelAdapter : started inbound.input.anonymous.CbMIwdkJSBO1ZoPDOtHtCg . . . --- [ main] c.e.l.LoggingConsumerApplication : Started LoggingConsumerApplication in 2.531 seconds (JVM running for 2.897) +---- -Go to RabbitMQ management console or any other RabbitMQ client and simply send message to `input.anonymous.CbMIwdkJSBO1ZoPDOtHtCg` -(NOTE: the `anonymous.CbMIwdkJSBO1ZoPDOtHtCg` part represents the group name and is generated and will be different in your environment. For something more -predictable you can use explicit group name via `spring.cloud.stream.bindings.input.group=hello`). +Go to the RabbitMQ management console or any other RabbitMQ client and send a message to `input.anonymous.CbMIwdkJSBO1ZoPDOtHtCg`. +The `anonymous.CbMIwdkJSBO1ZoPDOtHtCg` part represents the group name and is generated, so it is bound to be different in your environment. +For something more predictable, you can use an explicit group name by setting `spring.cloud.stream.bindings.input.group=hello` (or whatever name you like). -The contents of the message should be JSON representation of `Person` class, so let's send this: +The contents of the message should be a JSON representation of the `Person` class, as follows: - {"name":"Turd Ferguson"} + {"name":"Sam Spade"} -And in your console you should see: +Then, in your console, you should see: - Received: Turd Ferguson +`Received: Sam Spade` -You can also build/package your application into a boot jar (i.e., `./mvnw clean install`) and run the built JAR using `java -jar` command. +You can also build and package your application into a boot jar (by using `./mvnw clean install`) and run the built JAR by using the `java -jar` command. -That is all! +Now you have a working (albeit very basic) Spring Cloud Stream application. == What's New in 2.0? -Spring Cloud Stream introduces quite a number of new features, enhancements and changes. The following sections outline most notable ones. +Spring Cloud Stream introduces a number of new features, enhancements, and changes. The following sections outline the most notable ones: +* <> +* <> + +[[spring-cloud-stream-preface-new-features]] === New Features and Components -==== Polling Consumer -Introduction of _polled consumers_, where the application can control message processing rates. Please refer to the appropriate section for more details. -You can also read this blog for more details https://spring.io/blog/2018/02/27/spring-cloud-stream-2-0-polled-consumers +* *Polling Consumers*: Introduction of polled consumers, which let the application can control message processing rates. +See "`<>`" for more details. +You can also read https://spring.io/blog/2018/02/27/spring-cloud-stream-2-0-polled-consumers[this blog post] for more details. -==== Micrometer support +* *Micrometer Support*: Metrics has been switched to use https://micrometer.io/[Micrometer]. +`MeterRegistry` is also provided as a bean so that custom applications can autowire it to capture custom metrics. +See "`<>`" for more details. -Metrics has been switched to use https://micrometer.io/[Micrometer]. `MeterRegistry` is also provided as a bean so custom application can autowire it to capture custom metrics. -Please refer to the appropriate section for more details +* *New Actuator Binding Controls*: New actuator binding controls let you both visualize and control the Bindings lifecycle. +For more details, see <>. -==== New Actuator Binding controls -There are now new new Actuator binding controls to both visualize as well as control Bindings lifecycle. For more details please visit <> +* *Configurable RetryTemplate*: Aside from providing properties to configure `RetryTemplate`, we now let you provide your own template, effectively overriding the one provided by the framework. +To use it, configure it as a `@Bean` in your application. -==== Configurable RetryTemplate -Aside from providing properties to configure `RetryTemplate` we now allow you to provide your own effectively overriding the one provided by the framework. Simply configure -it as a `@Bean` in your application. +[[spring-cloud-stream-preface-notable-enhancements]] +=== Notable Enhancements -=== Notable changes and enhancements +This version includes the following notable enhancements: -==== Both Actuator and Web dependencies are now optional +* <> +* <> +* <> -This helps to slim down the footprint of the deployed application in the event neither of the functionality is required. -It also allows one to swicth between the reactive and conventional web paradigms by adding one of the following dependencies manually: +[[spring-cloud-stream-preface-actuator-web-dependencies]] +==== Both Actuator and Web Dependencies Are Now Optional + +This change slims down the footprint of the deployed application in the event neither actuator nor web dependencies required. +It also lets you switch between the reactive and conventional web paradigms by manually adding one of the following dependencies. + +The following listing shows how to add the conventional web framework: [source,xml] ---- @@ -138,7 +174,9 @@ It also allows one to swicth between the reactive and conventional web paradigms spring-boot-starter-web ---- -or + +The following listing shows how to add the reactive web framework: + [source,xml] ---- @@ -147,7 +185,7 @@ or ---- -Actuator dependency can be added as follows: +The following list shows how to add the actuator dependency: [source,xml] ---- @@ -156,42 +194,53 @@ Actuator dependency can be added as follows: ---- -==== Content-type negotiation improvenents -One of the core themes for 2.0 is improvements (both consistency and performance) around content-type negotiation and message conversion. -The following summary outlines notable changes and improvements. Please refer to the appropriate section for more details as well as this blog -https://spring.io/blog/2018/02/26/spring-cloud-stream-2-0-content-type-negotiation-and-transformation. +[[spring-cloud-stream-preface-content-type-negotiation-improvements]] +==== Content-type Negotiation Improvements -* All message conversion is now handled *only* by `MessageConverters`. -* Introduction of `@StreamMessageConverter` annotation to provide custom `MessageConverters`. -* Introduction of the default _Content Type_ as `application/json` which needs to be taken into consideration when migrating 1.3 -application and/or operating in the mixed mode (i.e., 1.3 producer -> 2.0 consumer). -* Messages with textual payloads and _contentType_ `text/...` or `.../json` are no longer converted to `Message` for cases where argument type of the provided `MessageHandler` -can not be determnied (i.e., `public void handle(Message message)` or `public void handle(Object payload)`). Further more, a strong argument type may not be enough -to properly convert messages, so `contentType` header is may be used as supplement by some `MessageConverters`. +One of the core themes for verion 2.0 is improvements (in both consistency and performance) around content-type negotiation and message conversion. +The following summary outlines the notable changes and improvements in this area. +See the "`<>`" section for more details. +Also https://spring.io/blog/2018/02/26/spring-cloud-stream-2-0-content-type-negotiation-and-transformation[this blog post] contains more detail. +* All message conversion is now handled *only* by `MessageConverter` objects. +* We introduced the `@StreamMessageConverter` annotation to provide custom `MessageConverter` objects. +* We introduced the default `Content Type` as `application/json`, which needs to be taken into consideration when migrating 1.3 application or operating in the mixed mode (that is, 1.3 producer -> 2.0 consumer). +* Messages with textual payloads and a `contentType` of `text/...` or `.../json` are no longer converted to `Message` for cases where the argument type of the provided `MessageHandler` can not be determined (that is, `public void handle(Message message)` or `public void handle(Object payload)`). +Furthermore, a strong argument type may not be enough to properly convert messages, so the `contentType` header may be used as a supplement by some `MessageConverters`. + +[[spring-cloud-stream-preface-notable-deprecations]] === Notable Deprecations -==== Java serialization (Java native and Kryo) -* `JavaSerializationMessageConverter` and `KryoMessageConverter`. While these two converters remain for now, they will be moved out of the core packages and support in the future. -The main reason for this deprecation is to signal the issue _type-based language-specific_ serialization couuld cause in the distributed environments, where Producers and Consumers -may not only depend on different JVM versions or have different versions of supporting libraries (i.e., Kryo), but to also draw the attention to the fact that Consumers and Producers -may and in a lot of cases are non-Java based. -==== Deprecated classes and methods -Following is a quick summary of notable deprecations. See corresponding javadocs fort more details. +As of version 2.0, the following items have been deprecated: -* `SharedChannelRegistry` in favor of `SharedBindingTargetRegistry`. -* `Bindings` - beans qualified by it are already uniquely identified by their type. For example, provided `Source`, `Processor` or custom bindings: +* <> +* <> + +[[spring-cloud-stream-preface-deprecation-java-serialization]] +==== Java Serialization (Java Native and Kryo) + +`JavaSerializationMessageConverter` and `KryoMessageConverter` remain for now. However, we plan to move them out of the core packages and support in the future. +The main reason for this deprecation is to flag the issue that type-based, language-specific serialization could cause in distributed environments, where Producers and Consumers may depend on different JVM versions or have different versions of supporting libraries (that is, Kryo). +We also wanted to draw the attention to the fact that Consumers and Producers may not be Java-based. + +[[spring-cloud-stream-preface-deprecation-classes-methods]] +==== Deprecated Classes and Methods +The following is a quick summary of notable deprecations. See the corresponding {spring-cloud-stream-javadoc-current}[javadoc] for more details. + +* `SharedChannelRegistry`. Use `SharedBindingTargetRegistry`. +* `Bindings`. +Beans qualified by it are already uniquely identified by their type -- for example, provided `Source`, `Processor`, or custom bindings: [source,java] ---- -public interface Foo { - String OUTPUT = "fooOutput"; +public interface Sample { + String OUTPUT = "sampleOutput"; - @Output(Foo.OUTPUT) + @Output(Sample.OUTPUT) MessageChannel output(); } ---- * `HeaderMode.raw`. Use `none`, `headers` or `embeddedHeaders` -* `ProducerProperties.partitionKeyExtractorClass` in favor of `partitionKeyExtractorName` and `ProducerProperties.partitionSelectorClass` in favor of `partitionSelectorName`. -This is to ensure that both components are Spring configured/managed and referenced in Spring-friendly way. -* `BinderAwareRouterBeanPostProcessor` - while the component exists it is no longer a Bean Post Processor and will be renamed in the future. -* `BinderProperties.setEnvironment(Properties environment)` in favor of `BinderProperties.setEnvironment(Map environment)`. +* `ProducerProperties.partitionKeyExtractorClass`. Use `partitionKeyExtractorName` and `ProducerProperties.partitionSelectorClass` in favor of `partitionSelectorName`. +This change ensures that both components are Spring configured and managed and are referenced in a Spring-friendly way. +* `BinderAwareRouterBeanPostProcessor`. While the component exists, it is no longer a Bean Post Processor and will be renamed in the future. +* `BinderProperties.setEnvironment(Properties environment)`. Use `BinderProperties.setEnvironment(Map environment)`. diff --git a/spring-cloud-stream-core-docs/src/main/asciidoc/spring-cloud-stream-overview.adoc b/spring-cloud-stream-core-docs/src/main/asciidoc/spring-cloud-stream-overview.adoc index 31c85516c..fa7ddeaac 100644 --- a/spring-cloud-stream-core-docs/src/main/asciidoc/spring-cloud-stream-overview.adoc +++ b/spring-cloud-stream-core-docs/src/main/asciidoc/spring-cloud-stream-overview.adoc @@ -4,13 +4,15 @@ This section goes into more detail about how you can work with Spring Cloud Stre It covers topics such as creating and running stream applications. -- +[[spring-cloud-stream-overview-introducing]] == Introducing Spring Cloud Stream + Spring Cloud Stream is a framework for building message-driven microservice applications. -Spring Cloud Stream builds upon Spring Boot to create standalone, production-grade Spring applications, and uses Spring Integration to provide connectivity to message brokers. +Spring Cloud Stream builds upon Spring Boot to create standalone, production-grade Spring applications and uses Spring Integration to provide connectivity to message brokers. It provides opinionated configuration of middleware from several vendors, introducing the concepts of persistent publish-subscribe semantics, consumer groups, and partitions. You can add the `@EnableBinding` annotation to your application to get immediate connectivity to a message broker, and you can add `@StreamListener` to a method to cause it to receive events for stream processing. -The following is a simple sink application which receives external messages. +The following example shows a sink application that receives external messages: [source,java] ---- @@ -30,12 +32,10 @@ public class VoteRecordingSinkApplication { ---- The `@EnableBinding` annotation takes one or more interfaces as parameters (in this case, the parameter is a single `Sink` interface). -An interface declares input and/or output channels. -Spring Cloud Stream provides the interfaces `Source`, `Sink`, and `Processor`; you can also define your own interfaces. +An interface declares input and output channels. +Spring Cloud Stream provides the `Source`, `Sink`, and `Processor` interfaces. You can also define your own interfaces. - - -The following is the definition of the `Sink` interface: +The following listing shows the definition of the `Sink` interface: [source,java] ---- @@ -47,11 +47,13 @@ public interface Sink { } ---- -The `@Input` annotation identifies an _input channel_, through which received messages enter the application; the `@Output` annotation identifies an _output channel_, through which published messages leave the application. -The `@Input` and `@Output` annotations can take a channel name as a parameter; if a name is not provided, the name of the annotated method will be used. +The `@Input` annotation identifies an input channel, through which received messages enter the application. +The `@Output` annotation identifies an output channel, through which published messages leave the application. +The `@Input` and `@Output` annotations can take a channel name as a parameter. +If a name is not provided, the name of the annotated method is used. -Spring Cloud Stream will create an implementation of the interface for you. -You can use this in the application by autowiring it, as in the following example of a test case. +Spring Cloud Stream creates an implementation of the interface for you. +You can use this in the application by autowiring it, as shown in the following example (from a test case): [source,java] ---- @@ -76,18 +78,18 @@ public class StreamApplicationTests { Spring Cloud Stream provides a number of abstractions and primitives that simplify the writing of message-driven microservice applications. This section gives an overview of the following: -* Spring Cloud Stream's application model -* The Binder abstraction -* Persistent publish-subscribe support -* Consumer group support -* Partitioning support -* A pluggable Binder API - +* <> +* <> +* <> +* <> +* <> +* <> +[[spring-cloud-stream-overview-application-model]] === Application Model A Spring Cloud Stream application consists of a middleware-neutral core. -The application communicates with the outside world through input and output _channels_ injected into it by Spring Cloud Stream. +The application communicates with the outside world through input and output channels injected into it by Spring Cloud Stream. Channels are connected to external brokers through middleware-specific Binder implementations. .Spring Cloud Stream Application @@ -95,24 +97,27 @@ image::SCSt-with-binder.png[width=300,scaledwidth="50%"] ==== Fat JAR -Spring Cloud Stream applications can be run in standalone mode from your IDE for testing. -To run a Spring Cloud Stream application in production, you can create an executable (or "fat") JAR by using the standard Spring Boot tooling provided for Maven or Gradle. +Spring Cloud Stream applications can be run in stand-alone mode from your IDE for testing. +To run a Spring Cloud Stream application in production, you can create an executable (or "`fat`") JAR by using the standard Spring Boot tooling provided for Maven or Gradle. See the https://docs.spring.io/spring-boot/docs/current/reference/html/howto-build.html#howto-create-an-executable-jar-with-maven[Spring Boot Reference Guide] for more details. +[[spring-cloud-stream-overview-binder-abstraction]] === The Binder Abstraction Spring Cloud Stream provides Binder implementations for https://github.com/spring-cloud/spring-cloud-stream/tree/master/spring-cloud-stream-binders/spring-cloud-stream-binder-kafka[Kafka] and https://github.com/spring-cloud/spring-cloud-stream/tree/master/spring-cloud-stream-binders/spring-cloud-stream-binder-rabbit[Rabbit MQ]. Spring Cloud Stream also includes a https://github.com/spring-cloud/spring-cloud-stream/blob/master/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/binder/TestSupportBinder.java[TestSupportBinder], which leaves a channel unmodified so that tests can interact with channels directly and reliably assert on what is received. -You can use the extensible API to write your own Binder. +You can also use the extensible API to write your own Binder. Spring Cloud Stream uses Spring Boot for configuration, and the Binder abstraction makes it possible for a Spring Cloud Stream application to be flexible in how it connects to middleware. -For example, deployers can dynamically choose, at runtime, the destinations (e.g., the Kafka topics or RabbitMQ exchanges) to which channels connect. +For example, deployers can dynamically choose, at runtime, the destinations (such as the Kafka topics or RabbitMQ exchanges) to which channels connect. Such configuration can be provided through external configuration properties and in any form supported by Spring Boot (including application arguments, environment variables, and `application.yml` or `application.properties` files). -In the sink example from the <<_introducing_spring_cloud_stream>> section, setting the application property `spring.cloud.stream.bindings.input.destination` to `raw-sensor-data` will cause it to read from the `raw-sensor-data` Kafka topic, or from a queue bound to the `raw-sensor-data` RabbitMQ exchange. +In the sink example from the <> section, setting the `spring.cloud.stream.bindings.input.destination` application property to `raw-sensor-data` causes it to read from the `raw-sensor-data` Kafka topic or from a queue bound to the `raw-sensor-data` RabbitMQ exchange. Spring Cloud Stream automatically detects and uses a binder found on the classpath. -You can easily use different types of middleware with the same code: just include a different binder at build time. -For more complex use cases, you can also package multiple binders with your application and have it choose the binder, and even whether to use different binders for different channels, at runtime. +You can use different types of middleware with the same code. +To do so, include a different binder at build time. +For more complex use cases, you can also package multiple binders with your application and have it choose the binder( and even whether to use different binders for different channels) at runtime. +[[spring-cloud-stream-overview-persistent-publish-subscribe-support]] === Persistent Publish-Subscribe Support Communication between applications follows a publish-subscribe model, where data is broadcast through shared topics. @@ -122,10 +127,10 @@ This can be seen in the following figure, which shows a typical deployment for a image::SCSt-sensors.png[width=300,scaledwidth="50%"] Data reported by sensors to an HTTP endpoint is sent to a common destination named `raw-sensor-data`. -From the destination, it is independently processed by a microservice application that computes time-windowed averages and by another microservice application that ingests the raw data into HDFS. +From the destination, it is independently processed by a microservice application that computes time-windowed averages and by another microservice application that ingests the raw data into HDFS (Hadoop Distributed File System). In order to process the data, both applications declare the topic as their input at runtime. -The publish-subscribe communication model reduces the complexity of both the producer and the consumer, and allows new applications to be added to the topology without disruption of the existing flow. +The publish-subscribe communication model reduces the complexity of both the producer and the consumer and lets new applications be added to the topology without disruption of the existing flow. For example, downstream from the average-calculating application, you can add an application that calculates the highest temperature values for display and monitoring. You can then add another application that interprets the same flow of averages for fault detection. Doing all communication through shared topics rather than point-to-point queues reduces coupling between microservices. @@ -136,9 +141,9 @@ By using native middleware support, Spring Cloud Stream also simplifies use of t [[consumer-groups]] === Consumer Groups While the publish-subscribe model makes it easy to connect applications through shared topics, the ability to scale up by creating multiple instances of a given application is equally important. -When doing this, different instances of an application are placed in a competing consumer relationship, where only one of the instances is expected to handle a given message. +When doing so, different instances of an application are placed in a competing consumer relationship, where only one of the instances is expected to handle a given message. -Spring Cloud Stream models this behavior through the concept of a _consumer group_. +Spring Cloud Stream models this behavior through the concept of a consumer group. (Spring Cloud Stream consumer groups are similar to and inspired by Kafka consumer groups.) Each consumer binding can use the `spring.cloud.stream.bindings..group` property to specify a group name. For the consumers shown in the following figure, this property would be set as `spring.cloud.stream.bindings..group=hdfsWrite` or `spring.cloud.stream.bindings..group=average`. @@ -146,7 +151,7 @@ For the consumers shown in the following figure, this property would be set as ` .Spring Cloud Stream Consumer Groups image::SCSt-groups.png[width=300,scaledwidth="50%"] -All groups which subscribe to a given destination receive a copy of published data, but only one member of each group receives a given message from that destination. +All groups that subscribe to a given destination receive a copy of published data, but only one member of each group receives a given message from that destination. By default, when a group is not specified, Spring Cloud Stream assigns the application to an anonymous and independent single-member consumer group that is in a publish-subscribe relationship with all other consumer groups. [[consumer-types]] @@ -154,49 +159,47 @@ By default, when a group is not specified, Spring Cloud Stream assigns the appli Two types of consumer are supported: -- Message-driven (sometimes referred to as Asynchronous) -- Polled (sometimes referred to as Synchronous) +* Message-driven (sometimes referred to as Asynchronous) +* Polled (sometimes referred to as Synchronous) -Prior to _version 2.0_, only asynchronous consumers were supported, where a message is delivered as soon as it is available (and there is a thread available to process it). +Prior to version 2.0, only asynchronous consumers were supported. A message is delivered as soon as it is available and a thread is available to process it. -You might want to use a synchronous consumer when you wish to control the rate at which messages are processed. +When you wish to control the rate at which messages are processed, you might want to use a synchronous consumer. +// TODO This needs more description. A sentence parallel to the last sentence of the preceding paragraph would help. [[durability]] ==== Durability -Consistent with the opinionated application model of Spring Cloud Stream, consumer group subscriptions are _durable_. -That is, a binder implementation ensures that group subscriptions are persistent, and ones at least one subscription for a group has been created, the group will receive messages, even if they are sent while all applications in the group are stopped. +Consistent with the opinionated application model of Spring Cloud Stream, consumer group subscriptions are durable. +That is, a binder implementation ensures that group subscriptions are persistent and that, once at least one subscription for a group has been created, the group receives messages, even if they are sent while all applications in the group are stopped. [NOTE] ==== Anonymous subscriptions are non-durable by nature. -For some binder implementations (e.g., RabbitMQ), it is possible to have non-durable group subscriptions. +For some binder implementations (such as RabbitMQ), it is possible to have non-durable group subscriptions. ==== In general, it is preferable to always specify a consumer group when binding an application to a given destination. When scaling up a Spring Cloud Stream application, you must specify a consumer group for each of its input bindings. -This prevents the application's instances from receiving duplicate messages (unless that behavior is desired, which is unusual). +Doing so prevents the application's instances from receiving duplicate messages (unless that behavior is desired, which is unusual). [[partitioning]] === Partitioning Support -Spring Cloud Stream provides support for _partitioning_ data between multiple instances of a given application. -In a partitioned scenario, the physical communication medium (e.g., the broker topic) is viewed as being structured into multiple partitions. +Spring Cloud Stream provides support for partitioning data between multiple instances of a given application. +In a partitioned scenario, the physical communication medium (such as the broker topic) is viewed as being structured into multiple partitions. One or more producer application instances send data to multiple consumer application instances and ensure that data identified by common characteristics are processed by the same consumer instance. Spring Cloud Stream provides a common abstraction for implementing partitioned processing use cases in a uniform fashion. -Partitioning can thus be used whether the broker itself is naturally partitioned (e.g., Kafka) or not (e.g., RabbitMQ). +Partitioning can thus be used whether the broker itself is naturally partitioned (for example, Kafka) or not (for example, RabbitMQ). .Spring Cloud Stream Partitioning image::SCSt-partitioning.png[width=300,scaledwidth="50%"] -Partitioning is a critical concept in stateful processing, where it is critical, for either performance or consistency reasons, to ensure that all related data is processed together. +Partitioning is a critical concept in stateful processing, where it is critical (for either performance or consistency reasons) to ensure that all related data is processed together. For example, in the time-windowed average calculation example, it is important that all measurements from any given sensor are processed by the same application instance. -[NOTE] -==== -To set up a partitioned processing scenario, you must configure both the data-producing and the data-consuming ends. -==== +NOTE: To set up a partitioned processing scenario, you must configure both the data-producing and the data-consuming ends. == Programming Model @@ -205,10 +208,20 @@ Spring Cloud Stream provides a number of predefined annotations for declaring bo === Declaring and Binding Producers and Consumers -==== Triggering Binding Via `@EnableBinding` +The following topics describe how to declare and bind producers and consumers: -You can turn a Spring application into a Spring Cloud Stream application by applying the `@EnableBinding` annotation to one of the application's configuration classes. -The `@EnableBinding` annotation itself is meta-annotated with `@Configuration` and triggers the configuration of Spring Cloud Stream infrastructure: +* <> +* <> +* <> +* <> +* <> +* <> + +[[spring-cloud-stream-overview-triggering-binding-enablebinding]] +==== Triggering Binding by Using `@EnableBinding` + +You can turn a Spring Boot application into a Spring Cloud Stream application by applying the `@EnableBinding` annotation to one of the application's configuration classes. +The `@EnableBinding` annotation itself is meta-annotated with `@Configuration` and triggers the configuration of the Spring Cloud Stream infrastructure. The following example shows a typical use case: [source,java] ---- @@ -222,19 +235,18 @@ public @interface EnableBinding { } ---- -The `@EnableBinding` annotation can take as parameters one or more interface classes that contain methods which represent bindable components (typically message channels). +The `@EnableBinding` annotation can take as parameters one or more interface classes that contain methods representing bindable components (typically message channels). -[NOTE] -==== -The `@EnableBinding` annotation is only required on your `Configuration` classes, you can provide as many binding interfaces as you need, for instance: `@EnableBinding(value={Orders.class, Payment.class}`. -Where both `Order` and `Payment` interfaces would declare `@Input` and `@Output` channels. -==== +NOTE: The `@EnableBinding` annotation is only required on your `Configuration` classes. You can provide as many binding interfaces as you need, as shown in the following example: +`@EnableBinding(value={Orders.class, Payment.class}` +where both `Order` and `Payment` interfaces would declare `@Input` and `@Output` channels. +[[spring-cloud-stream-overview-input-output]] ==== `@Input` and `@Output` -A Spring Cloud Stream application can have an arbitrary number of input and output channels defined in an interface as `@Input` and `@Output` methods: +A Spring Cloud Stream application can have an arbitrary number of input and output channels defined in an interface as `@Input` and `@Output` methods. The following example declares one input and two output channels: [source,java] ---- @@ -251,26 +263,15 @@ public interface Barista { } ---- -Using this interface as a parameter to `@EnableBinding` will trigger the creation of three bound channels named `orders`, `hotDrinks`, and `coldDrinks`, respectively. +Using the interface shown in the preceding example as a parameter to `@EnableBinding` triggers the creation of three bound channels named `orders`, `hotDrinks`, and `coldDrinks`, respectively. -[source,java] ----- -@EnableBinding(Barista.class) -public class CafeConfiguration { - - ... -} ----- -[NOTE] -==== -In Spring Cloud Stream, the bindable `MessageChannel` components are the Spring Messaging `MessageChannel` (for outbound) and its extension `SubscribableChannel` (for inbound). +NOTE: In Spring Cloud Stream, the bindable `MessageChannel` components are the Spring Messaging `MessageChannel` (for outbound) and its extension `SubscribableChannel` (for inbound). Using the same mechanism, other bindable components can be supported. -`KStream` support in Spring Cloud Stream Kafka binder is one such example where KStream is used as inbound/outbound `bindable` components. -Also, as discussed below, a `PollableMessageSource` can be bound to an inbound destination. -In this documentation, we will continue to refer to MessageChannels as the `bindable` components. -==== +`KStream` support in Spring Cloud Stream Kafka binder is one such example, where KStream is used as inbound and outbound `bindable` components. +Also, as discussed later, a `PollableMessageSource` can be bound to an inbound destination. +In this documentation, we continue to refer to MessageChannel components as the `bindable` components. -Starting with _version 2.0_, you can now bind a pollable consumer as follows: +Starting with version 2.0, you can now bind a pollable consumer, as follows: [source,java] ---- @@ -287,11 +288,11 @@ public interface PolledBarista { } ---- -In this case, an implementation of `PollableMessageSource` is bound to the `orders` "channel". +In this case, an implementation of `PollableMessageSource` is bound to the `orders` "`channel`". ===== Customizing Channel Names -Using the `@Input` and `@Output` annotations, you can specify a customized channel name for the channel, as shown in the following example: +By using the `@Input` and `@Output` annotations, you can specify a customized channel name for the channel, as shown in the following example: [source,java] ---- @@ -302,13 +303,13 @@ public interface Barista { } ---- -In this example, the created bound channel will be named `inboundOrders`. +In the preceding example, the created bound channel is named `inboundOrders`. ===== `Source`, `Sink`, and `Processor` -For easy addressing of the most common use cases, which involve either an input channel, an output channel, or both, Spring Cloud Stream provides three predefined interfaces out of the box. +For easy addressing of the most common use cases (which involve either an input channel, an output channel, or both), Spring Cloud Stream provides three pre-defined interfaces out of the box. -`Source` can be used for an application which has a single outbound channel. +`Source` can be used for an application which has a single outbound channel, as shown in the following example: [source,java] ---- @@ -322,7 +323,7 @@ public interface Source { } ---- -`Sink` can be used for an application which has a single inbound channel. +`Sink` can be used for an application which has a single inbound channel, as shown in the following example: [source,java] ---- @@ -336,7 +337,7 @@ public interface Sink { } ---- -`Processor` can be used for an application which has both an inbound channel and an outbound channel. +`Processor` can be used for an application that has both an inbound channel and an outbound channel, as shown in the following example: [source,java] ---- @@ -344,17 +345,23 @@ public interface Processor extends Source, Sink { } ---- -Spring Cloud Stream provides no special handling for any of these interfaces; they are only provided out of the box. +Spring Cloud Stream provides no special handling for any of these interfaces. They are only provided out of the box. +[[spring-cloud-stream-overview-accessing-bound-channels]] ==== Accessing Bound Channels -===== Injecting the Bound Interfaces +Spring Cloud Stream offers two ways to access bound channels: -For each bound interface, Spring Cloud Stream will generate a bean that implements the interface. -Invoking a `@Input`-annotated or `@Output`-annotated method of one of these beans will return the relevant bound channel. +* <> +* <> -The bean in the following example sends a message on the output channel when its `hello` method is invoked. -It invokes `output()` on the injected `Source` bean to retrieve the target channel. +[[spring-cloud-stream-overview-injecting-bound-interfaces]] +===== Injecting Bound Interfaces + +For each bound interface, Spring Cloud Stream generates a bean that implements the interface. +Invoking an `@Input`-annotated or `@Output`-annotated method of one of these beans returns the relevant bound channel. + +The bean in the following example sends a message on the output channel when its `hello` method is invoked and invokes `output()` on the injected `Source` bean to retrieve the target channel: [source,java] ---- @@ -374,9 +381,10 @@ public class SendingBean { } ---- +[[spring-cloud-stream-overview-injecting-channels-directly]] ===== Injecting Channels Directly -Bound channels can be also injected directly: +Bound channels can be also injected directly, as shown in the following example: [source, java] ---- @@ -397,7 +405,7 @@ public class SendingBean { ---- If the name of the channel is customized on the declaring annotation, that name should be used instead of the method name. -Given the following declaration: +Consider the following declaration: [source,java] ---- @@ -408,7 +416,7 @@ public interface CustomSource { } ---- -The channel will be injected as shown in the following example: +Given that declaration, the channel is injected as shown in the following example: [source, java] ---- @@ -428,15 +436,16 @@ public class SendingBean { } ---- +[[spring-cloud-stream-overview-producing-consuming-messages]] ==== Producing and Consuming Messages -You can write a Spring Cloud Stream application using either Spring Integration annotations or Spring Cloud Stream's `@StreamListener` annotation. -The `@StreamListener` annotation is modeled after other Spring Messaging annotations (such as `@MessageMapping`, `@JmsListener`, `@RabbitListener`, etc.) but adds content type management and type coercion features. +You can write a Spring Cloud Stream application by using either Spring Integration annotations or Spring Cloud Stream's `@StreamListener` annotation. +The `@StreamListener` annotation is modeled after other Spring Messaging annotations (such as `@MessageMapping`, `@JmsListener`, `@RabbitListener`, and so on) but adds content type management and type coercion features. ===== Native Spring Integration Support Because Spring Cloud Stream is based on Spring Integration, Stream completely inherits Integration's foundation and infrastructure as well as the component itself. -For example, you can attach the output channel of a `Source` to a `MessageSource`: +For example, you can attach the output channel of a `Source` to a `MessageSource`, as follows: [source, java] ---- @@ -454,7 +463,7 @@ public class TimerSource { } ---- -Or you can use a processor's channels in a transformer: +Similarly, you can use a processor's channels in a transformer, as follows: [source,java] ---- @@ -467,43 +476,39 @@ public class TransformProcessor { } ---- -[NOTE] -==== -It's important to understant that when you consume from the same binding using `@StreamListener` a pubsub model is used, where each method annotated with `@StreamListener` receives it's own copy of the message, each one has its own consumer group. -However, if you share a bindable channel as an input for `@Aggregator`, `@Transformer` or `@ServiceActivator`, those will consume in a competing model, no individual consumer group is created for each subscription. -==== +NOTE: It is important to understand that, when you consume from the same binding by using `@StreamListener`, a pub-sub model is used. Each method annotated with `@StreamListener` receives its own copy of the message and each one has its own consumer group. +However, if you share a bindable channel as an input for `@Aggregator`, `@Transformer`, or `@ServiceActivator`, those consume in a competing model. No individual consumer group is created for each subscription. +[[spring-cloud-stream-overview-error-channel-support]] ===== Spring Integration Error Channel Support -Spring Cloud Stream supports publishing error messages received by the Spring Integration global -error channel. Error messages sent to the `errorChannel` can be published to a specific destination -at the broker by configuring a binding for the outbound target named `error`. For example, to -publish error messages to a broker destination named "myErrors", provide the following property: -`spring.cloud.stream.bindings.error.destination=myErrors`. +Spring Cloud Stream supports publishing error messages received by the Spring Integration global error channel. +Error messages sent to the `errorChannel` can be published to a specific destination at the broker by configuring a binding named `error` for the outbound target. +For example, to publish error messages to a broker destination named `myErrors`, set the following property: `spring.cloud.stream.bindings.error.destination=myErrors`. [[binder-error-channels]] ===== Message Channel Binders and Error Channels -Starting with _version 1.3_, some `MessageChannel` - based binders publish errors to a discrete error channel for each destination. -In addition, these error channels are bridged to the global Spring Integration `errorChannel` mentioned above. -You can therefore consume errors for specific destinations and/or for all destinations, using a standard Spring Integration flow (`IntegrationFlow`, `@ServiceActivator`, etc.). +Starting with version 1.3, some `MessageChannel`-based binders publish errors to a discrete error channel for each destination. +In addition, these error channels are bridged to the global Spring Integration `errorChannel` mentioned spring-cloud-stream-overview-error-channel-support[earlier]. +You can, therefore, consume errors for specific destinations or for all destinations by using a standard Spring Integration flow (`IntegrationFlow`, `@ServiceActivator`, and others). On the consumer side, the listener thread catches any exceptions and forwards an `ErrorMessage` to the destination's error channel. The payload of the message is a `MessagingException` with the normal `failedMessage` and `cause` properties. Usually, the raw data received from the broker is included in a header. -For binders that support (and are configured with) a dead letter destination; a `MessagePublishingErrorHandler` is subscribed to the channel, and the raw data is forwarded to the dead letter destination. +For binders that support (and are configured with) a dead letter destination, a `MessagePublishingErrorHandler` is subscribed to the channel and the raw data is forwarded to the dead letter destination. -On the producer side; for binders that support some kind of async result after publishing messages (e.g. RabbitMQ, Kafka), you can enable an error channel by setting the `...producer.errorChannelEnabled` to `true`. -The payload of the `ErrorMessage` depends on the binder implementation but will be a `MessagingException` with the normal `failedMessage` property, as well as additional properties about the failure. -Refer to the binder documentation for complete details. +On the producer side, for binders that support some kind of asynchronous result after publishing messages (such as RabbitMQ and Kafka), you can enable an error channel by setting the `...producer.errorChannelEnabled` to `true`. +The payload of the `ErrorMessage` depends on the binder implementation but is a `MessagingException` with the normal `failedMessage` property as well as additional properties about the failure. +See the spring-cloud-stream-overview-binders[binder documentation] for complete details. ===== Using @StreamListener for Automatic Content Type Handling -Complementary to its Spring Integration support, Spring Cloud Stream provides its own `@StreamListener` annotation, modeled after other Spring Messaging annotations (e.g. `@MessageMapping`, `@JmsListener`, `@RabbitListener`, etc.). +Complementary to its Spring Integration support, Spring Cloud Stream provides its own `@StreamListener` annotation, modeled after other Spring Messaging annotations (`@MessageMapping`, `@JmsListener`, `@RabbitListener`, and others). The `@StreamListener` annotation provides a simpler model for handling inbound messages, especially when dealing with use cases that involve content type management and type coercion. Spring Cloud Stream provides an extensible `MessageConverter` mechanism for handling data conversion by bound channels and for, in this case, dispatching to methods annotated with `@StreamListener`. -The following is an example of an application which processes external `Vote` events: +The following example shows an application that processes external `Vote` events: [source,java] ---- @@ -521,13 +526,13 @@ public class VoteHandler { ---- The distinction between `@StreamListener` and a Spring Integration `@ServiceActivator` is seen when considering an inbound `Message` that has a `String` payload and a `contentType` header of `application/json`. -In the case of `@StreamListener`, the `MessageConverter` mechanism will use the `contentType` header to parse the `String` payload into a `Vote` object. +In the case of `@StreamListener`, the `MessageConverter` mechanism uses the `contentType` header to parse the `String` payload into a `Vote` object. -As with other Spring Messaging methods, method arguments can be annotated with `@Payload`, `@Headers` and `@Header`. +As with other Spring Messaging methods, method arguments can be annotated with `@Payload`, `@Headers`, and `@Header`. [NOTE] ==== -For methods which return data, you must use the `@SendTo` annotation to specify the output binding destination for data returned by the method: +For methods that return data, you must use the `@SendTo` annotation to specify the output binding destination for data returned by the method, as shown in the following example: [source,java] ---- @@ -546,20 +551,19 @@ public class TransformProcessor { ---- ==== -===== Using @StreamListener for dispatching messages to multiple methods +===== Using @StreamListener for Dispatching Messages to Multiple Methods Since version 1.2, Spring Cloud Stream supports dispatching messages to multiple `@StreamListener` methods registered on an input channel, based on a condition. In order to be eligible to support conditional dispatching, a method must satisfy the follow conditions: -* it must not return a value -* it must be an individual message handling method (reactive API methods are not supported) +* It must not return a value. +* It must be an individual message handling method (reactive API methods are not supported). -The condition is specified via a SpEL expression in the `condition` attribute of the annotation and is evaluated for each message. -All the handlers that match the condition will be invoked in the same thread and no assumption must be made about the order in which the invocations take place. +The condition is specified by a SpEL expression in the `condition` attribute of the annotation and is evaluated for each message. +All the handlers that match the condition are invoked in the same thread, and no assumption must be made about the order in which the invocations take place. -An example of using `@StreamListener` with dispatching conditions can be seen below. -In this example, all the messages bearing a header `type` with the value `foo` will be dispatched to the `receiveFoo` method, and all the messages bearing a header `type` with the value `bar` will be dispatched to the `receiveBar` method. +In the following example of a `@StreamListener` with dispatching conditions, all the messages bearing a header `type` with the value `bogey` are dispatched to the `receiveBogey` method, and all the messages bearing a header `type` with the value `bacall` are dispatched to the `receiveBacall` method. [source,java] ---- @@ -567,27 +571,25 @@ In this example, all the messages bearing a header `type` with the value `foo` w @EnableAutoConfiguration public static class TestPojoWithAnnotatedArguments { - @StreamListener(target = Sink.INPUT, condition = "headers['type']=='foo'") - public void receiveFoo(@Payload FooPojo fooPojo) { + @StreamListener(target = Sink.INPUT, condition = "headers['type']=='bogey'") + public void receiveBogey(@Payload BogeyPojo bogeyPojo) { // handle the message } - @StreamListener(target = Sink.INPUT, condition = "headers['type']=='bar'") - public void receiveBar(@Payload BarPojo barPojo) { + @StreamListener(target = Sink.INPUT, condition = "headers['type']=='bacall'") + public void receiveBacall(@Payload BacallPojo bacallPojo) { // handle the message } } ---- -[NOTE] -==== -Dispatching via `@StreamListener` conditions is only supported for handlers of individual messages, and not for reactive programming support (described below). -==== +NOTE: Dispatching through `@StreamListener` conditions is only supported for handlers of individual messages, not for reactive programming support (described spring-cloud-stream-overview-reactive-programming-support[later]). +[[spring-cloud-streams-overview-using-polled-consumers]] ===== Using Polled Consumers When using polled consumers, you poll the `PollableMessageSource` on demand. -For example, given... +Consider the following example of a polled consumer: [source,java] ---- @@ -602,7 +604,7 @@ public interface PolledConsumer { } ---- -...you might use that consumer as follows: +Given the polled consumer in the preceding example, you might use it as follows: [source,java] ---- @@ -626,14 +628,14 @@ public ApplicationRunner poller(PollableMessageSource destIn, MessageChannel des } ---- -The `PollableMessageSource.poll()` method takes a `MessageHandler` argument (often a lambda expression as shown here). +The `PollableMessageSource.poll()` method takes a `MessageHandler` argument (often a lambda expression, as shown here). It returns `true` if the message was received and successfully processed. -As with message-driven consumers, if the `MessageHandler` throws an exception, messages are published to error channels as discussed in <>. +As with message-driven consumers, if the `MessageHandler` throws an exception, messages are published to error channels, as discussed in "`<>`". -Normally, the `poll()` method will acknowledge the message when the `MessageHandler` exits. -If the method exits abnormally, the message is rejected (not requeued). -You can override that behavior, by taking responsibility for the acknowledgment, as follows: +Normally, the `poll()` method acknowledges the message when the `MessageHandler` exits. +If the method exits abnormally, the message is rejected (not re-queued). +You can override that behavior by taking responsibility for the acknowledgment, as shown in the following example: [source,java] ---- @@ -654,18 +656,18 @@ public ApplicationRunner poller(PollableMessageSource dest1In, MessageChannel de } ---- -IMPORTANT: You must ack (or nack) the message at some point, to avoid resource leaks. +IMPORTANT: You must `ack` (or `nack`) the message at some point, to avoid resource leaks. -IMPORTANT: Some messaging systems (such as Apache Kafka) maintain a simple offset in a log, if a delivery fails and is requeued with `StaticMessageHeaderAccessor.getAcknowledgmentCallback(m).acknowledge(Status.REQUEUE);`, any later successfully ack'd messages will be redelivered. +IMPORTANT: Some messaging systems (such as Apache Kafka) maintain a simple offset in a log. If a delivery fails and is re-queued with `StaticMessageHeaderAccessor.getAcknowledgmentCallback(m).acknowledge(Status.REQUEUE);`, any later successfully ack'd messages are redelivered. -There is also an overloaded `poll` method: +There is also an overloaded `poll` method, for which the definition is as follows: [source,java] ---- poll(MessageHandler handler, ParameterizedTypeReference type) ---- -The `type` is a conversion hint allowing the incoming message payload to be converted: +The `type` is a conversion hint that allows the incoming message payload to be converted, as shown in the following example: [source,java] ---- @@ -676,57 +678,46 @@ boolean result = pollableSource.poll(received -> { }, new ParameterizedTypeReference>() {}); ---- +[[spring-cloud-stream-overview-reactive-programming-support]] ==== Reactive Programming Support Spring Cloud Stream also supports the use of reactive APIs where incoming and outgoing data is handled as continuous data flows. -Support for reactive APIs is available via the `spring-cloud-stream-reactive`, which needs to be added explicitly to your project. +Support for reactive APIs is available through `spring-cloud-stream-reactive`, which needs to be added explicitly to your project. +The programming model with reactive APIs is declarative. Instead of specifying how each individual message should be handled, you can use operators that describe functional transformations from inbound to outbound data flows. -The programming model with reactive APIs is declarative, where instead of specifying how each individual message should be handled, you can use operators that describe functional transformations from inbound to outbound data flows. +At present Spring Cloud Stream supports the only the https://projectreactor.io/[Reactor API]. +In the future, we intend to support a more generic model based on Reactive Streams. -Spring Cloud Stream supports the following reactive APIs: +The reactive programming model also uses the `@StreamListener` annotation for setting up reactive handlers. +The differences are that: -* Reactor +* The `@StreamListener` annotation must not specify an input or output, as they are provided as arguments and return values from the method. +* The arguments of the method must be annotated with `@Input` and `@Output`, indicating which input or output the incoming and outgoing data flows connect to, respectively. +* The return value of the method, if any, is annotated with `@Output`, indicating the input where data should be sent. -In the future, it is intended to support a more generic model based on Reactive Streams. +NOTE: Reactive programming support requires Java 1.8. -The reactive programming model is also using the `@StreamListener` annotation for setting up reactive handlers. The differences are that: - -* the `@StreamListener` annotation must not specify an input or output, as they are provided as arguments and return values from the method; -* the arguments of the method must be annotated with `@Input` and `@Output` indicating which input or output will the incoming and respectively outgoing data flows connect to; -* the return value of the method, if any, will be annotated with `@Output`, indicating the input where data shall be sent. - -[NOTE] -==== -Reactive programming support requires Java 1.8. -==== - -[NOTE] -==== -As of Spring Cloud Stream 1.1.1 and later (starting with release train Brooklyn.SR2), reactive programming support requires the use of Reactor 3.0.4.RELEASE and higher. +NOTE: As of Spring Cloud Stream 1.1.1 and later (starting with release train Brooklyn.SR2), reactive programming support requires the use of Reactor 3.0.4.RELEASE and higher. Earlier Reactor versions (including 3.0.1.RELEASE, 3.0.2.RELEASE and 3.0.3.RELEASE) are not supported. -`spring-cloud-stream-reactive` will transitively retrieve the proper version, but it is possible for the project structure to manage the version of the `io.projectreactor:reactor-core` to an earlier release, especially when using Maven. -This is the case for projects generated via Spring Initializr with Spring Boot 1.x, which will override the Reactor version to `2.0.8.RELEASE`. -In such cases you must ensure that the proper version of the artifact is released. -This can be simply achieved by adding a direct dependency on `io.projectreactor:reactor-core` with a version of `3.0.4.RELEASE` or later to your project. -==== +`spring-cloud-stream-reactive` transitively retrieves the proper version, but it is possible for the project structure to manage the version of the `io.projectreactor:reactor-core` to an earlier release, especially when using Maven. +This is the case for projects generated by using Spring Initializr with Spring Boot 1.x, which overrides the Reactor version to `2.0.8.RELEASE`. +In such cases, you must ensure that the proper version of the artifact is released. +You can do so by adding a direct dependency on `io.projectreactor:reactor-core` with a version of `3.0.4.RELEASE` or later to your project. -[NOTE] -==== -The use of term `reactive` is currently referring to the reactive APIs being used and not to the execution model being reactive (i.e. the bound endpoints are still using a 'push' rather than 'pull' model). While some backpressure support is provided by the use of Reactor, we do intend on the long run to support entirely reactive pipelines by the use of native reactive clients for the connected middleware. -==== +NOTE: The use of term, "`reactive`", currently refers to the reactive APIs being used and not to the execution model being reactive (that is, the bound endpoints still use a 'push' rather than a 'pull' model). While some backpressure support is provided by the use of Reactor, we do intend, in a future release, to support entirely reactive pipelines by the use of native reactive clients for the connected middleware. -===== Reactor-based handlers +===== Reactor-based Handlers -A Reactor based handler can have the following argument types: +A Reactor-based handler can have the following argument types: -* For arguments annotated with `@Input`, it supports the Reactor type `Flux`. - The parameterization of the inbound Flux follows the same rules as in the case of individual message handling: it can be the entire `Message`, a POJO which can be the `Message` payload, or a POJO which is the result of a transformation based on the `Message` content-type header. Multiple inputs are provided; -* For arguments annotated with `Output`, it supports the type `FluxSender` which connects a `Flux` produced by the method with an output. Generally speaking, specifying outputs as arguments is only recommended when the method can have multiple outputs; +* For arguments annotated with `@Input`, it supports the Reactor `Flux` type. +The parameterization of the inbound Flux follows the same rules as in the case of individual message handling: It can be the entire `Message`, a POJO that can be the `Message` payload, or a POJO that is the result of a transformation based on the `Message` content-type header. Multiple inputs are provided. +* For arguments annotated with `Output`, it supports the `FluxSender` type, which connects a `Flux` produced by the method with an output. Generally speaking, specifying outputs as arguments is only recommended when the method can have multiple outputs. -A Reactor based handler supports a return type of `Flux`, case in which it must be annotated with `@Output`. We recommend using the return value of the method when a single output flux is available. +A Reactor-based handler supports a return type of `Flux`. In that case, it must be annotated with `@Output`. We recommend using the return value of the method when a single output `Flux` is available. -Here is an example of a simple Reactor-based Processor. +The following example shows a Reactor-based `Processor`: [source, java] ---- @@ -742,7 +733,7 @@ public static class UppercaseTransformer { } ---- -The same processor using output arguments looks like this: +The same processor using output arguments looks like the following example: [source, java] ---- @@ -760,16 +751,15 @@ public static class UppercaseTransformer { ===== Reactive Sources -Spring Cloud Stream reactive support also provides the ability for creating reactive sources through the StreamEmitter annotation. -Using StreamEmitter annotation, a regular source may be converted to a reactive one. -StreamEmitter is a method level annotation that marks a method to be an emitter to outputs declared via EnableBinding. -It is not allowed to use the Input annotation along with StreamEmitter, as the methods marked with this annotation are not listening from any input, rather generating to an output. -Following the same programming model used in StreamListener, StreamEmitter also allows flexible ways of using the Output annotation depending on whether the method has any arguments, return type etc. +Spring Cloud Stream reactive support also provides the ability for creating reactive sources through the `@StreamEmitter` annotation. +By using the `@StreamEmitter` annotation, a regular source may be converted to a reactive one. +`@StreamEmitter` is a method level annotation that marks a method to be an emitter to outputs declared with `@EnableBinding`. +You cannot use the `@Input` annotation along with `@StreamEmitter`, as the methods marked with this annotation are not listening for any input. Rather, methods marked with `@StreamEmitter` generate output. +Following the same programming model used in `@StreamListener`, `@StreamEmitter` also allows flexible ways of using the `@Output` annotation, depending on whether the method has any arguments, a return type, and other considerations. -Here are some examples of using StreamEmitter in various styles. +The remainder of this section contains examples of using the `@StreamEmitter` annotation in various styles. -The following example will emit the "Hello World" message every millisecond and publish to a Flux. -In this case, the resulting messages in Flux will be sent to the output channel of the Source. +The following example emits the `Hello, World` message every millisecond and publishes to a Reactor `Flux`: [source, java] ---- @@ -786,8 +776,10 @@ public static class HelloWorldEmitter { } ---- -Following is another flavor of the same sample as above. -Instead of returning a Flux, this method uses a FluxSender to programmatically send Flux from a source. +In the preceding example, the resulting messages in the `Flux` are sent to the output channel of the `Source`. + +The next example is another flavor of an `@StreamEmmitter` that sends a Reactor `Flux`. +Instead of returning a `Flux`, the following method uses a `FluxSender` to programmatically send a `Flux` from a source: [source, java] ---- @@ -804,8 +796,8 @@ public static class HelloWorldEmitter { } ---- -Following is exactly same as the above snippet in functionality and style. -However, instead of using an explicit Output annotation at the method level, it is used as the method parameter level. +The next example is exactly same as the above snippet in functionality and style. +However, instead of using an explicit `@Output` annotation on the method, it uses the annotation on the method parameter. [source, java] ---- @@ -821,8 +813,8 @@ public static class HelloWorldEmitter { } ---- -Here is yet another flavor of writing reacting sources using the Reactive Streams Publisher API and the support for it in the https://github.com/spring-projects/spring-integration-java-dsl/wiki/Spring-Integration-Java-DSL-Reference[Spring Integration Java DSL]. -The Publisher is still using Reactor Flux under the hood, but from an application perspective, that is transparent to the user and only needs Reactive Streams and Java DSL for Spring Integration. +The last example in this section is yet another flavor of writing reacting sources by using the Reactive Streams Publisher API and taking advantage of the support for it in https://github.com/spring-projects/spring-integration-java-dsl/wiki/Spring-Integration-Java-DSL-Reference[Spring Integration Java DSL]. +The `Publisher` in the following example still uses Reactor `Flux` under the hood, but, from an application perspective, that is transparent to the user and only needs Reactive Streams and Java DSL for Spring Integration: [source, java] ---- @@ -842,35 +834,34 @@ public static class HelloWorldEmitter { } ---- +[[spring-cloud-stream-overview-aggregation]] ==== Aggregation -Spring Cloud Stream provides support for aggregating multiple applications together, connecting their input and output channels directly and avoiding the additional cost of exchanging messages via a broker. +Spring Cloud Stream provides support for aggregating multiple applications together, connecting their input and output channels directly, and avoiding the additional cost of exchanging messages through a broker. As of version 1.0 of Spring Cloud Stream, aggregation is supported only for the following types of applications: -* _sources_ - applications with a single output channel named `output`, typically having a single binding of the type `org.springframework.cloud.stream.messaging.Source` -* _sinks_ - applications with a single input channel named `input`, typically having a single binding of the type `org.springframework.cloud.stream.messaging.Sink` -* _processors_ - applications with a single input channel named `input` and a single output channel named `output`, typically having a single binding of the type `org.springframework.cloud.stream.messaging.Processor`. +* Sources: Applications with a single output channel named `output`, typically having a single binding of the type `org.springframework.cloud.stream.messaging.Source`. +* Sinks: Applications with a single input channel named `input`, typically having a single binding of the type `org.springframework.cloud.stream.messaging.Sink`. +* Processors: Applications with a single input channel named `input` and a single output channel named `output`, typically having a single binding of the type `org.springframework.cloud.stream.messaging.Processor`. -They can be aggregated together by creating a sequence of interconnected applications, in which the output channel of an element in the sequence is connected to the input channel of the next element, if it exists. -A sequence can start with either a _source_ or a _processor_, it can contain an arbitrary number of _processors_ and must end with either a _processor_ or a _sink_. +They can be aggregated together by creating a sequence of interconnected applications in which the output channel of an element in the sequence is connected to the input channel of the next element, if it exists. +A sequence can start with either a source or a processor, can contain an arbitrary number of processors, and must end with either a processor or a sink. -Depending on the nature of the starting and ending element, the sequence may have one or more bindable channels, as follows: +Depending on the nature of the starting and ending elements, the sequence may have one or more bindable channels, as follows: -* if the sequence starts with a source and ends with a sink, all communication between the applications is direct and no channels will be bound -* if the sequence starts with a processor, then its input channel will become the `input` channel of the aggregate and will be bound accordingly -* if the sequence ends with a processor, then its output channel will become the `output` channel of the aggregate and will be bound accordingly +* If the sequence starts with a source and ends with a sink, all communication between the applications is direct and no channels are bound. +* If the sequence starts with a processor, its input channel becomes the `input` channel of the aggregate and is bound accordingly. +* If the sequence ends with a processor, its output channel becomes the `output` channel of the aggregate and is bound accordingly. -Aggregation is performed using the `AggregateApplicationBuilder` utility class, as in the following example. -Let's consider a project in which we have source, processor and a sink, which may be defined in the project, or may be contained in one of the project's dependencies. +Aggregation is performed using the `AggregateApplicationBuilder` utility class, as shown in the next example. +Consider a project in which we have a source, a processor and a sink, all of which may be defined in the project or may be contained in one of the project's dependencies. -[NOTE] -==== -Each component (source, sink or processor) in an aggregate application must be provided in a separate package if the configuration classes use `@SpringBootApplication`. -This is required to avoid cross-talk between applications, due to the classpath scanning performed by `@SpringBootApplication` on the configuration classes inside the same package. -In the example below, it can be seen that the Source, Processor and Sink application classes are grouped in separate packages. -A possible alternative is to provide the source, sink or processor configuration in a separate `@Configuration` class, avoid the use of `@SpringBootApplication`/`@ComponentScan` and use those for aggregation. -==== +NOTE: Each component (source, sink, or processor) in an aggregate application must be provided in a separate package if the configuration classes use `@SpringBootApplication`. +This requirement avoids cross-talk between applications, due to the classpath scanning performed by `@SpringBootApplication` on the configuration classes inside the same package. +In the next example, you can see that the `Source`, `Processor`, and `Sink` application classes are grouped in separate packages. +A possible alternative is to provide the source, sink, or processor configuration in a separate `@Configuration` class, avoid the use of `@SpringBootApplication`/`@ComponentScan`, and use those annotations for aggregation. +The example consists of three classes in three packages, as follows: [source,java] ---- @@ -925,7 +916,9 @@ public class SourceApplication { } ---- -Each configuration can be used for running a separate component, but in this case they can be aggregated together as follows: + +Each configuration can be used to run a separate component. +However, in this case, they can be aggregated together, as follows: [source,java] ---- @@ -945,17 +938,17 @@ public class SampleAggregateApplication { } ---- -The starting component of the sequence is provided as argument to the `from()` method. -The ending component of the sequence is provided as argument to the `to()` method. -Intermediate processors are provided as argument to the `via()` method. -Multiple processors of the same type can be chained together (e.g. for pipelining transformations with different configurations). +The starting component of the sequence is provided as an argument to the `from()` method. +The ending component of the sequence is provided as an argument to the `to()` method. +Intermediate processors are provided as an argument to the `via()` method. +Multiple processors of the same type can be chained together (for example, for pipelining transformations with different configurations). For each component, the builder can provide runtime arguments for Spring Boot configuration. -===== Configuring aggregate application +===== Configuring an Aggregate Application -Spring Cloud Stream supports passing properties for the individual applications inside the aggregate application using 'namespace' as prefix. +Spring Cloud Stream supports passing properties for the individual applications inside the aggregate application by using 'namespace' as a prefix. -The namespace can be set for applications as follows: +The `namespace` can be set for applications, as shown in the following example: [source,java] ---- @@ -971,21 +964,18 @@ public class SampleAggregateApplication { } ---- -Ones the 'namespace' is set for the individual applications, the application properties with the `namespace` as prefix can be passed to the aggregate application using any supported property source (commandline, environment properties etc.). +Once the 'namespace' is set for the individual applications, the application properties with the `namespace` as prefix can be passed to the aggregate application by using any supported property source (command line, environment properties, and others). -For instance, to override the default `fixedDelay` and `debug` properties of 'source' and 'sink' applications: +For instance, to override the default `fixedDelay` and `debug` properties of 'source' and 'sink' applications, you could use the following `java` command: [source] ----- java -jar target/MyAggregateApplication-0.0.1-SNAPSHOT.jar --source.fixedDelay=10000 --sink.debug=false ----- -===== Configuring binding service properties for non self contained aggregate application +===== Configuring Binding Service Properties for a Non-self-contained Aggregate Application -The non self-contained aggregate application is bound to external broker via either or both the inbound/outbound components (typically, message channels) of the aggregate application while the applications inside the aggregate application are directly bound. -For example: a source application's output and a processor application's input are directly bound while the processor's output channel is bound to an external destination at the broker. -When passing the binding service properties for non-self contained aggregate application, it is required to pass the binding service properties to the aggregate application instead of setting them as 'args' to individual child application. -For instance, +The non-self-contained aggregate application is bound to an external broker with either or both of the inbound and outbound components (typically, message channels) of the aggregate application, while the applications inside the aggregate application are directly bound. +For example, a source application's output and a processor application's input are directly bound, while the processor's output channel is bound to an external destination at the broker. +When passing the binding service properties for non-self-contained aggregate application, it is required to pass the binding service properties to the aggregate application instead of setting them as 'args' to individual child application, as shown in the following example: [source,java] ---- @@ -1000,10 +990,9 @@ public class SampleAggregateApplication { } ---- +Binding properties, such as `--spring.cloud.stream.bindings.output.destination=processor-output`, need to be specified as one of the external configuration properties (command line argument and so on). -The binding properties like `--spring.cloud.stream.bindings.output.destination=processor-output` need to be specified as one of the external configuration properties (cmdline arg etc.). - - +[[spring-cloud-stream-overview-binders]] == Binders Spring Cloud Stream provides a Binder abstraction for use in connecting to physical destinations at the external middleware. @@ -1011,24 +1000,27 @@ This section provides information about the main concepts behind the Binder SPI, === Producers and Consumers +The following image shows the general relationship of producers and consumers: + .Producers and Consumers image::producers-consumers.png[width=300,scaledwidth="75%"] -A _producer_ is any component that sends messages to a channel. -The channel can be bound to an external message broker via a Binder implementation for that broker. -When invoking the `bindProducer()` method, the first parameter is the name of the destination within the broker, the second parameter is the local channel instance to which the producer will send messages, and the third parameter contains properties (such as a partition key expression) to be used within the adapter that is created for that channel. +A producer is any component that sends messages to a channel. +The channel can be bound to an external message broker with a `Binder` implementation for that broker. +When invoking the `bindProducer()` method, the first parameter is the name of the destination within the broker, the second parameter is the local channel instance to which the producer sends messages, and the third parameter contains properties (such as a partition key expression) to be used within the adapter that is created for that channel. -A _consumer_ is any component that receives messages from a channel. +A consumer is any component that receives messages from a channel. As with a producer, the consumer's channel can be bound to an external message broker. When invoking the `bindConsumer()` method, the first parameter is the destination name, and a second parameter provides the name of a logical group of consumers. -Each group that is represented by consumer bindings for a given destination receives a copy of each message that a producer sends to that destination (i.e., publish-subscribe semantics). -If there are multiple consumer instances bound using the same group name, then messages will be load-balanced across those consumer instances so that each message sent by a producer is consumed by only a single consumer instance within each group (i.e., queueing semantics). +Each group that is represented by consumer bindings for a given destination receives a copy of each message that a producer sends to that destination (that is, it follows normal publish-subscribe semantics). +If there are multiple consumer instances bound with the same group name, then messages are load-balanced across those consumer instances so that each message sent by a producer is consumed by only a single consumer instance within each group (that is, it follows normal queueing semantics). +[[spring-cloud-stream-overview-binder-api]] === Binder SPI -The Binder SPI consists of a number of interfaces, out-of-the box utility classes and discovery strategies that provide a pluggable mechanism for connecting to external middleware. +The Binder SPI consists of a number of interfaces, out-of-the box utility classes, and discovery strategies that provide a pluggable mechanism for connecting to external middleware. -The key point of the SPI is the `Binder` interface which is a strategy for connecting inputs and outputs to external middleware. +The key point of the SPI is the `Binder` interface, which is a strategy for connecting inputs and outputs to external middleware. The following listing shows the definnition of the `Binder` interface: [source,java] ---- @@ -1041,19 +1033,20 @@ public interface Binder ---- -For the specific maven coordinates of other binder dependencies, please refer to the documentation of that binder implementation. +For the specific Maven coordinates of other binder dependencies, see the documentation of that binder implementation. [[multiple-binders]] === Multiple Binders on the Classpath When multiple binders are present on the classpath, the application must indicate which binder is to be used for each channel binding. -Each binder configuration contains a `META-INF/spring.binders`, which is a simple properties file: +Each binder configuration contains a `META-INF/spring.binders` file, which is a simple properties file, as shown in the following example: [source] ---- @@ -1088,12 +1081,13 @@ rabbit:\ org.springframework.cloud.stream.binder.rabbit.config.RabbitServiceAutoConfiguration ---- -Similar files exist for the other provided binder implementations (e.g., Kafka), and custom binder implementations are expected to provide them, as well. +Similar files exist for the other provided binder implementations (such as Kafka), and custom binder implementations are expected to provide them as well. The key represents an identifying name for the binder implementation, whereas the value is a comma-separated list of configuration classes that each contain one and only one bean definition of type `org.springframework.cloud.stream.binder.Binder`. -Binder selection can either be performed globally, using the `spring.cloud.stream.defaultBinder` property (e.g., `spring.cloud.stream.defaultBinder=rabbit`) or individually, by configuring the binder on each channel binding. -For instance, a processor application (that has channels with the names `input` and `output` for read/write respectively) which reads from Kafka and writes to RabbitMQ can specify the following configuration: +Binder selection can either be performed globally, using the `spring.cloud.stream.defaultBinder` property (for example, `spring.cloud.stream.defaultBinder=rabbit`) or individually, by configuring the binder on each channel binding. +For instance, a processor application (that has channels named `input` and `output` for read and write respectively) that reads from Kafka and writes to RabbitMQ can specify the following configuration: +[source] ---- spring.cloud.stream.bindings.input.binder=kafka spring.cloud.stream.bindings.output.binder=rabbit @@ -1102,19 +1096,16 @@ spring.cloud.stream.bindings.output.binder=rabbit [[multiple-systems]] === Connecting to Multiple Systems -By default, binders share the application's Spring Boot auto-configuration, so that one instance of each binder found on the classpath will be created. +By default, binders share the application's Spring Boot auto-configuration, so that one instance of each binder found on the classpath is created. If your application should connect to more than one broker of the same type, you can specify multiple binder configurations, each with different environment settings. -[NOTE] -==== -Turning on explicit binder configuration will disable the default binder configuration process altogether. -If you do this, all binders in use must be included in the configuration. -Frameworks that intend to use Spring Cloud Stream transparently may create binder configurations that can be referenced by name, but will not affect the default binder configuration. -In order to do so, a binder configuration may have its `defaultCandidate` flag set to false, e.g. `spring.cloud.stream.binders..defaultCandidate=false`. -This denotes a configuration that will exist independently of the default binder configuration process. -==== +NOTE: Turning on explicit binder configuration disables the default binder configuration process altogether. +If you do so, all binders in use must be included in the configuration. +Frameworks that intend to use Spring Cloud Stream transparently may create binder configurations that can be referenced by name, but they do not affect the default binder configuration. +In order to do so, a binder configuration may have its `defaultCandidate` flag set to false (for example, `spring.cloud.stream.binders..defaultCandidate=false`). +This denotes a configuration that exists independently of the default binder configuration process. -For example, this is the typical configuration for a processor application which connects to two RabbitMQ broker instances: +The following example shows a typical configuration for a processor application that connects to two RabbitMQ broker instances: [source,yml] ---- @@ -1123,10 +1114,10 @@ spring: stream: bindings: input: - destination: foo + destination: thing1 binder: rabbit1 output: - destination: bar + destination: thing2 binder: rabbit2 binders: rabbit1: @@ -1144,11 +1135,10 @@ spring: ---- === Binding visualization and control -Since version 2.0 Spring Cloud Stream supports visualization and control of the Bindings via Actuator endpoints. +Since version 2.0, Spring Cloud Stream supports visualization and control of the Bindings through Actuator endpoints. -[NOTE] -==== -Given that starting with version 2.0 _actuator_ and _web_ are optional, one must first add one of the web dependencies as well as the actuator dependency manually. +Starting with version 2.0 actuator and web are optional, you must first add one of the web dependencies as well as add the actuator dependency manually. +The following example shows how to add the dependency for the Web framework: [source,xml] ---- @@ -1157,7 +1147,9 @@ Given that starting with version 2.0 _actuator_ and _web_ are optional, one must spring-boot-starter-web ---- -or + +The following example shows how to add the dependency for the WebFlux framework: + [source,xml] ---- @@ -1166,7 +1158,7 @@ or ---- -Actuator dependency can be added as follows: +You can add the Actuator dependency as follows: [source,xml] ---- @@ -1175,279 +1167,265 @@ Actuator dependency can be added as follows: ---- -You must also enable `bindings` actuator endpoints with the following property `--management.endpoints.web.exposure.include=bindings`. -==== -Once the above prerequisites are satisfied you should see the following in the logs when application is started: +You must also enable the `bindings` actuator endpoints by setting the following property: `--management.endpoints.web.exposure.include=bindings`. + +Once those prerequisites are satisfied. you should see the following in the logs when application start: : Mapped "{[/actuator/bindings/{name}],methods=[POST]. . . : Mapped "{[/actuator/bindings],methods=[GET]. . . : Mapped "{[/actuator/bindings/{name}],methods=[GET]. . . -To visualize current bindings simply access the following URL: ----- -http://:/actuator/bindings ----- +To visualize the current bindings, access the following URL: +`http://:/actuator/bindings` -or +Alternative, to see a single binding, access one of the URLs similar to the following: +`http://:/actuator/bindings/myBindingName` ----- -http://:/actuator/bindings/myBindingName ----- +You can also stop, start, pause, and resume individual bindings by posting to the same URL while providing a `state` argument as JSON, as shown in the following examples: -...if you want to visualize a single binding named 'myBindingName' - -You can also _stop, start, pause_ and _resume_ individual binding by posting to the same URL while providing `state` argument as JSON. - -For example, ----- curl -d '{"state":"STOPPED"}' -H "Content-Type: application/json" -X POST http://:/actuator/bindings/myBindingName curl -d '{"state":"STARTED"}' -H "Content-Type: application/json" -X POST http://:/actuator/bindings/myBindingName curl -d '{"state":"PAUSED"}' -H "Content-Type: application/json" -X POST http://:/actuator/bindings/myBindingName curl -d '{"state":"RESUMED"}' -H "Content-Type: application/json" -X POST http://:/actuator/bindings/myBindingName ----- -[NOTE] -==== -_PAUSED_ and _RESUMED_ are only effective if corresponding binder and its underlyig technology supports it, otherwise you'll see the warning message in the logs. -Currently only Kafka binder supports _PAUSED_ and _RESUMED_ state. -==== +NOTE: `PAUSED` and `RESUMED` work only when the corresponding binder and its underlying technology supports it. Otherwise, you see the warning message in the logs. +Currently, only Kafka binder supports the `PAUSED` and `RESUMED` states. -=== Binder configuration properties +=== Binder Configuration Properties The following properties are available when creating custom binder configurations. They must be prefixed with `spring.cloud.stream.binders.`. type:: - The binder type. -It typically references one of the binders found on the classpath, in particular a key in a `META-INF/spring.binders` file. +The binder type. +It typically references one of the binders found on the classpath -- in particular, a key in a `META-INF/spring.binders` file. + By default, it has the same value as the configuration name. inheritEnvironment:: - Whether the configuration will inherit the environment of the application itself. +Whether the configuration inherits the environment of the application itself. + -Default `true`. +Default: `true`. environment:: - Root for a set of properties that can be used to customize the environment of the binder. -When this is configured, the context in which the binder is being created is not a child of the application context. -This allows for complete separation between the binder components and the application components. +Root for a set of properties that can be used to customize the environment of the binder. +When this property is set, the context in which the binder is being created is not a child of the application context. +This setting allows for complete separation between the binder components and the application components. + -Default `empty`. +Default: `empty`. defaultCandidate:: - Whether the binder configuration is a candidate for being considered a default binder, or can be used only when explicitly referenced. -This allows adding binder configurations without interfering with the default processing. +Whether the binder configuration is a candidate for being considered a default binder or can be used only when explicitly referenced. +This setting allows adding binder configurations without interfering with the default processing. + -Default `true`. +Default: `true`. == Configuration Options Spring Cloud Stream supports general configuration options as well as configuration for bindings and binders. -Some binders allow additional binding properties to support middleware-specific features. +Some binders let additional binding properties support middleware-specific features. -Configuration options can be provided to Spring Cloud Stream applications via any mechanism supported by Spring Boot. +Configuration options can be provided to Spring Cloud Stream applications through any mechanism supported by Spring Boot. This includes application arguments, environment variables, and YAML or .properties files. === Spring Cloud Stream Properties spring.cloud.stream.instanceCount:: - The number of deployed instances of an application. -Must be set for partitioning on the producer side, and on the consumer side if using RabbitMQ and with Kafka if `autoRebalanceEnabled=false`. +The number of deployed instances of an application. +Must be set for partitioning on the producer side. Must be set on the consumer side when using RabbitMQ and with Kafka if `autoRebalanceEnabled=false`. + Default: `1`. spring.cloud.stream.instanceIndex:: - The instance index of the application: a number from `0` to `instanceCount`-1. +The instance index of the application: A number from `0` to `instanceCount - 1`. Used for partitioning with RabbitMQ and with Kafka if `autoRebalanceEnabled=false`. Automatically set in Cloud Foundry to match the application's instance index. + spring.cloud.stream.dynamicDestinations:: - A list of destinations that can be bound dynamically (for example, in a dynamic routing scenario). +A list of destinations that can be bound dynamically (for example, in a dynamic routing scenario). If set, only listed destinations can be bound. + -Default: empty (allowing any destination to be bound). +Default: empty (letting any destination be bound). spring.cloud.stream.defaultBinder:: - The default binder to use, if multiple binders are configured. +The default binder to use, if multiple binders are configured. See <>. + Default: empty. spring.cloud.stream.overrideCloudConnectors:: - This property is only applicable when the `cloud` profile is active and Spring Cloud Connectors are provided with the application. -If the property is false (the default), the binder will detect a suitable bound service (e.g. a RabbitMQ service bound in Cloud Foundry for the RabbitMQ binder) and will use it for creating connections (usually via Spring Cloud Connectors). -When set to true, this property instructs binders to completely ignore the bound services and rely on Spring Boot properties (e.g. relying on the `spring.rabbitmq.*` properties provided in the environment for the RabbitMQ binder). +This property is only applicable when the `cloud` profile is active and Spring Cloud Connectors are provided with the application. +If the property is `false` (the default), the binder detects a suitable bound service (for example, a RabbitMQ service bound in Cloud Foundry for the RabbitMQ binder) and uses it for creating connections (usually through Spring Cloud Connectors). +When set to `true`, this property instructs binders to completely ignore the bound services and rely on Spring Boot properties (for example, relying on the `spring.rabbitmq.*` properties provided in the environment for the RabbitMQ binder). The typical usage of this property is to be nested in a customized environment <>. + -Default: false. +Default: `false`. spring.cloud.stream.bindingRetryInterval:: - The interval (seconds) between retrying binding creation when, for example, the binder doesn't support late binding and the broker is down (e.g. Apache Kafka). -Set to zero to treat such conditions as fatal, preventing the application from starting. +The interval (in seconds) between retrying binding creation when, for example, the binder does not support late binding and the broker (for example, Apache Kafka) is down. +Set it to zero to treat such conditions as fatal, preventing the application from starting. + -Default: 30 +Default: `30` [[binding-properties]] === Binding Properties -Binding properties are supplied using the format `spring.cloud.stream.bindings..=`. -The `` represents the name of the channel being configured (e.g., `output` for a `Source`). +Binding properties are supplied by using the format of `spring.cloud.stream.bindings..=`. +The `` represents the name of the channel being configured (for example, `output` for a `Source`). -To avoid repetition, Spring Cloud Stream supports setting values for all channels, in the format `spring.cloud.stream.default.=`. +To avoid repetition, Spring Cloud Stream supports setting values for all channels, in the format of `spring.cloud.stream.default.=`. -In what follows, we indicate where we have omitted the `spring.cloud.stream.bindings..` prefix and focus just on the property name, with the understanding that the prefix will be included at runtime. +In what follows, we indicate where we have omitted the `spring.cloud.stream.bindings..` prefix and focus just on the property name, with the understanding that the prefix ise included at runtime. ==== Properties for Use of Spring Cloud Stream -The following binding properties are available for both input and output bindings and must be prefixed with `spring.cloud.stream.bindings..`, e.g. `spring.cloud.stream.bindings.input.destination=ticktock`. +The following binding properties are available for both input and output bindings and must be prefixed with `spring.cloud.stream.bindings..` (for example, `spring.cloud.stream.bindings.input.destination=ticktock`). -Default values can be set by using the prefix `spring.cloud.stream.default`, e.g. `spring.cloud.stream.default.contentType=application/json`. +Default values can be set by using the `spring.cloud.stream.default` prefix (for example`spring.cloud.stream.default.contentType=application/json`). destination:: - The target destination of a channel on the bound middleware (e.g., the RabbitMQ exchange or Kafka topic). - If the channel is bound as a consumer, it could be bound to multiple destinations and the destination names can be specified as comma separated String values. - If not set, the channel name is used instead. - The default value of this property cannot be overridden. +The target destination of a channel on the bound middleware (for example, the RabbitMQ exchange or Kafka topic). +If the channel is bound as a consumer, it could be bound to multiple destinations, and the destination names can be specified as comma-separated `String` values. +If not set, the channel name is used instead. +The default value of this property cannot be overridden. group:: - The consumer group of the channel. +The consumer group of the channel. Applies only to inbound bindings. See <>. + -Default: null (indicating an anonymous consumer). +Default: `null` (indicating an anonymous consumer). contentType:: - The content type of the channel. -//See <>. +The content type of the channel. +See "`<>`". + -Default: null (so that no type coercion is performed). +Default: `null` (no type coercion is performed). binder:: - The binder used by this binding. -See <> for details. +The binder used by this binding. +See "`<>`" for details. + -Default: null (the default binder will be used, if one exists). +Default: `null` (the default binder is used, if it exists). -==== Consumer properties +==== Consumer Properties -The following binding properties are available for input bindings only and must be prefixed with `spring.cloud.stream.bindings..consumer.`, e.g. `spring.cloud.stream.bindings.input.consumer.concurrency=3`. +The following binding properties are available for input bindings only and must be prefixed with `spring.cloud.stream.bindings..consumer.` (for example, `spring.cloud.stream.bindings.input.consumer.concurrency=3`). -Default values can be set by using the prefix `spring.cloud.stream.default.consumer`, e.g. `spring.cloud.stream.default.consumer.headerMode=none`. +Default values can be set by using the `spring.cloud.stream.default.consumer` prefix (for example, `spring.cloud.stream.default.consumer.headerMode=none`). concurrency:: - The concurrency of the inbound consumer. +The concurrency of the inbound consumer. + Default: `1`. partitioned:: - Whether the consumer receives data from a partitioned producer. +Whether the consumer receives data from a partitioned producer. + Default: `false`. headerMode:: - When set to `none`, disables header parsing on input. +When set to `none`, disables header parsing on input. Effective only for messaging middleware that does not support message headers natively and requires header embedding. This option is useful when consuming data from non-Spring Cloud Stream applications when native headers are not supported. -When set to `headers`, uses the middleware's native header mechanism. -When set to `embeddedHeaders`, embeds headers into the message payload. +When set to `headers`, it uses the middleware's native header mechanism. +When set to `embeddedHeaders`, it embeds headers into the message payload. + -Default: depends on binder implementation. +Default: depends on the binder implementation. maxAttempts:: - If processing fails, the number of attempts to process the message (including the first). - Set to 1 to disable retry. +If processing fails, the number of attempts to process the message (including the first). +Set to `1` to disable retry. + Default: `3`. backOffInitialInterval:: - The backoff initial interval on retry. +The backoff initial interval on retry. + Default: `1000`. backOffMaxInterval:: - The maximum backoff interval. +The maximum backoff interval. + Default: `10000`. backOffMultiplier:: - The backoff multiplier. +The backoff multiplier. + Default: `2.0`. instanceIndex:: - When set to a value greater than equal to zero, allows customizing the instance index of this consumer (if different from `spring.cloud.stream.instanceIndex`). -When set to a negative value, it will default to `spring.cloud.stream.instanceIndex`. -See that property for more information. +When set to a value greater than equal to zero, it allows customizing the instance index of this consumer (if different from `spring.cloud.stream.instanceIndex`). +When set to a negative value, it defaults to `spring.cloud.stream.instanceIndex`. +See "`<>`" for more information. + Default: `-1`. instanceCount:: - When set to a value greater than equal to zero, allows customizing the instance count of this consumer (if different from `spring.cloud.stream.instanceCount`). -When set to a negative value, it will default to `spring.cloud.stream.instanceCount`. -See that property for more information. +When set to a value greater than equal to zero, it allows customizing the instance count of this consumer (if different from `spring.cloud.stream.instanceCount`). +When set to a negative value, it defaults to `spring.cloud.stream.instanceCount`. +See "`<>`" for more information. + Default: `-1`. ==== Producer Properties -The following binding properties are available for output bindings only and must be prefixed with `spring.cloud.stream.bindings..producer.`, e.g. `spring.cloud.stream.bindings.input.producer.partitionKeyExpression=payload.id`. +The following binding properties are available for output bindings only and must be prefixed with `spring.cloud.stream.bindings..producer.` (for example, `spring.cloud.stream.bindings.input.producer.partitionKeyExpression=payload.id`). -Default values can be set by using the prefix `spring.cloud.stream.default.producer`, e.g. `spring.cloud.stream.default.producer.partitionKeyExpression=payload.id`. +Default values can be set by using the prefix `spring.cloud.stream.default.producer` (for example, `spring.cloud.stream.default.producer.partitionKeyExpression=payload.id`). partitionKeyExpression:: - A SpEL expression that determines how to partition outbound data. -If set, or if `partitionKeyExtractorClass` is set, outbound data on this channel will be partitioned, and `partitionCount` must be set to a value greater than 1 to be effective. -The two options are mutually exclusive. -See <>. +A SpEL expression that determines how to partition outbound data. +If set, or if `partitionKeyExtractorClass` is set, outbound data on this channel is partitioned. `partitionCount` must be set to a value greater than 1 to be effective. +Mutually exclusive with `partitionKeyExtractorClass`. +See "`<>`". + Default: null. partitionKeyExtractorClass:: - A `PartitionKeyExtractorStrategy` implementation. -If set, or if `partitionKeyExpression` is set, outbound data on this channel will be partitioned, and `partitionCount` must be set to a value greater than 1 to be effective. -The two options are mutually exclusive. -See <>. +A `PartitionKeyExtractorStrategy` implementation. +If set, or if `partitionKeyExpression` is set, outbound data on this channel is partitioned. `partitionCount` must be set to a value greater than 1 to be effective. +Mutually exclusive with `partitionKeyExpression`. +See "`<>`". + -Default: null. +Default: `null`. partitionSelectorClass:: A `PartitionSelectorStrategy` implementation. Mutually exclusive with `partitionSelectorExpression`. -If neither is set, the partition will be selected as the `hashCode(key) % partitionCount`, where `key` is computed via either `partitionKeyExpression` or `partitionKeyExtractorClass`. +If neither is set, the partition is selected as the `hashCode(key) % partitionCount`, where `key` is computed through either `partitionKeyExpression` or `partitionKeyExtractorClass`. + -Default: null. +Default: `null`. partitionSelectorExpression:: - A SpEL expression for customizing partition selection. +A SpEL expression for customizing partition selection. Mutually exclusive with `partitionSelectorClass`. -If neither is set, the partition will be selected as the `hashCode(key) % partitionCount`, where `key` is computed via either `partitionKeyExpression` or `partitionKeyExtractorClass`. +If neither is set, the partition is selected as the `hashCode(key) % partitionCount`, where `key` is computed through either `partitionKeyExpression` or `partitionKeyExtractorClass`. + -Default: null. +Default: `null`. partitionCount:: - The number of target partitions for the data, if partitioning is enabled. -Must be - set to a value greater than 1 if the producer is partitioned. -On Kafka, interpreted as a - hint; the larger of this and the partition count of the target topic is used instead. +The number of target partitions for the data, if partitioning is enabled. +Must be set to a value greater than 1 if the producer is partitioned. +On Kafka, it is interpreted as a hint. The larger of this and the partition count of the target topic is used instead. + Default: `1`. requiredGroups:: - A comma-separated list of groups to which the producer must ensure message delivery even if they start after it has been created (e.g., by pre-creating durable queues in RabbitMQ). +A comma-separated list of groups to which the producer must ensure message delivery even if they start after it has been created (for example, by pre-creating durable queues in RabbitMQ). headerMode:: - When set to `none`, disables header embedding on output. -Effective only for messaging middleware that does not support message headers natively and requires header embedding. +When set to `none`, it disables header embedding on output. +It is effective only for messaging middleware that does not support message headers natively and requires header embedding. This option is useful when producing data for non-Spring Cloud Stream applications when native headers are not supported. -When set to `headers`, uses the middleware's native header mechanism. -When set to `embeddedHeaders`, embeds headers into the message payload. +When set to `headers`, it uses the middleware's native header mechanism. +When set to `embeddedHeaders`, it embeds headers into the message payload. + -Default: Depends on binder implementation. +Default: Depends on the binder implementation. useNativeEncoding:: - When set to `true`, the outbound message is serialized directly by client library, which must be configured correspondingly (e.g. setting an appropriate Kafka producer value serializer). +When set to `true`, the outbound message is serialized directly by client library, which must be configured correspondingly (for example, setting an appropriate Kafka producer value serializer). When this configuration is being used, the outbound message marshalling is not based on the `contentType` of the binding. -When native encoding is used, it is the responsibility of the consumer to use appropriate decoder (ex: Kafka consumer value de-serializer) to deserialize the inbound message. -Also, when native encoding/decoding is used the `headerMode=embeddedHeaders` property is ignored and headers will not be embedded into the message. +When native encoding is used, it is the responsibility of the consumer to use an appropriate decoder (for example, the Kafka consumer value de-serializer) to deserialize the inbound message. +Also, when native encoding and decoding is used, the `headerMode=embeddedHeaders` property is ignored and headers are not embedded in the message. + Default: `false`. errorChannelEnabled:: - When set to `true`, if the binder supports async send results; send failures will be sent to an error channel for the destination. - See <> for more information. +When set to `true`, if the binder supports asynchroous send results, send failures are sent to an error channel for the destination. +See "`<>`" for more information. + Default: `false`. [[dynamicdestination]] -=== Using dynamically bound destinations +=== Using Dynamically Bound Destinations -Besides the channels defined via `@EnableBinding`, Spring Cloud Stream allows applications to send messages to dynamically bound destinations. +Besides the channels defined by using `@EnableBinding`, Spring Cloud Stream lets applications send messages to dynamically bound destinations. This is useful, for example, when the target destination needs to be determined at runtime. Applications can do so by using the `BinderAwareChannelResolver` bean, registered automatically by the `@EnableBinding` annotation. -The property 'spring.cloud.stream.dynamicDestinations' can be used for restricting the dynamic destination names to a set known beforehand (whitelisting). -If the property is not set, any destination can be bound dynamically. +The 'spring.cloud.stream.dynamicDestinations' property can be used for restricting the dynamic destination names to a known set (whitelisting). +If this property is not set, any destination can be bound dynamically. -The `BinderAwareChannelResolver` can be used directly as in the following example, in which a REST controller uses a path variable to decide the target channel. +The `BinderAwareChannelResolver` can be used directly, as shown in the following example of a REST controller using a path variable to decide the target channel: [source,java] ---- @@ -1472,7 +1450,7 @@ public class SourceWithDynamicDestination { } ---- -After starting the application on the default port 8080, when sending the following data: +Now consider what happens when we start the application on the default port (8080) and make the following requests with CURL: ---- curl -H "Content-Type: application/json" -X POST -d "customer-1" http://localhost:8080/customers @@ -1480,10 +1458,9 @@ curl -H "Content-Type: application/json" -X POST -d "customer-1" http://localhos curl -H "Content-Type: application/json" -X POST -d "order-1" http://localhost:8080/orders ---- -The destinations 'customers' and 'orders' are created in the broker (for example: exchange in case of Rabbit or topic in case of Kafka) with the names 'customers' and 'orders', and the data is published to the appropriate destinations. +The destinations, 'customers' and 'orders', are created in the broker (in the exchange for Rabbit or in the topic for Kafka) with names of 'customers' and 'orders', and the data is published to the appropriate destinations. -The `BinderAwareChannelResolver` is a general purpose Spring Integration `DestinationResolver` and can be injected in other components. -For example, in a router using a SpEL expression based on the `target` field of an incoming JSON message. +The `BinderAwareChannelResolver` is a general-purpose Spring Integration `DestinationResolver` and can be injected in other components -- for example, in a router using a SpEL expression based on the `target` field of an incoming JSON message. The following example includes a router that reads SpEL expressions: [source,java] ---- @@ -1526,8 +1503,9 @@ public class SourceWithDynamicDestination { The https://github.com/spring-cloud-stream-app-starters/router[Router Sink Application] uses this technique to create the destinations on-demand. If the channel names are known in advance, you can configure the producer properties as with any other destination. -Alternatively, if you register a `NewBindingCallback<>` bean, it will be invoked just before the binding is created. -The callback takes the generic type of the extended producer properties used by the binder; it has one method: +Alternatively, if you register a `NewBindingCallback<>` bean, it is invoked just before the binding is created. +The callback takes the generic type of the extended producer properties used by the binder. +It has one method: [source, java] ---- @@ -1535,7 +1513,7 @@ void configure(String channelName, MessageChannel channel, ProducerProperties pr T extendedProducerProperties); ---- -The following is an example using the RabbitMQ binder: +The following example shows how to use the RabbitMQ binder: [source, java] ---- @@ -1552,32 +1530,25 @@ public NewBindingCallback dynamicConfigurer() { NOTE: If you need to support dynamic destinations with multiple binder types, use `Object` for the generic type and cast the `extended` argument as needed. -[[contenttypemanagement]] -== Content Type negotiation +[[content-type-management]] +== Content Type Negotiation -=== Introduction +Data transformation is one of the core features of any message-driven microservice architecture. Given that, in Spring Cloud Stream, such data +is represented as a Spring `Message`, a message may have to be transformed to a desired shape or size before reaching its destination. This is required for two reasons: -Data transformation is one of the core features of any message-driven microservice architecture. Given that in Spring Cloud Stream, such data -is represented as a Spring `Message`, such message may have to be transformed to a desired shape/size before reaching its destination. This is required for two reasons: +. To convert the contents of the incoming message to match the signature of the application-provided handler. -_1. To convert the contents of the incoming message to match the signature of the application-provided handler._ +. To convert the contents of the outgoing message to the wire format. -_2. To convert the contents of the outgoing message to the wire format._ +The wire format is typically `byte[]` (that is true for the Kafka and Rabbit binders), but it is governed by the binder implementation. -The wire format is typically `byte[]` (i.e., Kafka and Rabbit binders), but is governed by the binder implementation. +In Spring Cloud Stream, message transformation is accomplished with an `org.springframework.messaging.converter.MessageConverter`. -In Spring Cloud Stream, message transformation is accomplished with a `org.springframework.messaging.converter.MessageConverter`. - -[NOTE] -==== -As a supplement to the details to follow you may also want to read the following -https://spring.io/blog/2018/02/26/spring-cloud-stream-2-0-content-type-negotiation-and-transformation[blog] -==== +NOTE: As a supplement to the details to follow, you may also want to read the following https://spring.io/blog/2018/02/26/spring-cloud-stream-2-0-content-type-negotiation-and-transformation[blog post]. === Mechanics -To better understand the mechanics and the necessity behind content-type negotiation let’s look at the very simple use case using the following message -handler as an example. Also let’s assume that this is the only handler in the application (no internal pipeline) for simplicity. +To better understand the mechanics and the necessity behind content-type negotiation, we take a look at a very simple use case by using the following message handler as an example: [source, java] ---- @@ -1586,63 +1557,61 @@ handler as an example. Also let’s assume that this is the only handler in the public String handle(Person person) {..} ---- -The above handler expects `Person` type as an argument and will produce `String` type as an output. In order for the framework to succeed in passing the incoming -`Message` as an argument to this handler it has to somehow transform the payload of the `Message` from the wire format to `Person` type. -In other words the framework must locate and apply the appropriate `MessageConverter`. To accomplish that the framework needs some instructions -from the user. One of these instructions is already provided by the signature of the handler method itself (`Person` type), so in theory, that should and in some -cases is enough, but for the majority of the use cases in order to select the appropriate `MessageConverter` the framework needs an additional piece of information. +NOTE: For simplicity, we assume that this is the only handler in the application (we assume there is no internal pipeline). + +The handler shown in the preceding example expects a `Person` object as an argument and produces a `String` type as an output. +In order for the framework to succeed in passing the incoming `Message` as an argument to this handler, it has to somehow transform the payload of the `Message` type from the wire format to a `Person` type. +In other words, the framework must locate and apply the appropriate `MessageConverter`. +To accomplish that, the framework needs some instructions from the user. +One of these instructions is already provided by the signature of the handler method itself (`Person` type). +Consequently, in theory, that should be (and, in some cases, is) enough. +However, for the majority of use cases, in order to select the appropriate `MessageConverter`, the framework needs an additional piece of information. That missing piece is `contentType`. -Spring Cloud Stream provides three simple mechanisms to define `contentType` and they all come with precedence order: +Spring Cloud Stream provides three mechanisms to define `contentType` (in order of precedence): -_1. ***HEADER*** - the `contentType` can be communicated through the Message itself. By simply providing `contentType` header you are declaring the content type to use to locate and -apply the appropriate MessageConverter._ +. *HEADER*: The `contentType` can be communicated through the Message itself. By providing a `contentType` header, you declare the content type to use to locate and apply the appropriate `MessageConverter`. -_2. ***BINDING*** - the `contentType` can be set per destination binding via `spring.cloud.stream.bindings.input.content-type` property. NOTE: the segment `input` in the property name -corresponds to the actual name of the destination which is “input” in our case. This approach allows one to declare per-binding the content type to use to locate and -apply the appropriate MessageConverter._ +. *BINDING*: The `contentType` can be set per destination binding by setting the `spring.cloud.stream.bindings.input.content-type` property. ++ +NOTE: The `input` segment in the property name corresponds to the actual name of the destination (which is “input” in our case). This approach lets you declare, on a per-binding basis, the content type to use to locate and apply the appropriate `MessageConverter`. -_3. ***DEFAULT*** - in the event `contentType` is not present in the Message header and/or binding, the default `application/json` content type will be used to -locate and apply the appropriate MessageConverter._ +. *DEFAULT*: If `contentType` is not present in the `Message` header or the binding, the default `application/json` content type is used to +locate and apply the appropriate `MessageConverter`. -As mentioned, the above also demonstrates the order of precedence in the event there is a tie. For example, header provided content type takes precedence over any other content type. -The same applies for content type set per binding which essentially allows one to override the default content type. But it also provides a sensible default which was determined from -the community feedback. +As mentioned earlier, the preceding list also demonstrates the order of precedence in case of a tie. For example, a header-provided content type takes precedence over any other content type. +The same applies for a content type set on a per-binding basis, which essentially lets you override the default content type. +However, it also provides a sensible default (which was determined from community feedback). -Another reason for making `application/json` the default stems from the interoperability requirements driven by distributed microservices architectures where producer and consumer not only -run in different JVMs, but can also run on different non-JVM platforms. +Another reason for making `application/json` the default stems from the interoperability requirements driven by distributed microservices architectures, where producer and consumer not only run in different JVMs but can also run on different non-JVM platforms. -Once the non-void handler method returns and unless the return value is already a `Message`, the new `Message` is constructed with return vlaue as the payload while inheriting -headers from the input `Message` less the ones defined/filtered by `SpringIntegrationProperties.messageHandlerNotPropagatedHeaders`. -By default, there is only one header set there - `contentType`. This means that the new `Message` will not have `contentType` header set, thus ensuring that the `contentType` -can evolve. You can always opt out to returning a `Message` from the handler method where you can inject any header you wish. +When the non-void handler method returns, if the the return value is already a `Message`, that `Message` becomes the payload. However, when the return value is not a `Message`, the new `Message` is constructed with the return value as the payload while inheriting +headers from the input `Message` minus the headers defined or filtered by `SpringIntegrationProperties.messageHandlerNotPropagatedHeaders`. +By default, there is only one header set there: `contentType`. This means that the new `Message` does not have `contentType` header set, thus ensuring that the `contentType` can evolve. +You can always opt out of returning a `Message` from the handler method where you can inject any header you wish. -If there is an internal pipeline the `Message` is sent to the next handler going through the same process of conversion, or if there is no internal - pipeline or you’ve reached the end of it the `Message` is sent back to the output destination. +If there is an internal pipeline, the `Message` is sent to the next handler by going through the same process of conversion. However, if there is no internal pipeline or you have reached the end of it, the `Message` is sent back to the output destination. -==== Content type vs. argument type +==== Content Type versus Argument Type -As it was mentioned, for the framework to select the appropriate MessageConverter it requires _argument type_ and optionally _content type_ information. -The logic for selecting the appropriate `MessageConverter` resides with the argument resolvers (`HandlerMethodArgumentResolvers`), right before the invocation of the user -defined handler method (that is when the actual argument type is known to the framework). -If argument type does NOT match the type of the current payload the framework delegates to the stack of the -pre-configured `MessageConverters` to see if any one of them can convert the payload. As you can see the `Object fromMessage(Message message, Class targetClass);` -operation of the MessageConverter takes `targetClass` as one of its arguments. The framework also ensures that the provided `Message` always contains `contentType` header -in the event one was not there already (injects the default one or the one set per binding). -That is the mechanism by which framework determines if message can be converted to a target type - `contentType` and argumenyt type. -If no appropriate `MessageConverter` is found the exception is thrown at which time you can add custom `MessageConverter` (more on this later). +As mentioned earlier, for the framework to select the appropriate `MessageConverter`, it requires argument type and, optionally, content type information. +The logic for selecting the appropriate `MessageConverter` resides with the argument resolvers (`HandlerMethodArgumentResolvers`), which trigger right before the invocation of the user-defined handler method (which is when the actual argument type is known to the framework). +If the argument type does not match the type of the current payload, the framework delegates to the stack of the +pre-configured `MessageConverters` to see if any one of them can convert the payload. +As you can see, the `Object fromMessage(Message message, Class targetClass);` +operation of the MessageConverter takes `targetClass` as one of its arguments. +The framework also ensures that the provided `Message` always contains a `contentType` header. +When no contentType header was already present, it injects either the per-binding `contentType` header or the default `contentType` header. +The combination of `contentType` argument type is the mechanism by which framework determines if message can be converted to a target type. +If no appropriate `MessageConverter` is found, an exception is thrown, which you can handle by adding a custom `MessageConverter` (see "`<>`"). -But what if the payload type matches the target type declared by the handler method? In this cases there is obviously nothing to convert and the -payload will be passed unmodified. While this sounds pretty straight forward and logical, keep in mind handler methods that take `Message` and/or `Object` as an -argument. By doing so you are essentially forfeiting the conversion process by declaring the target type to be `Object` which is an `instanceof` everything in Java. - -In other words: -[NOTE] -==== -Do NOT expect Message to be converted into some type based on the `contentType` only. Remember that the `contentType` is complimentary to the target type. -A hint if you wish which `MessageConverter` may or may not take into consideration. -==== +But what if the payload type matches the target type declared by the handler method? In this case, there is nothing to convert, and the +payload is passed unmodified. While this sounds pretty straightforward and logical, keep in mind handler methods that take a `Message` or `Object` as an argument. +By declaring the target type to be `Object` (which is an `instanceof` everything in Java), you essentially forfeit the conversion process. +NOTEDo not expect `Message` to be converted into some other type based only on the `contentType`. +Remember that the `contentType` is complementary to the target type. +If you wish, you can provide a hint, which `MessageConverter` may or may not take into consideration. ==== Message Converters @@ -1655,18 +1624,17 @@ Object fromMessage(Message message, Class targetClass); Message toMessage(Object payload, @Nullable MessageHeaders headers); ---- -It is important to understand the contract of these methods and their usage specifically in the context of Spring Cloud Stream. +It is important to understand the contract of these methods and their usage, specifically in the context of Spring Cloud Stream. -The `fromMessage` method converts incoming `Message` to an argument type. The payload of the `Message` could be _any type_ and it's -up to the actual implementation of the `MessageConverter` to support multiple types. For example, some JSON converter may support the payload type as `byte[]` -and `String` etc. This is important when application contains an internal pipeline (i.e., _input -> handler1 -> handler2 ->. . . -> output_) and the output of -the upstream handler results in a `Message` which may not be in the initial wire format. +The `fromMessage` method converts an incoming `Message` to an argument type. +The payload of the `Message` could be any type, and it is +up to the actual implementation of the `MessageConverter` to support multiple types. +For example, some JSON converter may support the payload type as `byte[]`, `String`, and others. +This is important when the application contains an internal pipeline (that is, input -> handler1 -> handler2 ->. . . -> output) and the output of the upstream handler results in a `Message` which may not be in the initial wire format. -However. . . +However, the `toMessage` method has a more strict contract and must always convert `Message` to the wire format: `byte[]`. -The `toMessage` method has a more strict contract and must always convert `Message` to the wire format - `byte[]`. - -So for all intents and purposes (and especially when implementing your own converter) you might as well look at them as: +So, for all intents and purposes (and especially when implementing your own converter) you regard the two methods as having the following signatures: [source, java] ---- @@ -1677,43 +1645,35 @@ Message toMessage(Object payload, @Nullable MessageHeaders headers); === Provided MessageConverters -As it was mentioned earlier the framework already provides a stack of `MessageConverters` to handle most common use cases. Below is the ordered list of provided `MessageConverters`. +As mentioned earlier, the framework already provides a stack of `MessageConverters` to handle most common use cases. +The following list describes the provided `MessageConverters`, in order of precedence (the first `MessageConverter` that works is used): -[NOTE] -==== -It is important to understand the importance of the order since the mechanism by which the framework locates the appropriate `MessageConverter` is by iterating through each and asking -if it can convert using the first one that can convert. -==== +. `ApplicationJsonMessageMarshallingConverter`: Variation of the `org.springframework.messaging.converter.MappingJackson2MessageConverter`. Supports conversion of the payload of the `Message` from `String` or `byte[]`. +. `TupleJsonMessageConverter`: *DEPRECATED* Supports conversion of the payload of the `Message` from `org.springframework.tuple.Tuple`. +. `ByteArrayMessageConverter`: Supports conversion of the payload of the `Message` from `byte[]` to `byte[]` for cases when `contentType` is set to `application/octet-stream`. It is essentially a pass through and exists primarily for backward compatibility. +. `ObjectStringMessageConverter`: Supports conversion of any type to a `String` when `contentType` is `text/plain`. +It invokes Object’s `toString()` method or, if the payload is `byte[]`, a new `String(byte[])`. +. `JavaSerializationMessageConverter`: *DEPRECATED* Supports conversion based on java serialization when `contentType` is `application/x-java-serialized-object`. +. `KryoMessageConverter`: *DEPRECATED* Supports conversion based on Kryo serialization when `contentType` is `application/x-java-object`. +. `JsonUnmarshallingConverter`: Similar to the `ApplicationJsonMessageMarshallingConverter`. It supports conversion of any type when `contentType` is `application/x-java-object`. +It expects the actual type information to be embedded in the `contentType` as an attribute (for example, `application/x-java-object;type=thingOne.thingTwo.Cat`). -1. `ApplicationJsonMessageMarshallingConverter` - _variation of the `org.springframework.messaging.converter.MappingJackson2MessageConverter`. Supports conversion of the payload of the -`Message` from `String` or `byte[]`._ -2. `TupleJsonMessageConverter` - _***[DEPRECATED]*** Supports conversion of the payload of the `Message` from `org.springframework.tuple.Tuple`._ -3. `ByteArrayMessageConverter` - _Supports conversion of the payload of the `Message` from `byte[]` to `byte[]` for cases when `contentType` is set to `application/octet-stream`. -Essentially a pass through and exists primarily for backward compatibility._ -4. `ObjectStringMessageConverter` - _Supports conversion of any type to a `String`, when contentType is `text/plain`. Invokes Object’s `toString()` method or if payload is -`byte[]` then new `String(byte[])`._ -5. `JavaSerializationMessageConverter` - _***[DEPRECATED]*** Supports conversion based on java serialization when `contentType` is `application/x-java-serialized-object`._ -6. `KryoMessageConverter` - _***[DEPRECATED]*** Supports conversion based on kryo serialization when `contentType` is `application/x-java-object`._ -7. `JsonUnmarshallingConverter` - _Similar to the `ApplicationJsonMessageMarshallingConverter`. Supports conversion of any type when `contentType` is `application/x-java-object`. -Expects the actual type information to be embedded in the `contentType` as an attribute (e.g., `application/x-java-object;type=foo.bar.Baz`)._ +When no appropriate converter is found, the framework throws an exception. When that happens, you should check your code and configuration and ensure you did not miss anything (that is, ensure that you provided a `contentType` by using a binding or a header). +However, most likely, you found some uncommon case (such as a custom `contentType` perhaps) and the current stack of provided `MessageConverters` +does not know how to convert. If that is the case, you can add custom `MessageConverter`. See <>. -In the event no appropriate converter is found the framework will throw an exception at which point you should check your code and configfuration and ensure you didn't miss anything -(i.e., provide `contentType` via binding or header). However, most likely you are dealing with some uncommon case (custom `contentType` perhaps) and the current stack of provided `MessageConverters` -doesn't know how to convert. And if that's the case you can add custom `MessageConverter`. +[[spring-cloud-stream-overview-user-defined-message-converters]] +=== User-defined Message Converters -=== User defined Message Converters +Spring Cloud Stream exposes a mechanism to define and register additional `MessageConverters`. +To use it, implement `org.springframework.messaging.converter.MessageConverter`, configure it as a `@Bean`, and annotate it with `@StreamMessageConverter`. +It is then added to the existing stack of `MessageConverters`. +The `@StreamMessageConverter` annotation ensures that other other converters that may be present on the application context are not picked up by mistake. -Spring Cloud Stream exposes a mechanism to define and register additional `MessageConverters`. All you need to do is implement `org.springframework.messaging.converter.MessageConverter`, -confiure it as `@Bean` and annotate it with `@StreamMessageConverter` and it will be added to the existing stack of `MessageConverters`. The `@StreamMessageConverter` qualifier annotation -is to avoid picking up other converters that may be present on the _Application Context_. +NOTE: It is important to understand that custom `MessageConverter` implementations are added to the head of the existing stack. +Consequently, custom `MessageConverter` implementations take precedence over the existing ones, which lets you override as well as add to the existing converters. -[NOTE] -==== -It is important to undetrstand that custom `MessageConverters` are added to the head of the existing stack. -This allows custom `MessageConverters` to take precedence over the existing ones, thus supporting not only addition, but the override of the existing ones. -==== - -Here is an example of creating a message converter bean to support new content type `application/bar`: +The following example shows how to create a message converter bean to support a new content type called `application/bar`: [source,java] ---- @@ -1729,10 +1689,7 @@ public static class SinkApplication { return new MyCustomMessageConverter(); } } ----- -[source,java] ----- public class MyCustomMessageConverter extends AbstractMessageConverter { public MyCustomMessageConverter() { @@ -1753,10 +1710,10 @@ public class MyCustomMessageConverter extends AbstractMessageConverter { ---- Spring Cloud Stream also provides support for Avro-based converters and schema evolution. -See <> for details. +See "`<>`" for details. [[schema-evolution]] -== Schema evolution support +== Schema Evolution Support Spring Cloud Stream provides support for schema-based message converters through its `spring-cloud-stream-schema` module. Currently, the only serialization format supported out of the box for schema-based message converters is Apache Avro, with more formats to be added in future versions. @@ -1765,18 +1722,18 @@ Currently, the only serialization format supported out of the box for schema-bas The `spring-cloud-stream-schema` module contains two types of message converters that can be used for Apache Avro serialization: -* converters using the class information of the serialized/deserialized objects, or a schema with a location known at startup; -* converters using a schema registry - they locate the schemas at runtime, as well as dynamically registering new schemas as domain objects evolve. +* Converters that use the class information of the serialized or deserialized objects or a schema with a location known at startup. +* Converters that use a schema registry. They locate the schemas at runtime and dynamically register new schemas as domain objects evolve. -=== Converters with schema support +=== Converters with Schema Support -The `AvroSchemaMessageConverter` supports serializing and deserializing messages either using a predefined schema or by using the schema information available in the class (either reflectively, or contained in the `SpecificRecord`). -If the target type of the conversion is a `GenericRecord`, then a schema must be set. +The `AvroSchemaMessageConverter` supports serializing and deserializing messages either by using a predefined schema or by using the schema information available in the class (either reflectively or contained in the `SpecificRecord`). +If the target type of the conversion is a `GenericRecord`, a schema must be set. -For using it, you can simply add it to the application context, optionally specifying one ore more `MimeTypes` to associate it with. +To use it, you can simply add it to the application context, optionally specifying one or more `MimeTypes` with which to associate it. The default `MimeType` is `application/avro`. -Here is an example of configuring it in a sink application registering the Apache Avro `MessageConverter`, without a predefined schema: +The following example shows how to configure a converter in a sink application by registering the Apache Avro `MessageConverter` without a predefined schema: [source,java] ---- @@ -1793,7 +1750,7 @@ public static class SinkApplication { } ---- -Conversely, here is an application that registers a converter with a predefined schema, to be found on the classpath: +Conversely, the following application registers a converter with a predefined schema (found on the classpath): [source,java] ---- @@ -1812,33 +1769,34 @@ public static class SinkApplication { } ---- -In order to understand the schema registry client converter, we will describe the schema registry support first. +In order to understand the schema registry client converter, we must first describe the schema registry support. === Schema Registry Support Most serialization models, especially the ones that aim for portability across different platforms and languages, rely on a schema that describes how the data is serialized in the binary payload. In order to serialize the data and then to interpret it, both the sending and receiving sides must have access to a schema that describes the binary format. -In certain cases, the schema can be inferred from the payload type on serialization, or from the target type on deserialization, but in a lot of cases applications benefit from having access to an explicit schema that describes the binary data format. -A schema registry allows you to store schema information in a textual format (typically JSON) and makes that information accessible to various applications that need it to receive and send data in binary format. +In certain cases, the schema can be inferred from the payload type on serialization or from the target type on deserialization. +However, many applications benefit from having access to an explicit schema that describes the binary data format. +A schema registry lets you store schema information in a textual format (typically JSON) and makes that information accessible to various applications that need it to receive and send data in binary format. A schema is referenceable as a tuple consisting of: -* a _subject_ that is the logical name of the schema; -* the schema _version_; -* the schema _format_ which describes the binary format of the data. +* A subject that is the logical name of the schema +* The schema version +* The schema format, which describes the binary format of the data === Schema Registry Server Spring Cloud Stream provides a schema registry server implementation. -In order to use it, you can simply add the `spring-cloud-stream-schema-server` artifact to your project and use the `@EnableSchemaRegistryServer` annotation, adding the schema registry server REST controller to your application. -This annotation is intended to be used with Spring Boot web applications, and the listening port of the server is controlled by the `server.port` setting. -The `spring.cloud.stream.schema.server.path` setting can be used to control the root path of the schema server (especially when it is embedded in other applications). -The `spring.cloud.stream.schema.server.allowSchemaDeletion` boolean setting enables the deletion of schema. By default this is disabled. +To use it, you can add the `spring-cloud-stream-schema-server` artifact to your project and use the `@EnableSchemaRegistryServer` annotation, which adds the schema registry server REST controller to your application. +This annotation is intended to be used with Spring Boot web applications, and the listening port of the server is controlled by the `server.port` property. +The `spring.cloud.stream.schema.server.path` property can be used to control the root path of the schema server (especially when it is embedded in other applications). +The `spring.cloud.stream.schema.server.allowSchemaDeletion` boolean property enables the deletion of a schema. By default, this is disabled. The schema registry server uses a relational database to store the schemas. - By default, it uses an embedded database. -You can customize the schema storage using the http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-sql[Spring Boot SQL database and JDBC configuration options]. +By default, it uses an embedded database. +You can customize the schema storage by using the http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-sql[Spring Boot SQL database and JDBC configuration options]. -A Spring Boot application enabling the schema registry looks as follows: +The following example shows a Spring Boot application that enables the schema registry: [source,java] ---- @@ -1855,83 +1813,96 @@ public class SchemaRegistryServerApplication { The Schema Registry Server API consists of the following operations: -`POST /` +* `POST /` -- see "`<>`" +* 'GET /{subject}/{format}/{version}' -- see "`<>`" +* `GET /{subject}/{format}` -- see "`<>`" +* `GET /schemas/{id}` -- see "`<>`" +* `DELETE /{subject}/{format}/{version}` -- see "`<>`" +* `DELETE /schemas/{id}` -- see "`<>`" +* `DELETE /{subject}` -- see "`<>`" -Register a new schema. +[[spring-cloud-stream-overview-registering-new-schema]] +===== Registering a New Schema -Accepts JSON payload with the following fields: +To register a new schema, send a `POST` request to the `/` endpoint. -* `subject` the schema subject; -* `format` the schema format; -* `definition` the schema definition. +The `/` accepts a JSON payload with the following fields: -Response is a schema object in JSON format, with the following fields: +* `subject`: The schema subject +* `format`: The schema format +* `definition`: The schema definition -* `id` the schema id; -* `subject` the schema subject; -* `format` the schema format; -* `version` the schema version; -* `definition` the schema definition. +Its response is a schema object in JSON, with the following fields: -`GET /{subject}/{format}/{version}` +* `id`: The schema ID +* `subject`: The schema subject +* `format`: The schema format +* `version`: The schema version +* `definition`: The schema definition -Retrieve an existing schema by its subject, format and version. +[[spring-cloud-stream-overview-retrieve-schema-subject-format-version]] +===== Retrieving an Existing Schema by Subject, Format, and Version -Response is a schema object in JSON format, with the following fields: +To retrieve an existing schema by subject, format, and version, send `GET` request to the `/{subject}/{format}/{version}` endpoint. -* `id` the schema id; -* `subject` the schema subject; -* `format` the schema format; -* `version` the schema version; -* `definition` the schema definition. +Its response is a schema object in JSON, with the following fields: -`GET /{subject}/{format}` +* `id`: The schema ID +* `subject`: The schema subject +* `format`: The schema format +* `version`: The schema version +* `definition`: The schema definition -Retrieve a list of existing schema by its subject and format. +[[spring-cloud-stream-overview-retrieve-schema-subject-format]] +===== Retrieving an Existing Schema by Subject and Format -Response is a list of schemas with each schema object in JSON format, with the following fields: +To retrieve an existing schema by subject and format, send a `GET` request to the `/subject/format` endpoint. -* `id` the schema id; -* `subject` the schema subject; -* `format` the schema format; -* `version` the schema version; -* `definition` the schema definition. +Its response is a list of schemas with each schema object in JSON, with the following fields: -`GET /schemas/{id}` +* `id`: The schema ID +* `subject`: The schema subject +* `format`: The schema format +* `version`: The schema version +* `definition`: The schema definition -Retrieve an existing schema by its id. +[[spring-cloud-stream-overview-retrieve-schema-id]] +===== Retrieving an Existing Schema by ID -Response is a schema object in JSON format, with the following fields: +To retrieve a schema by its ID, send a `GET` request to the `/schemas/{id}` endpoint. -* `id` the schema id; -* `subject` the schema subject; -* `format` the schema format; -* `version` the schema version; -* `definition` the schema definition. +Its response is a schema object in JSON, with the following fields: -`DELETE /{subject}/{format}/{version}` +* `id`: The schema ID +* `subject`: The schema subject +* `format`: The schema format +* `version`: The schema version +* `definition`: The schema definition -Delete an existing schema by its subject, format and version. +[[spring-cloud-stream-overview-deleting-schema-subject-format-version]] +===== Deleting a Schema by Subject, Format, and Version -`DELETE /schemas/{id}` +To delete a schema identified by its subject, format, and version, send a `DELETE` request to the `/{subject}/{format}/{version}` endpoint. -Delete an existing schema by its id. +[[spring-cloud-stream-overview-deleting-schema-id]] +===== Deleting a Schema by ID +To delete a schema by its ID, send a `DELETE` request to the `/schemas/{id}` endpoint. + +[[spring-cloud-stream-overview-deleting-schema-subject]] +===== Deleting a Schema by Subject `DELETE /{subject}` Delete existing schemas by their subject. -[NOTE] -==== -This note applies to users of Spring Cloud Stream 1.1.0.RELEASE only. -Spring Cloud Stream 1.1.0.RELEASE used the table name `schema` for storing `Schema` objects, which is a keyword in a number of database implementations. -To avoid any conflicts in the future, starting with 1.1.1.RELEASE we have opted for the name `SCHEMA_REPOSITORY` for the storage table. -Any Spring Cloud Stream 1.1.0.RELEASE users that are upgrading are advised to migrate their existing schemas to the new table before upgrading. -==== +NOTE: This note applies to users of Spring Cloud Stream 1.1.0.RELEASE only. +Spring Cloud Stream 1.1.0.RELEASE used the table name, `schema`, for storing `Schema` objects. `Schema` is a keyword in a number of database implementations. +To avoid any conflicts in the future, starting with 1.1.1.RELEASE, we have opted for the name `SCHEMA_REPOSITORY` for the storage table. +Any Spring Cloud Stream 1.1.0.RELEASE users who upgrade should migrate their existing schemas to the new table before upgrading. === Schema Registry Client -The client-side abstraction for interacting with schema registry servers is the `SchemaRegistryClient` interface, with the following structure: +The client-side abstraction for interacting with schema registry servers is the `SchemaRegistryClient` interface, which has the following structure: [source,java] ---- @@ -1946,9 +1917,9 @@ public interface SchemaRegistryClient { } ---- -Spring Cloud Stream provides out of the box implementations for interacting with its own schema server, as well as for interacting with the Confluent Schema Registry. +Spring Cloud Stream provides out-of-the-box implementations for interacting with its own schema server and for interacting with the Confluent Schema Registry. -A client for the Spring Cloud Stream schema registry can be configured using the `@EnableSchemaRegistryClient` as follows: +A client for the Spring Cloud Stream schema registry can be configured byusing the `@EnableSchemaRegistryClient`, as follows: [source,java] ---- @@ -1960,18 +1931,15 @@ A client for the Spring Cloud Stream schema registry can be configured using the } ---- -[NOTE] -==== -The default converter is optimized to cache not only the schemas from the remote server but also the `parse()` and `toString()` methods that are quite expensive. +NOTE: The default converter is optimized to cache not only the schemas from the remote server but also the `parse()` and `toString()` methods, which are quite expensive. Because of this, it uses a `DefaultSchemaRegistryClient` that does not caches responses. If you intend to use the client directly on your code, you can request a bean that also caches responses to be created. -To do that, just add the property `spring.cloud.stream.schemaRegistryClient.cached=true` to your application properties. -==== +To do so, add the property `spring.cloud.stream.schemaRegistryClient.cached=true` to your application properties. ==== Using Confluent's Schema Registry -The default configuration will create a `DefaultSchemaRegistryClient` bean. -If you want to use the Confluent schema registry, you need to create a bean of type `ConfluentSchemaRegistryClient`, which will supersede the one configured by default by the framework. +The default configuration creates a `DefaultSchemaRegistryClient` bean. +If you want to use the Confluent schema registry, you need to create a bean of type `ConfluentSchemaRegistryClient`, which supersedes the one configured by default by the framework. The following example shows how to create such a bean: [source,java] ---- @@ -1982,20 +1950,17 @@ public SchemaRegistryClient schemaRegistryClient(@Value("${spring.cloud.stream.s return client; } ---- -[NOTE] -==== -The ConfluentSchemaRegistryClient is tested against Confluent platform version 3.2.2. -==== +NOTE: The ConfluentSchemaRegistryClient is tested against Confluent platform version 3.2.2. -==== Schema Registry Client properties +==== Schema Registry Client Properties The Schema Registry Client supports the following properties: -spring.cloud.stream.schemaRegistryClient.endpoint:: The location of the schema-server. -Use a full URL when setting this, including protocol (`http` or `https`) , port and context path. +`spring.cloud.stream.schemaRegistryClient.endpoint`:: The location of the schema-server. +When setting this, use a full URL, including protocol (`http` or `https`) , port, and context path. + -Default:: ``http://localhost:8990/`` -spring.cloud.stream.schemaRegistryClient.cached:: Whether the client should cache schema server responses. +Default:: `http://localhost:8990/` +`spring.cloud.stream.schemaRegistryClient.cached`:: Whether the client should cache schema server responses. Normally set to `false`, as the caching happens in the message converter. Clients using the schema registry client should set this to `true`. + @@ -2004,121 +1969,137 @@ Default:: `true` === Avro Schema Registry Client Message Converters -For Spring Boot applications that have a `SchemaRegistryClient` bean registered with the application context, Spring Cloud Stream will auto-configure an Apache Avro message converter that uses the schema registry client for schema management. +For Spring Boot applications that have a `SchemaRegistryClient` bean registered with the application context, Spring Cloud Stream autoconfigures an Apache Avro message converter that uses the schema registry client for schema management. This eases schema evolution, as applications that receive messages can get easy access to a writer schema that can be reconciled with their own reader schema. -For outbound messages, the `MessageConverter` will be activated if the content type of the channel is set to `application/*+avro`, e.g.: +For outbound messages, if the content type of the channel is set to `application/*+avro`, the `MessageConverter` is activated, as shown in the following example: [source,properties] ---- spring.cloud.stream.bindings.output.contentType=application/*+avro ---- -During the outbound conversion, the message converter will try to infer the schemas of the outbound messages based on their type and register them to a subject based on the payload type using the `SchemaRegistryClient`. -If an identical schema is already found, then a reference to it will be retrieved. -If not, the schema will be registered and a new version number will be provided. -The message will be sent with a `contentType` header using the scheme `application/[prefix].[subject].v[version]+avro`, where `prefix` is configurable and `subject` is deduced from the payload type. +During the outbound conversion, the message converter tries to infer the schema of each outbound messages (based on its type) and register it to a subject (based on the payload type) by using the `SchemaRegistryClient`. +If an identical schema is already found, then a reference to it is retrieved. +If not, the schema is registered, and a new version number is provided. +The message is sent with a `contentType` header by using the following scheme: `application/[prefix].[subject].v[version]+avro`, where `prefix` is configurable and `subject` is deduced from the payload type. -For example, a message of the type `User` may be sent as a binary payload with a content type of `application/vnd.user.v2+avro`, where `user` is the subject and `2` is the version number. +For example, a message of the type `User` might be sent as a binary payload with a content type of `application/vnd.user.v2+avro`, where `user` is the subject and `2` is the version number. -When receiving messages, the converter will infer the schema reference from the header of the incoming message and will try to retrieve it. The schema will be used as the writer schema in the deserialization process. +When receiving messages, the converter infers the schema reference from the header of the incoming message and tries to retrieve it. The schema is used as the writer schema in the deserialization process. +==== Avro Schema Registry Message Converter Properties -==== Avro Schema Registry Message Converter properties - -If you have enabled Avro based schema registry client by setting `spring.cloud.stream.bindings.output.contentType=application/*+avro` you can customize the behavior of the registration with the following properties. +If you have enabled Avro based schema registry client by setting `spring.cloud.stream.bindings.output.contentType=application/*+avro`, you can customize the behavior of the registration by setting the following properties. spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled:: Enable if you want the converter to use reflection to infer a Schema from a POJO. + -Default:: `false` +Default: `false` + -spring.cloud.stream.schema.avro.readerSchema:: Avro compares schema versions by looking at a writer schema (origin payload) and a reader schema (your application payload), check https://avro.apache.org/docs/1.7.6/spec.html[Avro] documentation for more information. If set, this overrides any lookups at the schema server and uses the local schema as the reader schema. -Default:: `null` +spring.cloud.stream.schema.avro.readerSchema:: Avro compares schema versions by looking at a writer schema (origin payload) and a reader schema (your application payload). See the https://avro.apache.org/docs/1.7.6/spec.html[Avro documentation] for more information. If set, this overrides any lookups at the schema server and uses the local schema as the reader schema. +Default: `null` + -spring.cloud.stream.schema.avro.schemaLocations:: Register any `.avsc` files listed in this property with the Schema Server. +spring.cloud.stream.schema.avro.schemaLocations:: Registers any `.avsc` files listed in this property with the Schema Server. + -Default:: `empty` +Default: `empty` + spring.cloud.stream.schema.avro.prefix:: The prefix to be used on the Content-Type header. + -Default:: `vnd` +Default: `vnd` === Schema Registration and Resolution -To better understand how Spring Cloud Stream registers and resolves new schemas, as well as its use of Avro schema comparison features, we will provide two separate subsections below: one for the registration, and one for the resolution of schemas. +To better understand how Spring Cloud Stream registers and resolves new schemas and its use of Avro schema comparison features, we provide two separate subsections: +* "`<>`" +* "`<>`" + +[[spring-cloud-stream-overview-schema-registration-process]] ==== Schema Registration Process (Serialization) The first part of the registration process is extracting a schema from the payload that is being sent over a channel. Avro types such as `SpecificRecord` or `GenericRecord` already contain a schema, which can be retrieved immediately from the instance. -In the case of POJOs a schema will be inferred if the property `spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled` is set to `true` (the default). +In the case of POJOs, a schema is inferred if the `spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled` property is set to `true` (the default). .Schema Writer Resolution Process image::schema_resolution.png[width=300,scaledwidth="75%",align="center"] -Ones a schema is obtained, the converter will then load its metadata (version) from the remote server. -First it queries a local cache, and if not found it then submits the data to the server that will reply with versioning information. -The converter will always cache the results to avoid the overhead of querying the Schema Server for every new message that needs to be serialized. +Ones a schema is obtained, the converter loads its metadata (version) from the remote server. +First, it queries a local cache. If no result is found, it submits the data to the server, which replies with versioning information. +The converter always caches the results to avoid the overhead of querying the Schema Server for every new message that needs to be serialized. .Schema Registration Process image::registration.png[width=300,scaledwidth="75%",align="center"] -With the schema version information, the converter sets the `contentType` header of the message to carry the version information such as `application/vnd.user.v1+avro` +With the schema version information, the converter sets the `contentType` header of the message to carry the version information -- for example: `application/vnd.user.v1+avro`. +[[spring-cloud-stream-overview-schema-resolution-process]] ==== Schema Resolution Process (Deserialization) -When reading messages that contain version information (i.e. a `contentType` header with a scheme like above), the converter will query the Schema server to fetch the *writer* schema of the message. -Ones it has found the correct schema of the incoming message, it then retrieves the reader schema and using Avro's schema resolution support reads it into the reader definition (setting defaults and missing properties). +When reading messages that contain version information (that is, a `contentType` header with a scheme like the one described under "`<>`"), the converter queries the Schema server to fetch the writer schema of the message. +Once it has found the correct schema of the incoming message, it retrieves the reader schema and, by using Avro's schema resolution support, reads it into the reader definition (setting defaults and any missing properties). .Schema Reading Resolution Process image::schema_reading.png[width=300,scaledwidth="75%",align="center"] -[NOTE] -==== -It's important to understand the difference between a writer schema (the application that wrote the message) and a reader schema (the receiving application). -Please take a moment to read https://avro.apache.org/docs/1.7.6/spec.html[the Avro terminology] and understand the process. -Spring Cloud Stream will always fetch the writer schema to determine how to read a message. If you want to get Avro's schema evolution support working you need to make sure that a readerSchema was properly set for your application. -==== - +NOTE: You should understand the difference between a writer schema (the application that wrote the message) and a reader schema (the receiving application). +We suggest taking a moment to read https://avro.apache.org/docs/1.7.6/spec.html[the Avro terminology] and understand the process. +Spring Cloud Stream always fetches the writer schema to determine how to read a message. +If you want to get Avro's schema evolution support working, you need to make sure that a `readerSchema` was properly set for your application. == Inter-Application Communication +Spring Cloud Stream enables communication between applications. Inter-application communication is a complex issue spanning several concerns, as described in the following topics: + +* "`<>`" +* "`<>`" +* "`<>`" + +[[spring-cloud-stream-overview-connecting-multiple-application-instances]] === Connecting Multiple Application Instances While Spring Cloud Stream makes it easy for individual Spring Boot applications to connect to messaging systems, the typical scenario for Spring Cloud Stream is the creation of multi-application pipelines, where microservice applications send data to each other. -You can achieve this scenario by correlating the input and output destinations of adjacent applications. +You can achieve this scenario by correlating the input and output destinations of "`adjacent`" applications. -Supposing that a design calls for the Time Source application to send data to the Log Sink application, you can use a common destination named `ticktock` for bindings within both applications. +Suppose a design calls for the Time Source application to send data to the Log Sink application. You could use a common destination named `ticktock` for bindings within both applications. -Time Source (that has the channel name `output`) will set the following property: +Time Source (that has the channel name `output`) would set the following property: ---- spring.cloud.stream.bindings.output.destination=ticktock ---- -Log Sink (that has the channel name `input`) will set the following property: +Log Sink (that has the channel name `input`) would set the following property: ---- spring.cloud.stream.bindings.input.destination=ticktock ---- +[[spring-cloud-stream-overview-instance-index-instance-count]] === Instance Index and Instance Count When scaling up Spring Cloud Stream applications, each instance can receive information about how many other instances of the same application exist and what its own instance index is. Spring Cloud Stream does this through the `spring.cloud.stream.instanceCount` and `spring.cloud.stream.instanceIndex` properties. -For example, if there are three instances of a HDFS sink application, all three instances will have `spring.cloud.stream.instanceCount` set to `3`, and the individual applications will have `spring.cloud.stream.instanceIndex` set to `0`, `1`, and `2`, respectively. +For example, if there are three instances of a HDFS sink application, all three instances have `spring.cloud.stream.instanceCount` set to `3`, and the individual applications have `spring.cloud.stream.instanceIndex` set to `0`, `1`, and `2`, respectively. -When Spring Cloud Stream applications are deployed via Spring Cloud Data Flow, these properties are configured automatically; when Spring Cloud Stream applications are launched independently, these properties must be set correctly. +When Spring Cloud Stream applications are deployed through Spring Cloud Data Flow, these properties are configured automatically; when Spring Cloud Stream applications are launched independently, these properties must be set correctly. By default, `spring.cloud.stream.instanceCount` is `1`, and `spring.cloud.stream.instanceIndex` is `0`. -In a scaled-up scenario, correct configuration of these two properties is important for addressing partitioning behavior (see below) in general, and the two properties are always required by certain binders (e.g., the Kafka binder) in order to ensure that data are split correctly across multiple consumer instances. +In a scaled-up scenario, correct configuration of these two properties is important for addressing partitioning behavior (see below) in general, and the two properties are always required by certain binders (for example, the Kafka binder) in order to ensure that data are split correctly across multiple consumer instances. +[[spring-cloud-stream-overview-partitioning]] === Partitioning +Partitioning in Spring Cloud Stream consists of two tasks: + +* "`<>`" +* "`<>`" + +[[spring-cloud-stream-overview-configuring-output-bindings-partitioning]] ==== Configuring Output Bindings for Partitioning -An output binding is configured to send partitioned data by setting one and only one of its `partitionKeyExpression` or `partitionKeyExtractorName` (see next paragraph) properties, as well as its `partitionCount` property. +You can configure an output binding to send partitioned data by setting one and only one of its `partitionKeyExpression` or `partitionKeyExtractorName` properties, as well as its `partitionCount` property. For example, the following is a valid and typical configuration: @@ -2127,14 +2108,15 @@ spring.cloud.stream.bindings.output.producer.partitionKeyExpression=payload.id spring.cloud.stream.bindings.output.producer.partitionCount=5 ---- -Based on the above example configuration, data will be sent to the target partition using the following logic. +Based on that example configuration, data is sent to the target partition by using the following logic. A partition key's value is calculated for each message sent to a partitioned output channel based on the `partitionKeyExpression`. -The `partitionKeyExpression` is a SpEL expression which is evaluated against the outbound message for extracting the partitioning key. +The `partitionKeyExpression` is a SpEL expression that is evaluated against the outbound message for extracting the partitioning key. +If a SpEL expression is not sufficient for your needs, you can instead calculate the partition key value by providing an implementation of `org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy` and configuring it as a bean (by using the `@Bean` annotation). +If you have more then one bean of type `org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy` available in the Application Context, you can further filter it by specifying its name with the `partitionKeyExtractorName` property, as shown in the following example: -If a SpEL expression is not sufficient for your needs, you can instead calculate the partition key value by providing implementation of `org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy` and configuring it as a bean (i.e., `@Bean`). In the event you have more then one bean of type `org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy` available in the Application Context you can further filter it by specifying its name via `partitionKeyExtractorName` property: - +[source] ---- --spring.cloud.stream.bindings.output.producer.partitionKeyExtractorName=customPartitionKeyExtractor --spring.cloud.stream.bindings.output.producer.partitionCount=5 @@ -2145,12 +2127,15 @@ public CustomPartitionKeyExtractorClass customPartitionKeyExtractor() { } ---- -NOTE: In previous versions of Spring Cloud Stream you could specify the implementation of `org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy` as `spring.cloud.stream.bindings.output.producer.partitionKeyExtractorClass` property. Since version 2.0 this property is deprecated and support for it will be removed in a future version. +NOTE: In previous versions of Spring Cloud Stream, you could specify the implementation of `org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy` by setting the `spring.cloud.stream.bindings.output.producer.partitionKeyExtractorClass` property. +Since version 2.0, this property is deprecated, and support for it will be removed in a future version. -Ones the message key is calculated, the partition selection process will determine the target partition as a value between `0` and `partitionCount - 1`. -The default calculation, applicable in most scenarios, is based on the formula `key.hashCode() % partitionCount`. -This can be customized on the binding, either by setting a SpEL expression to be evaluated against the 'key' (via the `partitionSelectorExpression` property) or by configuring an implementation of `org.springframework.cloud.stream.binder.PartitionSelectorStrategy` as a bean (i.e., @Bean). And similarly to the `PartitionKeyExtractorStrategy` you can further filter it using `spring.cloud.stream.bindings.output.producer.partitionSelectorName` property in the event there are more then one bean of this type is available in the Application Context. +Once the message key is calculated, the partition selection process determines the target partition as a value between `0` and `partitionCount - 1`. +The default calculation, applicable in most scenarios, is based on the following formula: `key.hashCode() % partitionCount`. +This can be customized on the binding, either by setting a SpEL expression to be evaluated against the 'key' (through the `partitionSelectorExpression` property) or by configuring an implementation of `org.springframework.cloud.stream.binder.PartitionSelectorStrategy` as a bean (by using the @Bean annotation). +Similar to the `PartitionKeyExtractorStrategy`, you can further filter it by using the `spring.cloud.stream.bindings.output.producer.partitionSelectorName` property when more than one bean of this type is available in the Application Context, as shown in the following example: +[source] ---- --spring.cloud.stream.bindings.output.producer.partitionSelectorName=customPartitionSelector . . . @@ -2160,12 +2145,13 @@ public CustomPartitionSelectorClass customPartitionSelector() { } ---- -NOTE: In previous versions of Spring Cloud Stream you could specify the implementation of `org.springframework.cloud.stream.binder.PartitionSelectorStrategy` as `spring.cloud.stream.bindings.output.producer.partitionSelectorClass` property. Since version 2.0 this property is deprecated and support for it will be removed in a future version. +NOTE: In previous versions of Spring Cloud Stream you could specify the implementation of `org.springframework.cloud.stream.binder.PartitionSelectorStrategy` by setting the `spring.cloud.stream.bindings.output.producer.partitionSelectorClass` property. +Since version 2.0, this property is deprecated and support for it will be removed in a future version. +[[spring-cloud-stream-overview-configuring-input-bindings-partitioning]] +==== Configuring Input Bindings for Partitioning -===== Configuring Input Bindings for Partitioning - -An input binding (with the channel name `input`) is configured to receive partitioned data by setting its `partitioned` property, as well as the `instanceIndex` and `instanceCount` properties on the application itself, as in the following example: +An input binding (with the channel name `input`) is configured to receive partitioned data by setting its `partitioned` property, as well as the `instanceIndex` and `instanceCount` properties on the application itself, as shown in the following example: ---- spring.cloud.stream.bindings.input.consumer.partitioned=true @@ -2173,21 +2159,23 @@ spring.cloud.stream.instanceIndex=3 spring.cloud.stream.instanceCount=5 ---- -The `instanceCount` value represents the total number of application instances between which the data need to be partitioned, and the `instanceIndex` must be a unique value across the multiple instances, between `0` and `instanceCount - 1`. +The `instanceCount` value represents the total number of application instances between which the data should be partitioned. +The `instanceIndex` must be a unique value across the multiple instances, with a value between `0` and `instanceCount - 1`. The instance index helps each application instance to identify the unique partition(s) from which it receives data. -It is required by binders using technology that doesn't support partitioning natively, for example, with RabbitMQ, there is a queue for each partition, with the queue name containing the instance index. -With Kafka, if `autoRebalanceEnabled` is `true` (default), Kafka will take care of distributing partitions across instances and these properties are not required. -If `autoRebalanceEnabled` is set to false, the `instanceCount` and `instanceIndex` are used by the binder to determine which partition(s) the instance will subscribe to (you must have at least as many partitions as there are instances). -The binder will allocate the partitions instead of Kafka. +It is required by binders using technology that does not support partitioning natively. +For example, with RabbitMQ, there is a queue for each partition, with the queue name containing the instance index. +With Kafka, if `autoRebalanceEnabled` is `true` (default), Kafka takes care of distributing partitions across instances, and these properties are not required. +If `autoRebalanceEnabled` is set to false, the `instanceCount` and `instanceIndex` are used by the binder to determine which partition(s) the instance subscribes to (you must have at least as many partitions as there are instances). +The binder allocates the partitions instead of Kafka. This might be useful if you want messages for a particular partition to always go to the same instance. -When a binder configuration that requires them, it is important to set both values correctly in order to ensure that all of the data is consumed and that the application instances receive mutually exclusive datasets. +When a binder configuration requires them, it is important to set both values correctly in order to ensure that all of the data is consumed and that the application instances receive mutually exclusive datasets. -While a scenario which using multiple instances for partitioned data processing may be complex to set up in a standalone case, Spring Cloud Dataflow can simplify the process significantly by populating both the input and output values correctly as well as relying on the runtime infrastructure to provide information about the instance index and instance count. +While a scenario in which using multiple instances for partitioned data processing may be complex to set up in a standalone case, Spring Cloud Dataflow can simplify the process significantly by populating both the input and output values correctly and by letting you rely on the runtime infrastructure to provide information about the instance index and instance count. == Testing Spring Cloud Stream provides support for testing your microservice applications without connecting to a messaging system. -You can do that by using the `TestSupportBinder` provided by the `spring-cloud-stream-test-support` library, which can be added as a test dependency to the application: +You can do that by using the `TestSupportBinder` provided by the `spring-cloud-stream-test-support` library, which can be added as a test dependency to the application, as shown in the following example: [source,xml] ---- @@ -2198,19 +2186,16 @@ You can do that by using the `TestSupportBinder` provided by the `spring-cloud-s ---- -[NOTE] -==== -The `TestSupportBinder` uses the Spring Boot autoconfiguration mechanism to supersede the other binders found on the classpath. -Therefore, when adding a binder as a dependency, make sure that the `test` scope is being used. -==== +NOTE: The `TestSupportBinder` uses the Spring Boot autoconfiguration mechanism to supersede the other binders found on the classpath. +Therefore, when adding a binder as a dependency, you must make sure that the `test` scope is being used. -The `TestSupportBinder` allows users to interact with the bound channels and inspect what messages are sent and received by the application +The `TestSupportBinder` lets you interact with the bound channels and inspect any messages sent and received by the application. For outbound message channels, the `TestSupportBinder` registers a single subscriber and retains the messages emitted by the application in a `MessageCollector`. They can be retrieved during tests and have assertions made against them. -The user can also send messages to inbound message channels, so that the consumer application can consume the messages. -The following example shows how to test both input and output channels on a processor. +You can also send messages to inbound message channels so that the consumer application can consume the messages. +The following example shows how to test both input and output channels on a processor: [source,java] ---- @@ -2249,16 +2234,16 @@ public class ExampleTest { } ---- -In the example above, we are creating an application that has an input and an output channel, bound through the `Processor` interface. -The bound interface is injected into the test so we can have access to both channels. -We are sending a message on the input channel and we are using the `MessageCollector` provided by Spring Cloud Stream's test support to capture the message has been sent to the output channel as a result. -Ones we have received the message, we can validate that the component functions correctly. +In the preceding example, we create an application that has an input channel and an output channel, both bound through the `Processor` interface. +The bound interface is injected into the test so that we can have access to both channels. +We send a message on the input channel, and we use the `MessageCollector` provided by Spring Cloud Stream's test support to capture that the message has been sent to the output channel as a result. +Once we have received the message, we can validate that the component functions correctly. -=== Disabling the test binder autoconfiguration +=== Disabling the Test Binder Autoconfiguration The intent behind the test binder superseding all the other binders on the classpath is to make it easy to test your applications without making changes to your production dependencies. -In some cases (e.g. integration tests) it is useful to use the actual production binders instead, and that requires disabling the test binder autoconfiguration. -In order to do so, you can exclude the `org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration` class using one of the Spring Boot autoconfiguration exclusion mechanisms, as in the following example. +In some cases (for example, integration tests) it is useful to use the actual production binders instead, and that requires disabling the test binder autoconfiguration. +To do so, you can exclude the `org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration` class by using one of the Spring Boot autoconfiguration exclusion mechanisms, as shown in the following example: [source,java] ---- @@ -2273,72 +2258,67 @@ In order to do so, you can exclude the `org.springframework.cloud.stream.test.bi } ---- -When autoconfiguration is disabled, the test binder is available on the classpath, and its `defaultCandidate` property is set to `false`, so that it does not interfere with the regular user configuration. It can be referenced under the name `test` e.g.: +When autoconfiguration is disabled, the test binder is available on the classpath, and its `defaultCandidate` property is set to `false` so that it does not interfere with the regular user configuration. It can be referenced under the name, `test`, as shown in the following example: ----- -spring.cloud.stream.defaultBinder=test ----- +`spring.cloud.stream.defaultBinder=test` == Health Indicator Spring Cloud Stream provides a health indicator for binders. It is registered under the name of `binders` and can be enabled or disabled by setting the `management.health.binders.enabled` property. +[[spring-cloud-stream-overview-metrics-emitter]] == Metrics Emitter -Spring Cloud Stream provides a module called `spring-cloud-stream-metrics` that can be used to emit any available metric from https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-metrics.html[Spring Boot metrics endpoint] to a named channel. -This module allow operators to collect metrics from stream applications without relying on polling their endpoints. +Spring Cloud Stream provides a module called `spring-cloud-stream-metrics` that can be used to emit any available metric from the https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-metrics.html[Spring Boot metrics endpoint] to a named channel. +This module lets operators collect metrics from stream applications without relying on polling their endpoints. -The module is activated when you set the destination name for metrics binding, e.g. `spring.cloud.stream.bindings.applicationMetrics.destination=`. +The module is activated when you set the destination name for metrics binding -- for example, `spring.cloud.stream.bindings.applicationMetrics.destination=`. `applicationMetrics` can be configured in a similar fashion to any other producer binding. The default `contentType` setting of `applicationMetrics` is `application/json`. The following properties can be used for customizing the emission of metrics: spring.cloud.stream.metrics.key:: - The name of the metric being emitted. Should be an unique value per application. +The name of the metric being emitted. Should be a unique value per application. + -Default:: `${spring.application.name:${vcap.application.name:${spring.config.name:application}}}` +Default: `${spring.application.name:${vcap.application.name:${spring.config.name:application}}}` + spring.cloud.stream.metrics.prefix:: - Prefix string to be prepended to the metrics key. +Prefix string to be prepended to the metrics key. + Default: `` + spring.cloud.stream.metrics.properties:: - Just like the `includes` option, it allows white listing application properties that will be added to the metrics payload +As with the `includes` option, it allows white listing application properties that are added to the metrics payload + Default: null. - A detailed overview of the metrics export process can be found in the https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-metrics.html#production-ready-metric-writers[Spring Boot reference documentation]. -Spring Cloud Stream provides a metric exporter named `application` that can be configured via regular https://github.com/spring-projects/spring-boot/blob/1.5.x/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/TriggerProperties.java[Spring Boot metrics configuration properties]. +Spring Cloud Stream provides a metric exporter named `application` that can be configured through regular https://github.com/spring-projects/spring-boot/blob/1.5.x/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/TriggerProperties.java[Spring Boot metrics configuration properties]. -The exporter can be configured either by using the global Spring Boot configuration settings for exporters, or by using exporter-specific properties. -For using the global configuration settings, the properties should be prefixed by `spring.metric.export` (e.g. `spring.metric.export.includes=integration+++**+++`). -These configuration options will apply to all exporters (unless they have been configured differently). -Alternatively, if it is intended to use configuration settings that are different from the other exporters (e.g. for restricting the number of metrics published), the Spring Cloud Stream provided metrics exporter can be configured using the prefix `spring.metrics.export.triggers.application` (e.g. `spring.metrics.export.triggers.application.includes=integration+++**+++`). +The exporter can be configured either by using the global Spring Boot configuration settings for exporters or by using exporter-specific properties. +For using the global configuration settings, the properties should be prefixed by `spring.metric.export` (for example: `spring.metric.export.includes=integration+++**+++`). +These configuration options apply to all exporters (unless they have been configured differently). +Alternatively, if you intend to use configuration settings that differ from the other exporters (for example, for restricting the number of metrics published), the Spring Cloud Stream-provided metrics exporter can be configured by using the prefix `spring.metrics.export.triggers.application` (for example, `spring.metrics.export.triggers.application.includes=integration+++**+++`). -[NOTE] -==== -Due to Spring Boot's https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-relaxed-binding[relaxed binding] the value of a property being included can be slightly different than the original value. +NOTE: Due to Spring Boot's https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-relaxed-binding[relaxed binding], the value of a included property can be slightly different than the original value. -As a rule of thumb, the metric exporter will attempt to normalize all the properties in a consistent format using the dot notation (e.g. `JAVA_HOME` becomes `java.home`). +As a rule of thumb, the metric exporter tries to normalize all the properties in a consistent format by using the dot notation (for example, `JAVA_HOME` becomes `java.home`). -The goal of normalization is to make downstream consumers of those metrics capable of receiving property names consistently, regardless of how they are set on the monitored application (`--spring.application.name` or `SPRING_APPLICATION_NAME` would always yield `spring.application.name`). +The goal of normalization is to make downstream consumers of those metrics capable of receiving property names consistently, regardless of how they are set on the monitored application (for example, `--spring.application.name` or `SPRING_APPLICATION_NAME` always yields `spring.application.name`). -==== +Consider the following command: -Below is a sample of the data published to the channel in JSON format by the following command: - -``` +[source,bash] +---- java -jar time-source.jar \ --spring.cloud.stream.bindings.applicationMetrics.destination=someMetrics \ --spring.cloud.stream.metrics.properties=spring.application** \ --spring.metrics.export.includes=integration.channel.input**,integration.channel.output** -``` +---- -The resulting JSON is: +The following example shows data published to the channel in JSON format by the preceding command: [source,javascript] ---- @@ -2411,10 +2391,10 @@ The resulting JSON is: == Samples -For Spring Cloud Stream samples, please refer to the https://github.com/spring-cloud/spring-cloud-stream-samples[spring-cloud-stream-samples] repository on GitHub. +For Spring Cloud Stream samples, see the https://github.com/spring-cloud/spring-cloud-stream-samples[spring-cloud-stream-samples] repository on GitHub. -=== Deploying Stream applications on CloudFoundry +=== Deploying Stream Applications on CloudFoundry -On CloudFoundry services are usually exposed via a special environment variable called https://docs.cloudfoundry.org/devguide/deploy-apps/environment-variable.html#VCAP-SERVICES[VCAP_SERVICES]. +On CloudFoundry, services are usually exposed through a special environment variable called https://docs.cloudfoundry.org/devguide/deploy-apps/environment-variable.html#VCAP-SERVICES[VCAP_SERVICES]. -When configuring your binder connections, you can use the values from an environment variable as explained on the http://docs.spring.io/spring-cloud-dataflow-server-cloudfoundry/docs/current-SNAPSHOT/reference/htmlsingle/#getting-started-ups[dataflow cloudfoundry server] docs. +When configuring your binder connections, you can use the values from an environment variable as explained on the http://docs.spring.io/spring-cloud-dataflow-server-cloudfoundry/docs/current-SNAPSHOT/reference/htmlsingle/#getting-started-ups[dataflow Cloud Foundry Server] docs.