Spring cloud version h Stream message driven explanation

Posted by adeenutza on Sun, 19 Dec 2021 21:00:06 +0100

1, SpringCloud Stream

In the previous article, we explained the function of config + bus to automatically refresh all service configurations. This article continues to explain the message driven Stream

Last article address: https://blog.csdn.net/qq_43692950/article/details/122025347

Officially, Spring Cloud Stream is a framework for building message driven microservices.

The application interacts with the binder object in Spring Cloud Stream through inputs or outputs.
We configure binding, and the binder object of Spring Cloud Stream is responsible for interacting with the message middleware. Therefore, we only need to figure out how to interact with Spring Cloud Stream to facilitate the use of message driven methods.

By using Spring Integration to connect the message broker middleware to realize message event driven.
Spring Cloud Stream provides personalized automatic configuration implementation for some vendors' message middleware products, citing the three core concepts of publish subscribe, consumption group and partition.

Currently, only RabbitMQ and Kafka are supported.

Why use stream

For example, we use RabbitMQ and Kafka. Due to the differences in the architecture of these two message middleware, such as RabbitMQ with exchange and Kafka with Topic and Partitions partitions, the differences of these middleware cause us some problems in our actual project development. If we use one of the two message queues, we will meet the business requirements later, I want to migrate to another message queue. At this time, it is undoubtedly disastrous. A lot of things have to be pushed down and redone because it is coupled with our system. At this time, springcloud Stream provides us with a way to decouple.

Annotations in stream

Our stream is based on RabbitMQ, so please ensure that RabbitMQ is installed before building.

2, Build message provider

Create a SpringBoot module to introduce the following dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>

Modification of configuration file:

server:
  port: 7071

spring:
  application:
    name: cloud-stream-provider
  cloud:
    stream:
      binders: # Configure the service information of rabbitmq to be bound here;
        defaultRabbit: # Represents the name of the definition, which is used for binding integration
          type: rabbit # Message component type
          environment: # Set the environment configuration related to rabbitmq
            spring:
              rabbitmq:
                host: 192.168.40.1
                port: 5672
                username: guest
                password: guest
      bindings: # Integration of services
        output: # This name is the name of a channel
          destination: msgExchange # Represents the Exchange name definition to use
          content-type: application/json # Set the message type, json this time, and "text/plain" for text
          binder: defaultRabbit # Set the specific settings of the message service to be bound

Create service provider service

Interface:

public interface IMessageProvider {
    public String send(String msg);
}

realization:

@EnableBinding(Source.class) //Defines the push pipeline for messages
public class MessageProviderImpl implements IMessageProvider {
    @Resource
    private MessageChannel output; // Message sending pipeline

    @Override
    public String send(String msg) {
        return output.send(MessageBuilder.withPayload(msg).build()) ? "Sent successfully" : "Sending failed!";
    }
}

Create test interface:

@RestController
public class SendMessageController
{
    @Resource
    private IMessageProvider messageProvider;

    @GetMapping("/sendMessage/{msg}")
    public ResponseTemplate sendMessage(@PathVariable String msg)
    {
        String result = messageProvider.send(msg);
        return ResSuccessTemplate.builder().data(result).build();
    }

}

Start the message provider service and view the RabbitMQ console:

The switch we wrote has been registered in RabbitMQ.

Let's continue to build message consumers.

3, Build message consumers

Create another SpringBoot module, and also introduce the following dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Modify configuration file:

server:
  port: 6062

spring:
  application:
    name: cloud-stream-consumer
  cloud:
    stream:
      binders: # Configure the service information of rabbitmq to be bound here;
        defaultRabbit: # Represents the name of the definition, which is used for binding integration
          type: rabbit # Message component type
          environment: # Set the environment configuration related to rabbitmq
            spring:
              rabbitmq:
                host: www.bixc.net
                port: 5672
                username: guest
                password: guest
      bindings: # Integration of services
        input: # This name is the name of a channel
          destination: msgExchange # Represents the Exchange name definition to use
          content-type: application/json # Set the message type. This time, it is an object json. If it is text, set "text/plain"
          binder: defaultRabbit # Set the specific settings of the message service to be bound

Note: the destination here must be the same as that of the provider to indicate that it is linked to the same switch.

Write message listening:

@Slf4j
@Component
@EnableBinding(Sink.class)
public class ReceiveMessageListenerController {

    @Value("${server.port}")
    private String serverPort;


    @StreamListener(Sink.INPUT)
    public void input(Message<String> message) {
        log.info("Received message: {} , current port: {} ", message.getPayload(), serverPort);
    }
}

Start the service consumer and start the test below:

Browser access: http://localhost:7071/sendMessage/ Ha ha ha


It has been sent successfully. Look at the print log on the consumer side:


Also received the message.

Now we modify the port of the lower consumer and start the same consumer again. The port is 6061.


Send the message again and see the consumer's request:


Both consumers have received the message. If only one consumer wants to receive the message, it only needs to be placed in the lower interface of the same group. If there is no group specified in the above configuration, a group will be automatically generated:

Now we add grouping and modify the configuration of the consumer side:

spring:
  application:
    name: cloud-stream-consumer
  cloud:
    stream:
      binders: # Configure the service information of rabbitmq to be bound here;
        defaultRabbit: # Represents the name of the definition, which is used for binding integration
          type: rabbit # Message component type
          environment: # Set the environment configuration related to rabbitmq
            spring:
              rabbitmq:
                host: www.bixc.net
                port: 5672
                username: guest
                password: guest
      bindings: # Integration of services
        input: # This name is the name of a channel
          destination: msgExchange # Represents the Exchange name definition to use
          content-type: application/json # Set the message type. This time, it is an object json. If it is text, set "text/plain"
          binder: defaultRabbit # Set the specific settings of the message service to be bound
          group: groupA

group: groupA specifies the group name. Let's look at the information in RabbitMQ:

Send the following message:

Only one consumer received the message.

Another note here is that if you do not configure a group, a group will be randomly assigned every time you start. If you suddenly interrupt the service after receiving a message, you will lose the message if you start it again, so you must specify the group information.


Love little buddy can pay attention to my personal WeChat official account and get more learning materials.

Topics: Java RabbitMQ Microservices Middleware