×
Community Blog An Introduction to the Spring Cloud Stream System and Its Principles

An Introduction to the Spring Cloud Stream System and Its Principles

This article is an introduction and overview of the Spring Cloud Stream System and its principles.

By Luoye

Spring Cloud Stream is used to build highly scalable event-driven microservices within the Spring Cloud system to simplify the development of messages in Spring Cloud applications.

Spring Cloud Stream (SCS) has a lot of content. It also has many external dependencies. To get familiar with SCS, you must learn about Spring Messaging and Spring Integration first. This article will focus on three topics:

  • What Is Spring Messaging?
  • What Is Spring Integration?
  • What Is the SCS System, and How Does It Work?

Spring Messaging

Spring Messaging is a module in Spring Framework, which is a programming model of unified messaging.

  • For example, a Message has a message body called Payload and a Header:

1

package org.springframework.messaging;
public interface Message<T> {
    T getPayload();
    MessageHeaders getHeaders();
}
  • MessageChannel receives messages. You can call the send method to send messages to this message channel:

2

@FunctionalInterface
public interface MessageChannel {
    long INDEFINITE_TIMEOUT = -1;
    default boolean send(Message<?> message) {

         return send(message, INDEFINITE_TIMEOUT);

     }
     boolean send(Message<?> message, long timeout);
}

How Are Messages in the Message Channel Consumed?

  • This is realized using the SubscribableChannel, which can be subscribed to by subinterface of a message channel. The messages are subscribed to by MessageHandler:
public interface SubscribableChannel extends MessageChannel {
    boolean subscribe(MessageHandler handler);
    boolean unsubscribe(MessageHandler handler);
}
  • The messages are finally consumed and processed by MessageHandler:
@FunctionalInterface
public interface MessageHandler {
    void handleMessage(Message<?> message) throws MessagingException;
}

Spring Messaging Also Provides Some Other Internal Features Derived from the Messaging Model:

  • Message receiving argument processing and return value processing: HandlerMethodArgumentResolver, a message receiving argument handler, is used in conjunction with annotations, such as @Header and @Payload. HandlerMethodReturnValueHandler, the return value handler, is used in conjunction with the annotation @SendTo after a message is received.
  • Message Body Content Converter: MessageConverter
  • Unified and Abstract Message Sending Template: AbstractMessageSendingTemplate
  • Message Channel Interceptor: ChannelInterceptor

Spring Integration

Spring Integration extends the Spring programming model to support the Enterprise Integration Patterns. Spring Integration is an extension of Spring Messaging.

It involves many new concepts, including MessageRoute, MessageDispatcher, Filter, Transformer, Aggregator, and Splitter. It also provides the implementation of MessageChannel and MessageHandler, including DirectChannel, ExecutorChannel, PublishSubscribeChannel, MessageFilter, ServiceActivatingHandler, and MethodInvokingSplitter.

We Introduce Several Message Processing Methods Below:

  • Message Splitting:

3

  • Message Aggregation:

4

  • Message Filtering:

5

  • Message Dispatching:

6

Let’s Try Spring Integration with a Simple Example:

This code snippet and its explanation are listed below:

SubscribableChannel messageChannel =new DirectChannel(); // 1

messageChannel.subscribe(msg-> { // 2
 System.out.println("receive: " +msg.getPayload());
});

messageChannel.send(MessageBuilder.withPayload("msgfrom alibaba").build()); // 3
  • Build a subscribable message channel, messageChannel
  • Use MessageHandler to consume messages in this message channel
  • Send a message to this message channel. The message is eventually consumed by the MessageHandler in the message channel.
  • At last, the console prints: receive: msg from alibaba

DirectChannel has an internal UnicastingDispatcher, which dispatches messages to the corresponding MessageChannel. UnicastingDispatcher is a unicasting dispatcher, as you can tell from its name. Only one message channel can be selected. How does it choose the message channel? An internal LoadBalancingStrategy is provided. UnicastingDispatcher polls for the channel by default. Scaling up is supported.

Let’s modify the code above to use multiple MessageHandlers to process messages:

SubscribableChannel messageChannel = new DirectChannel();

messageChannel.subscribe(msg -> {
     System.out.println("receive1: " + msg.getPayload());
});

messageChannel.subscribe(msg -> {
     System.out.println("receive2: " + msg.getPayload());
});

