Spring cloud 2020 - spring cloud stream message driven (note)

Posted by christiank on Tue, 08 Mar 2022 03:52:09 +0100

Previous: SpringCloud 2020-SpringCloud config distributed configuration center (note)

1. Message driven overview

1.1 INTRODUCTION

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

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

https://docs.spring.io/spring-cloud-stream/docs/current/reference/html/

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

1.2 design idea

Standard MQ:

Why use Cloud Stream:


Why can stream unify the underlying differences:

Binder:


INPUT corresponds to the consumer and OUTPUT corresponds to the producer

1.3 Spring Cloud Stream standard process routine

1.4 coding API and common notes

2. Case description

3. Message driven producer

1. Create a new module cloud stream rabbitmq provider8801
2,pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020</artifactId>
        <groupId>com.liukai.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-stream-rabbitmq-provider8801</artifactId>

    <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.liukai.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>

</project>

3,yml

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 related environment configuration of 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 # Indicates 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,http://localhost:7002/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # Set the heartbeat interval (the default is 30 seconds)
    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

4. Main startup class streamqmain8801

package com.liukai.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

5. Business class: interface IMessageProvider

package com.liukai.springcloud.service;

/**
 * @author liukai
 * @version 1.0.0
 * @ClassName IMessageProvider.java
 * @Description TODO
 * @createTime 2021 23:14:00, March 25
 */
public interface IMessageProvider {

    public String send();

}

Interface implementation class MessageProviderImpl

package com.liukai.springcloud.service.impl;

import com.liukai.springcloud.service.IMessageProvider;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.MessageBuilder;

import javax.annotation.Resource;
import java.util.UUID;

/**
 * @author liukai
 * @version 1.0.0
 * @ClassName MessageProviderImpl.java
 * @Description TODO
 * @createTime 2021 23:15:00, March 25
 */
@EnableBinding(Source.class) //Defines the push pipeline for messages
public class MessageProviderImpl implements IMessageProvider {

    @Resource
    private MessageChannel output;

    @Override
    public String send() {
        String serial = UUID.randomUUID().toString();
        output.send(MessageBuilder.withPayload(serial).build());
        System.out.println("******serial:" + serial);
        return null;
    }
}

controller layer: SendMessageController

package com.liukai.springcloud.controller;

import com.liukai.springcloud.service.IMessageProvider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author liukai
 * @version 1.0.0
 * @ClassName SendMessageController.java
 * @Description TODO
 * @createTime 2021 23:00, March 25
 */
@RestController
public class SendMessageController {

    @Resource
    private IMessageProvider iMessageProvider;

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

}

6. Test: start 70017002, RABBITMQ
visit http://localhost:8801/sendMessage

RabbitMQ peaks

Console print data

4. Message driven consumers

1. Create a new module cloud stream rabbitmq consumer 8802
2,pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020</artifactId>
        <groupId>com.liukai.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-stream-rabbitmq-consumer8802</artifactId>

    <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.liukai.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>


</project>

3,yml

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 related environment configuration of 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 # Indicates 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,http://localhost:7002/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # Set the heartbeat interval (the default is 30 seconds)
    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

4. Main startup class

package com.liukai.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author liukai
 * @version 1.0.0
 * @ClassName StreamMQMain8802.java
 * @Description TODO
 * @createTime 2021 April 9, 2013 13:48:00
 */
@SpringBootApplication
public class StreamMQMain8802 {

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

}

5. Business layer: ReceiveMessageListenerController

package com.liukai.springcloud.controller;

import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.Message;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.stereotype.Component;
/**
 * @author liukai
 * @version 1.0.0
 * @ClassName ReceiveMessageListenerController.java
 * @Description TODO
 * @createTime 2021 April 9, 2013 13:50:00
 */
@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);
    }

}

6. Test: Test 8801 sends and receives messages, starts 7001700288018802, and accesses
http://localhost:8801/sendMessage


At this time, there is a crest in RabbitMQ.

5. Group consumption and persistence

1. Create a new cloud stream rabbitmq consumer 8803 according to 8802.
2. Start 70017002880188028803, RabbitMQ.
Continuous multiple visits http://localhost:8801/sendMessage

It can be found that 8802 and 8803 have received the messages sent by 8801 at the same time. There is the problem of repeated consumption.
Actual production cases:


3. Analysis
You can view 8802 and 8803 groups in RabbitMQ Manager. By default, the groups of each micro service are different.
Serial number of 8802:
studyExchange.anonymous.7iKR-4zwTcSWNHvchwzMrA
Group serial number of 8803:
studyExchange.anonymous.YGW64wJRTUmiFsrr6Rs8Vg
4. Cause: the default grouping group is different, and the group serial number is different. It is considered that different groups can be consumed.
You need to customize the configuration group to solve the problem of repeated consumption.
5. Grouping: 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.

  • User defined grouping: both 8802 and 8803 become different groups. The two groups are different. Add group under binder. 8802 is groupA and 8803 is groupB
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 related environment configuration of 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 # Indicates 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
          group: groupA

It can be found that the group serial number has changed
At this time, 8802 and 8803 are still two groups, so they will be consumed repeatedly.

  • Both 8802 and 8803 become the same group. The two groups are the same. Both are changed to groupA
    Four visits http://localhost:8801/sendMessage , it can be found that 8802 and 8803 were accepted twice, avoiding repeated consumption.

6. Persistence:

  • Close 8802 and 8803
  • Remove the group of 8802 and not 8803.
  • 4 visits http://localhost:8801/sendMessage
  • Start 8802 and 8803.
    It can be found that 8802 does not duplicate the messages sent before 8801 in RabbitMQ, but 8803 does.

Topics: Java RabbitMQ Spring Spring Boot Spring Cloud