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
- Create a new module cloud stream rabbitmq provider8801
- 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>
- 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
- Main startup class
@SpringBootApplication public class StreamMQMain8801 { public static void main(String[] args) { SpringApplication.run(StreamMQMain8801.class, args); } }
- Create a new service.IMessageProvider interface, send message interface
public interface IMessageProvider { public String send(); }
- 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; } }
- New controller.SendMessageController
@RestController public class SendMessageController { @Resource private IMessageProvider messageProvider; @GetMapping("/sendMessage") public String sengMessage(){ return messageProvider.send(); } }
- 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
- Create a new module cloud stream rabbitmq consumer 8802
- 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>
- 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
- Main startup class
@SpringBootApplication public class StreamMQMain8802 { public static void main(String[] args) { SpringApplication.run(StreamMQMain8802.class, args); } }
- 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); } }
- 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
- Stop 8802 and 8803 and remove the group: atguiguA of 8802.
- Then 8801 sends 4 messages.
- The 88028802 is started without fetching messages. (because the 8802 removes group: atguiguA, a new queue will be created after startup.)
- 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