messageChannel.send(MessageBuilder.withPayload("msg from alibaba").build());
messageChannel.send(MessageBuilder.withPayload("msg from alibaba").build());

The internal message dispatcher of DirectChannel is UnicastingDispatcher with a polling load balancing strategy. Therefore, two consumption activities here correspond to the two MessageHandlers. The console prints:

receive1: msg from alibaba
receive2: msg from alibaba

After introducing the UnicastingDispatcher, let’s take a look at the BroadcastingDispatcher, which is used by the PublishSubscribeChannel message channel. BroadcastingDispatcher dispatches messages to all MessageHandlers:

SubscribableChannel messageChannel = new PublishSubscribeChannel();

messageChannel.subscribe(msg -> {
     System.out.println("receive1: " + msg.getPayload());
});

messageChannel.subscribe(msg -> {
     System.out.println("receive2: " + msg.getPayload());
});

messageChannel.send(MessageBuilder.withPayload("msg from alibaba").build());
messageChannel.send(MessageBuilder.withPayload("msg from alibaba").build());

Spring Cloud Stream

Relationships between SCS and Other Modules Are Listed Below:

  • SCS is encapsulated on the basis of Spring Integration and introduces some concepts, such as Binder, Binding, @EnableBinding, and @StreamListener.
  • SCS is integrated with Spring Boot Actuator and provides the /bindings and /channelsendpoint.
  • SCS is integrated with Spring Boot Externalized Configuration and provides external configuration classes, such as BindingProperties and BinderProperties.
  • SCS enhances the processing logic in the cases of message sending failure and consumption failure.
  • SCS is an enhancement to Spring Integration. It is integrated with the Spring Boot system and is the foundation of Spring Cloud Bus. It shields implementation details of the underlying message middleware in the hope that a unified set of APIs can be used to send and consume messages. The implementation details of the underlying message middleware are realized by the Binder of each message middleware.

Binder is the component that binds an internal message middleware to an external one. It provides two Binding methods, bindConsumer and bindProducer, which are used to bind the consumer and the producer, respectively. The currently available implementations are Rabbit Binder and Kafka Binder, and Spring Cloud Alibaba has already implemented RocketMQ Binder.

7

From this figure, we can tell that Binding is the bridge that connects the application and the message middleware and is used to produce and consume messages. Let’s take a look at a simple example about using the RocketMQ Binder to analyze its underlying processing logic:

Enable the classes and send a message:

@SpringBootApplication
@EnableBinding({ Source.class, Sink.class }) // 1
public class SendAndReceiveApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(SendAndReceiveApplication.class, args);
    }
 
       @Bean // 2
    public CustomRunner customRunner() {
        return new CustomRunner();
    }

    public static class CustomRunner implements CommandLineRunner {

        @Autowired
        private Source source;

        @Override
        public void run(String... args) throws Exception {
            int count = 5;
            for (int index = 1; index <= count; index++) {
                source.output().send(MessageBuilder.withPayload("msg-" + index).build()); // 3
            }
        }
    }
}

Receive the message:

@Service
public class StreamListenerReceiveService {

    @StreamListener(Sink.INPUT) // 4
    public void receiveByStreamListener1(String receiveMsg) {
        System.out.println("receiveByStreamListener: " + receiveMsg);
    }

}

This snippet is very straightforward, and it does not involve any code that relates to RocketMQ. The message is sent and received based on the SCS system. If you want to switch to RabbitMQ or Kafka, you only need to modify the configuration file without any modification to the code.

Let’s analyze how this snippet works:

1.  SCS provides two interface attributes for the @EnableBinding annotation: Source and Sink. SCS builds the BindableProxyFactory internally based on the Source and Sink attributes. The MessageChannel returned by the corresponding output and input methods is DirectChannel. The values of the corresponding annotations modified by the output and input methods are the names of bindings in the configuration file.

public interface Source {
    String OUTPUT = "output";
    @Output(Source.OUTPUT)
    MessageChannel output();
}
public interface Sink {
    String INPUT = "input";
    @Input(Sink.INPUT)
    SubscribableChannel input();
}

The names of bindings in the configuration file are the values of annotations of the Source and Sink interfaces corresponding to the output and input methods:

