Spring Cloud Stream message driven integration RabbitMQ

Posted by Trip1 on Thu, 10 Feb 2022 17:09:37 +0100

Spring Cloud Stream message driven integration RabbitMQ

1. What do you do?

At present, there are too many message oriented middleware MQ used (for example, java uses RabbitMQ, while the background big data uses Kafka). Is there a new technology to make us no longer pay attention to the details of specific MQ? We only need to use an adaptive binding method to automatically give us the technology to switch within various MQ? This is the birth of Spring Cloud Stream technology.

In a word: shield the differences between the underlying message middleware, reduce the switching cost, and unify the programming model of message.

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

2. What is spring cloudstream

It is a framework of component message driven microservices.

The application interacts with the binder object in Spring Cloud Stream through inputs and outputs.

We configure binding, and the binder object of Spring Cloud Stream interacts with the message middleware.

Therefore, we only need to know how to interact with Spring Cloud Stream to facilitate the use of message driven mode.

By using Spring Integration to connect the message broker middleware to realize message time driving.

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.

Note: currently only RabbitMQ and Kafka are supported

3. Implementation mode

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

The message communication mode in Stream follows the publish subscribe mode.

formexplain
MiddlewareMiddleware, RabbitMQ/Kafka
BinderBinder is applied to the encapsulation between message middleware. Binder can easily connect the middleware and dynamically change the message type (corresponding to Kafka topic and RabbitMQ exchange), which can be realized through the configuration file
@InputThe annotation identifies the input pipeline through which messages received enter the application
@OutputAnnotations identify the output pipeline through which published messages leave the application
@StreamListenerListening queue is used for receiving messages from the consumer's queue
@EnalbeBindingRefers to the binding of channel and exchange

4. Message driven producer (rabbitmq based)

Add dependency to pom file:

        <!--introduce stream of RabbitMQ Message driven-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>

yml profile:

server:
  port: 8801

spring:
  application:
    name: cloud-stream-provider
  cloud:
    stream:
      binders:    #Configure the service information to bind rabbitmq here
        defaultRabbit:  #Represents the name of the definition, which is used in the binding collection
          type: rabbit  #Information 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 that you want to use the Exchange name definition (send messages to this Exchange)
          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:
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2  #Set the heartbeat interval (the default is 30 seconds)
    lease-expiration-duration-in-seconds: 5   #If no heartbeat is received within this time, it will be removed
    instance-id: send-8801.com    #Displays the host name in the information list
    prefer-ip-address: true   #The access path becomes an IP address

Interface:

public interface IMessageProvider {
    public String send();
}

Implementation class:

import com.atguigu.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;

@EnableBinding(Source.class)    //Defines the push pipeline for messages
public class IMessageProviderImpl 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 layer call:

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

import javax.annotation.Resource;

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

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

}

5. Message driven consumers

Add dependency to pom file:

 <!--introduce stream of RabbitMQ Message driven-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>

yml profile:

server:
  port: 8802

spring:
  application:
    name: cloud-stream-consumer
  cloud:
    stream:
      binders:    #Configure the service information to bind rabbitmq here
        defaultRabbit:  #Represents the name of the definition, which is used in the binding collection
          type: rabbit  #Information 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 that the Exchange name definition is to be used (messages from this Exchange will be received)
          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 (red does not affect startup!!!)

eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2  #Set the heartbeat interval (the default is 30 seconds)
    lease-expiration-duration-in-seconds: 5   #If no heartbeat is received within this time, it will be removed
    instance-id: receive-8802.com    #Displays the host name in the information list
    prefer-ip-address: true   #The access path becomes an IP address

controller layer receiving:

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)  //Open message driven binding
public class ReceiveMessageListenerController {

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

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

}

6. Consumer grouping

The following cases:

If an order is obtained by multiple services at the same time, it will cause data errors.

Stream uses message grouping to solve this problem.

Multiple consumers in the same group in the Stream are competitive, which can ensure that messages will only be consumed by one application, and different groups can consume comprehensively (repeated consumption).

We only need to add the configuration in the message consumer's yml configuration file:

7. Persistent message

If there are multiple message consumers, when one of the consumer services crashes, the message producer is still pushing messages. If persistence is not configured, the crashed service will not receive messages after restart. The configured attribute is also group, which supports persistence.
That is, adding the group attribute supports message persistence

Topics: RabbitMQ Spring Cloud