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.