spring.cloud.stream.bindings.output.destination=test-topic
spring.cloud.stream.bindings.output.content-type=text/plain
spring.cloud.stream.rocketmq.bindings.output.producer.group=demo-group

spring.cloud.stream.bindings.input.destination=test-topic
spring.cloud.stream.bindings.input.content-type=text/plain
spring.cloud.stream.bindings.input.group=test-group1

2.  Build the CommandLineRunner. The run method of CustomRunner will be executed upon startup of the application.

3.  Call the output method of the Source interface to obtain the DirectChannel and send a message to this message channel. The snippet is identical to the one in the Spring Integration section.

  • After a message is sent by the output method of the Source interface to the DirectChannel message channel, the message is processed by the MessageHandler called AbstractMessageChannelBinder#SendingHandler. Then, it delegates the message to the MessageHandler created by AbstractMessageChannelBinder#createProducerMessageHandler for processing. (This method is implemented by a different message middleware.)
  • The MessageHandler returned by the AbstractMessageChannelBinder#createProducerMessageHandler method of the message middleware converts the Spring Message to the corresponding Message model and sends the model to the broker of the middleware.

4.  Use the @StreamListener annotation to subscribe to the message. Note: The corresponding value of Sink.input in the annotation is "input." The configuration is subject to the value whose corresponding binding name is input:

  • The AbstractMessageChannelBinder#createConsumerEndpoint method implemented by a message middleware uses the Consumer to subscribe to the message and then converts the Message model of the message middleware to a Spring Message.
  • After the conversion, the Spring Message is sent to a message channel named input.
  • The StreamListenerMessageHandler corresponding to the @StreamListener annotation subscribes to the message channel whose name is input and consumes the message.

The text description is a little verbose. The following diagram summarizes the entire process. (The yellow-highlighted part covers the Binder implementation of the message middleware and the basic subscription and publication features of MQ.)

8

Let’s Take a Look at an SCS Snippet for Message Processing

@StreamListener(value = Sink.INPUT, condition = "headers['index']=='1'")
public void receiveByHeader(Message msg) {
     System.out.println("receive by headers['index']=='1': " + msg);
}

@StreamListener(value = Sink.INPUT, condition = "headers['index']=='9999'")
public void receivePerson(@Payload Person person) {
     System.out.println("receive Person: " + person);
}

@StreamListener(value = Sink.INPUT)
public void receiveAllMsg(String msg) {
     System.out.println("receive allMsg by StreamListener. content: " + msg);
}

@StreamListener(value = Sink.INPUT)
public void receiveHeaderAndMsg(@Header("index") String index, Message msg) {
     System.out.println("receive by HeaderAndMsg by StreamListener. content: " + msg);
}

You may find this snippet similar to the one that is used in the Spring MVC Controller to receive requests. Their architectures are similar. In Spring MVC, handler classes for arguments and return values in the Controller are org.springframework.web.method.support.HandlerMethodArgumentResolver and org.springframework.web.method.support.HandlerMethodReturnValueHandler.

The handler classes for arguments and return values in Spring Messaging were also mentioned above, which are org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver and org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler.

Their class names and the internal method names are also the same.

Summary

9

This figure summarizes classes of the SCS system. For more examples about SCS and the RocketMQ Binder, please see the RocketMQ Binder Demos document. It describes message aggregation, splitting, filtering, message exception handling, message tags, SQL filtering, and synchronous and asynchronous consumption.

0 0 0
Share on

Alibaba Developer

207 posts | 33 followers

You may also like

Comments

Alibaba Developer

207 posts | 33 followers

Related Products

  • AlibabaMQ for Apache RocketMQ

    AlibabaMQ for Apache RocketMQ is a distributed message queue service that supports reliable message-based asynchronous communication among microservices, distributed systems, and serverless applications.

    Learn More
  • Link IoT Edge

    Link IoT Edge allows for the management of millions of edge nodes by extending the capabilities of the cloud, thus providing users with services at the nearest location.

    Learn More
  • Elastic High Performance Computing Solution

    High Performance Computing (HPC) and AI technology helps scientific research institutions to perform viral gene sequencing, conduct new drug research and development, and shorten the research and development cycle.

    Learn More
  • Starter Package

    High-performance virtual machines with data transfer plan, starting from $2.50 per month

    Learn More