11 message driven Stream

Message driven Stream

Spring cloud stream can shield the differences between the underlying message middleware, reduce the switching cost, and agree with the message programming model

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

API: https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/3.0.1.RELEASE/reference/html/

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

design idea


Binder:

Spring Cloud Stream standard process routine

Coding API and common annotations

Message driven producer

  1. Create a new module cloud stream rabbitmq provider8801
  2. pom
<dependencies>
    <!--stream rabbit -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
    </dependency>
    <!--eureka client-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--monitor-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--Hot deployment-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

  1. yml
server:
  port: 8801

spring:
  application:
    name: cloud-stream-provider
  cloud:
    stream:
      binders: #Configure the service information of rabbitmq to bind 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: 112.124.22.24  #RabbitMQ uses localhost on the local machine and the ip address of the server on the server
                port: 5672
                username: admin
                password: 123
      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, to "text/plain" in this article
          binder: defaultRabbit #Set the specific settings of the message service to be bound (the red does not affect the use, and the location is correct)

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

  1. Main startup class
@SpringBootApplication
public class StreamMQMain8801 {

    public static void main(String[] args) {
        SpringApplication.run(StreamMQMain8801.class, args);
    }
}
  1. Create a new service.IMessageProvider interface, send message interface
public interface IMessageProvider {
    public String send();
}
  1. Create a new impl.IMessageProviderImpl implementation class under service
import com.atuigu.springcloud.service.IMessageProvider;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;
import javax.annotation.Resource;
import java.util.UUID;

@EnableBinding(Source.class) //Defines the push pipeline for messages
public class MessageProviderImpl implements IMessageProvider {

    @Resource
    private MessageChannel output;//Message sending channel

    @Override
    public String send() {
        String serial = UUID.randomUUID().toString();
        output.send(MessageBuilder.withPayload(serial).build());
        //Send message to RabbitMQ
        System.out.println("*****serial"+serial);
        return null;
    }
}
  1. New controller.SendMessageController
@RestController
public class SendMessageController {

    @Resource
    private IMessageProvider messageProvider;

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

}

  1. Test start 7001 rabbitmq 8801

Enter in the browser: http://localhost:8801/sendMessage , multiple refresh, spooled data:

In the background of RabbitMQ, you can see the graph of the newly generated switch and message

Message driven consumers

  1. Create a new module cloud stream rabbitmq consumer 8802
  2. pom
<dependencies>
    <!--stream rabbit -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
    </dependency>
    <!--eureka client-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--monitor-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--Hot deployment-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

  1. yml
server:
  port: 8802

spring:
  application:
    name: cloud-stream-consumer
  cloud:
    stream:
      binders: #Configure the service information of rabbitmq to bind 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: 112.124.22.24  #RabbitMQ uses localhost on the local machine and the ip address of the server on the server
                port: 5672
                username: admin
                password: 123
      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, to "text/plain" in this article
          binder: defaultRabbit #Set the specific settings of the message service to be bound (the red does not affect the use, and the location is correct)

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

  1. Main startup class
@SpringBootApplication
public class StreamMQMain8802 {

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

}
  1. Create a new controller.ReceiveMessageListenerController
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

@Component
@EnableBinding(Sink.class)
public class ReceiverMessageListenerController {

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

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

  1. test
    Start 700188018802

http://localhost:8801/sendMessage (8801 send message)

8802 received message: the console can see the output information

Group consumption and persistence


A consumer 8803 is created according to 8802



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

8802 8803 in different groups

Fault phenomenon: repeated consumption

Cause: different default groups with different group serial numbers are considered as different groups and can be consumed

The user-defined group is divided into the same group according to the user-defined configuration to solve the problem of repeated consumption

Grouping (queue)

Test grouping

8802 8803 set different groups

Modify yml of 8802

Modify yml of 8803

Test:

Set the same group

Change the group in yml of 8803 to Angelina, and then restart 8003.



Conclusion: only one microservice instance in the same group will be received at a time

Persistence

  1. Stop 8802 and 8803 and remove the group: atguiguA of 8802.
  2. Then 8801 sends 4 messages.
  3. The 88028802 is started without fetching messages. (because the 8802 removes group: atguiguA, a new queue will be created after startup.)
  4. Start 8803 and get the message of 8801 after startup. (because 8803 does not delete group: atguiguA, the atguiguA queue exists before 8801 sends the message, when 8803 is restarted after shutdown, you can get the information sent by 8801 during shutdown (if there are other consumers in the same group (queue), the message will be consumed by other consumers))

If the grouping attribute is not set, stream will automatically generate a temporary queue for you, and the queue will be deleted when the server goes offline. However, the queue with grouping attribute is a persistent queue, and the server will not be deleted when it goes offline, and the message will be obtained when it goes online again

Tags: Spring Cloud

Posted on Sun, 05 Dec 2021 06:58:32 -0500 by adamski