Spring cloud: stream message driven

Keywords: Java kafka RabbitMQ Distribution Spring Cloud

springcloud

Stream message driven

Message driven overview

What is Spring Cloud Stream: 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 automated configuration for some vendors' message oriented middleware products,
    • Three core concepts of publish subscribe, consumption group and partition are cited.
      Currently, only RabbitMQ and Kafka are supported.

Shield the differences of underlying message middleware, reduce the switching version, and unify the programming model of message

Official website: https://spring.io/projects/spring-cloud-stream#overview

Chinese Instruction Manual: https://m.wang1314.com/doc/webapp/topic/20971999.html

design idea

Standard mq

  • Producers / consumers transmit information through Message media: Message
  • Messages must go through a specific channel message channel
  • How are messages in the message channel consumed? Who is responsible for receiving and processing: the sub interface SubscribableChannel of the message channel MessageChannel is subscribed by the MessageHandler message processor

Why Cloud Stream

The differences of these middleware lead to some problems in our actual project development. If we use one of the two message queues and the business requirements behind it, 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 decoupling method.

Why can stream unify the underlying differences?

Without the concept of binder, when our SpringBoot application wants to directly interact with message middleware, there will be great differences in the implementation details due to the different original intentions of each message middleware

  • By defining the binder as the middle layer, the isolation between application and message middleware details is perfectly realized.
  • By exposing the unified Channel channel to the application, the application does not need to consider a variety of different message middleware implementations.

By default, the RabbitMQ binder implementation maps each target to TopicExchange. For each consumer group.

Binder binder

Without the concept of binder, when our SpringBoot application wants to directly interact with the message middleware, there will be great differences in the implementation details due to the different original intentions of each message middleware. By defining the binder as the middle layer, the isolation between the application and the message middleware details is perfectly realized.

The further encapsulation of message oriented middleware by Stream can make the code layer insensitive to the middleware, and even dynamically switch the middleware (rabbitmq to kafka), which makes the development of micro services highly decoupled, and services can pay more attention to their own business processes

By defining Binder as the middle layer, the isolation between application and message middleware details is realized.

  • INPUT corresponds to the consumer
  • OUTPUT corresponds to the producer

The message communication mode in Stream follows the publish subscribe mode

Broadcast Topic

  • RabbitMQ is Exchange
  • In kafka, it is Topic

Spring Cloud Stream standard process routine

  • Binder is convenient to connect middleware and shield differences
  • Channel channel is an abstraction of Queue. In the message communication system, it is the medium for storage and forwarding. The Queue is configured through the channel
  • Source and Sink can be simply understood as the reference object is the Spring Cloud Stream itself. Publishing messages from the Stream is output, and receiving messages is input

Coding API and common annotations

Case description

RabbitMQ environment is OK

Three new sub modules are built in the project

  • Cloud stream rabbitmq provider 8801, as a producer, sends messages
  • Cloud stream rabbitmq consumer 8802 as a message receiving module
  • Cloud stream rabbitmq consumer 8803 as a message receiving module

Message driven producer

cloud-stream-rabbitmq-provider8801

  <dependencies>


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

        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

     
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>



    </dependencies>


configuration file

server:
  port: 8801

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: localhost
                port: 5672
                username: guest
                password: guest
      bindings: # Integration of services
        output: # This name is the name of a channel
          destination: studyExchange # 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

eureka:
  client: # Configure Eureka registration on the client
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # Set the heartbeat interval (30 seconds by default)
    lease-expiration-duration-in-seconds: 5 # If the interval of 5 seconds is exceeded now (the default is 90 seconds)
    instance-id: send-8801.com  # Displays the host name in the information list
    prefer-ip-address: true     # The access path becomes an IP address

Main startup class streamqmain8801

@SpringBootApplication
public class StreamMQMain8801 {
    public static void main(String[] args) {
        SpringApplication.run(StreamMQMain8801.class, args);
    }
}
 

Business class

Send message interface

public interface IMessageProvider
{
    public String send();
}
 
 

Send message interface implementation class

@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 serial = UUID.randomUUID().toString();
        output.send(MessageBuilder.withPayload(serial).build());
        System.out.println("*****serial: "+serial);
        return null;
    }
}
 

Controller

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

    @GetMapping(value = "/sendMessage")
    public String sendMessage()
    {
        return messageProvider.send();
    }

}

Test access: http://localhost:8801/sendMessage

See the serial number and port number of the control belt output

Message driven consumers

cloud-stream-rabbitmq-consumer8802

    <dependencies>


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

        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>


configuration file

server:
  port: 8802

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: localhost
                port: 5672
                username: guest
                password: guest
      bindings: # Integration of services
        input: # This name is the name of a channel
          destination: studyExchange # 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

eureka:
  client: # Configure Eureka registration on the client
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # Set the heartbeat interval (30 seconds by default)
    lease-expiration-duration-in-seconds: 5 # If the interval of 5 seconds is exceeded now (the default is 90 seconds)
    instance-id: receive-8802.com  # Displays the host name in the information list
    prefer-ip-address: true     # The access path becomes an IP address
 

Main startup class streamqmain8802

@SpringBootApplication
public class StreamMQMain8802 {

    public static void main(String[] args) {
        SpringApplication.run(StreamMQMain8802.class, args);
    }

}
 

Business class

@Component
@EnableBinding(Sink.class)
public class ReceiveMessageListenerController {
    @Value("${server.port}")
    private String serverPort;

    @StreamListener(Sink.INPUT)
    public void input(Message<String> message) {
        System.out.println("Consumer 1, accept:"+message.getPayload()+"\t port:"+serverPort);
    }

}

Test 8801 sends 8802 receives messages

http://localhost:8801/sendMessage

At this time, 8802 will display the received message

According to 8802, clone will run 8803

Two problems after operation

There is the problem of repeated consumption

At present, 8802 / 8803 have been received at the same time, and there is a problem of repeated consumption

How to solve it?

The same group of consumers is a competitive relationship, and only one can consume

principle

When microservice applications are placed in the same group, it can ensure that messages will only be consumed once by one of them. Different groups can be consumed. There will be competition in the same group, and only one of them can be consumed.

The 8802 / 8803 implements polling packets. Only one consumer at a time. Messages sent by the 8801 module can only be received by one of 8802 or 8803, so as to avoid repeated consumption

Message persistence problem

Through the above, we have solved the problem of repeated consumption, and then look at persistence

  • The group of 8803: atguigua is not removed
  • The group of 8803: atguigua is not removed

8801 sends 4 messages to rabbitmq first

  • Start 8802 first, there is no grouping attribute configuration, and there is no message printed in the background
  • Start 8803 first, with grouping attribute configuration, and the message on MQ is printed in the background

Summary: consumers with groups can read the information of the group after startup

Posted by grail on Fri, 10 Sep 2021 18:05:11 -0700