RabbitMQ: message middleware

concept

In the microservice architecture, project A calls project B, and project B calls project C, project C calls project D. .
The user must wait for the content between the projects to run sequentially before returning the result to the user.
This is a synchronous call, the user may wait for a long time, and the user experience is relatively poor

Message middleware: It can be understood as a queue, and the tasks that users need to process are handed over to the queue, and the tasks are queued in the queue for execution, and the user does not need to wait for the execution time of the code.
insert image description here

Introduction to the management interface

1.Overview: This panel is the RabbitMQ basic information display panel, which lists server information, such as: node name, memory usage, disk usage, etc.
2.Connections: This panel displays all client links connected to RabbitMQ. Only links based on port 5672 are shown.
3.Channels: This panel displays the specific channels in each link. The marking method is a link (number), such as: 192.168.91.1:12345(1).
4.Exchanges: This panel displays the existing exchanges in RabbitMQ, and indicates the basic information such as the name and type of the exchange. Among them, only the direct exchange has a default exchange (AMQP default). When using the direct exchange, if the name is not explicitly specified, the AMQP default exchange can also be used to specify the name explicitly. But there is no default for other types of switches, all need to specify the name.
5.Queues: This panel displays the queue information in RabbitMQ.

4 Common Switch Types

1. Direct switch:

Direct will put the message into the specified queue according to the routing key.
Create a queue q1 in the Queues interface, create a direct switch fs.direct in Exchanges to bind the switch q1, specify the routing key, fs.q1
1. Producer
1. Import dependencies

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

2. Configure yml

# 配置RabbitMQ相关信息
# 当创建RabbitMQ容器的时候,不提供用户名和密码配置,自动创建用户guest,密码guest。
# guest用户只能本地访问RabbitMQ。
spring:
  rabbitmq:
    host: localhost # RabbitMQ服务器的IP。默认localhost
    port: 32769 # RabbitMQ服务器的端口。
    username: guest # RabbitMQ的访问用户名。默认guest。
    password: guest # RabbitMQ的访问密码。默认guest
    virtual-host: / # 连接RabbitMQ中的哪一个虚拟主机。默认 /

3. Send a message under the test package:
the type can be: AmqpTemplate (top-level interface), RabbitOperations (dedicated sub-interface), RabbitTemplate (specific implementation) It is
recommended to use the interface: the priority is RabbitOperations > AmqpTemplate

The message type that Spring AMQP can send must be Message type.
Spring AMQP can help programmers automatically encapsulate the message type Message object (default encapsulation), and automatically convert the encapsulated message body type to Object, as long as the type can be serialized.

After the message is sent to the queue, there is no need to wait, it is an asynchronous operation, and the producer will continue to execute.

@SpringBootTest
class RabbitmqApplicationTests {
    
    
    @Autowired
    private RabbitOperations rabbitOperations;
    @Test
    void contextLoads() {
    
    
		//仅发送,不需要等待返回值,异步,推荐
        rabbitOperations.convertAndSend("fs.dire"(交换机名称),"xy.q1"(路由key),"你好 rabbitmq"(消息内容));
        //发送,有返回值,使得rabbitmq变成同步的了,需要等待返回值,失去了rabbitmq的意义
        //rabbitOperations.convertSendAndReceive("","","");
    }
}

2. Consumer
1.pom.xml

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

2.yml

spring:
  rabbitmq:
    host: localhost
    port: 32769
    username: guest
    password: guest

3. Listening queue
Modifier: public
Return value: Asynchronous message must be void
Method name: Custom
Parameter table: A parameter, the type can be Message or a specific message body type (Object). message is the only type of message in Spring AMQP. Represents a complete message, consisting of a head and a body. If there is no processing requirement for the message header, you can directly define the specific type of the message body.
Method implementation: According to the specific requirements, just define it.

Note: Methods can throw any type of exception. As long as an exception is thrown, it means a consumption error, and RabbitMQ does not delete the messages in the queue.
Annotations are RabbitListeners.

If there are multiple consumption monitors, polling consumption is used by default, and consumption monitors do not need to wait for each other, and are executed concurrently.

@Component
public class StringMessageConsumer {
    
    
    @RabbitListener(queues = {
    
    "q1"})(队列名称可以指定多个)
    public void received1(String m){
    
    
        System.out.println(m);
    }

	@RabbitListener(queues = {
    
    "q1"})(队列名称可以指定多个)
    public void received2(String m){
    
    
        System.out.println(m);
    }
}

2. Fanout switch

Fan-shaped switch: The message will be sent to all queues bound to the current switch, and the routing key may not be written.
1. Create queues f1 and f2 on the Queues interface, and create a fanout switch fs in Exchanges. Fanout binds switch f1, f2, do not specify a route
2. Write test code

    @Autowired
    private RabbitOperations rabbitOperations;
    @Test
    void contextLoad1s() {
    
    
    	//没有绑定路由键,设为null即可
        rabbitOperations.convertAndSend("fs.fanout",null,"你好 rabbitmq");
    }

3. There is one more message in f1 and f2 queues

3. Topic switch

Topic Exchanger: Routing keys can include special characters for wildcarding. Special characters include: asterisk and '#'.
Asterisk: represents a word. Use '.' to separate multiple words.
'#' : represents 0~n characters, that is, any string. //For example, abc.# represents all routing keys starting with abc.
1. Create queues t1, t2, and t3 on the Queues interface and create topic switches fs.topic on Exchanges to bind switches t1, t2, and t3.
2. Specify routing keys for t1: abc.t1, specify t2 routing key: abc.*, specify t3 routing key: abc.#
3. Write test code

    @Autowired
    private RabbitOperations rabbitOperations;

    @Test
    void contextLoawd1s() {
    
    
        rabbitOperations.convertAndSend("fs.topic","abc.t1","你好 rabbitmq");//t1,t2,t3
        rabbitOperations.convertAndSend("fs.topic","abc.123","你好 rabbitmq");//t2,t3
        rabbitOperations.convertAndSend("fs.topic","abc.123.234","你好 rabbitmq");//t3
    }

4.headers switch

The main difference between the headers switch and the direct switch is that the header part of the message can be transmitted when the message is delivered.
Production messages
Create a queue h1 on the Queues interface, create a headers switch fs.headers in Exchanges, bind the switch h1, and specify the routing key, fs.h1

    @Autowired
    private RabbitOperations rabbitOperations;
    
    @Test
    void conwtextLoawd1s() {
    
    
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setHeader("fs","java");
        Message message = new Message("我是消息".getBytes(), messageProperties);
        rabbitOperations.convertAndSend("fs.headers","fs.h1",message);
    }

consumption news

    @Test
    void conwtextLoawd1s() {
    
    
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setHeader("fs","java");
        Message message = new Message("我是消息".getBytes(), messageProperties);
        rabbitOperations.convertAndSend("fs.headers","fs.h1",message);
    }

object type messaging

1. The producer
creates the User class
and pays attention to inheriting the serialization interface Serializable
and specifying the serialization version number serialVersionUID and consistent with the consumer (if not specified, the consumer’s User class must be exactly the same as the producer’s User class, otherwise, it will not Consider it a unified type)
The package path of the User class in the producer and consumer must be consistent

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    
    
    private final Long serialVersionUID = 1L;
    private String name;
    private Integer age;
}

发送消息
    	@Autowired
    	private RabbitOperations rabbitOperations;
    
        User user = new User("张三",18);
        rabbitOperations.convertAndSend("fs.direct","xy.q1",user);

2. Consumer
Create a User class in the consumer
and pay attention to inherit the serialization interface Serializable
and specify the serialization version number serialVersionUID and be consistent with the consumer (if not specified, the consumer’s User class must be exactly the same as the producer’s User class, Otherwise, it will not be considered as a unified type)
The package path of the User class in the producer and the consumer must be the same,
directly received with the corresponding type

@Component
public class StringMessageConsumer {
    
    
    @RabbitListener(queues = {
    
    "q1"})
    public void received(User(类型和发送来的一致即可) user){
    
    
        System.out.println(user);
    }
}

Uniformly use the Message object to receive, commonly used Headers switch

@Component
public class StringMessageConsumer {
    
    
    @RabbitListener(queues = {
    
    "q1"})
    public void received(Message message) throws IOException, ClassNotFoundException {
    
    
        byte[] body = message.getBody();
        
        ByteArrayInputStream bai = new ByteArrayInputStream(body);
        ObjectInputStream ois = new ObjectInputStream(bai);
        
        Object object = ois.readObject();
        if(object instanceof User){
    
    
            User user = (User) object;
            System.out.println(user);
        }
    }
}

synchronous wait

Wait for the result to return before proceeding.
The default wait is 5s, which can be configured, and return null
yml configuration when the timeout expires.

spring:
  rabbitmq:
    host: localhost # RabbitMQ服务器的IP。默认localhost
    port: 32769 # RabbitMQ服务器的端口。
    username: guest # RabbitMQ的访问用户名。默认guest。
    password: guest # RabbitMQ的访问密码。默认guest
    virtual-host: / # 连接RabbitMQ中的哪一个虚拟主机。默认 /

producer:

    @Test
    void contextLowads() {
    
    
    	//发送,有返回值,使得rabbitmq变成同步的了,需要等待返回值,等待结果返回,才能往下执行
        String aa = (String)rabbitOperations.convertSendAndReceive("fs.direct", "xy.q1", "aa");
        System.out.println(aa);
}

consumer:

@Component
public class StringMessageConsumer {
    
    
    @RabbitListener(queues = {
    
    "q1"})
    public String received(Message message) {
    
    
        return "hello";
    }
}

Create a queue using code

1. Create a queue on the producer side
It is created when a message is sent, not when the project is started.

@Configuration
public class RabbitMQConfig {
    
    
    // 发送消息时如果不存在这个队列,会自动创建这个队列。
    // 注意:是发送消息时,而不是启动项目时。
    // 相当于:可视化操作时创建一个队列
    // 如果队列创建完成后,没有绑定(没有另外两个方法),默认绑定到AMQP default交换器
    @Bean
    public Queue queue(){
    
    
        return new Queue("queue.second");
    }

    // 如果没有这个交换器,在发送消息创建这个交换器
    // 配置类中方法名就是这个类型的实例名。相当于<bean id="" class="">的id属性,返回值相当于class
    @Bean
    public DirectExchange directExchange(){
    
    
        return new DirectExchange("direct.first.ex");
    }

    // 配置类中方法参数,会由Spring 容器自动注入
    @Bean
    public Binding directBingding(DirectExchange directExchange,Queue queue){
    
    
        // with(“自定义路由键名称”)
        return BindingBuilder.bind(queue).to(directExchange).with("routing.key.2");
        // withQueueName() 表示队列名就是路由键名称
        // return BindingBuilder.bind(queue).to(directExchange).withQueueName();
    }
}

2. Consumers

    @RabbitListener(bindings = {
    
    
            @QueueBinding(
                    value = @Queue(name = "queue.second",autoDelete = "false", durable = "true"),
                    exchange = @Exchange(name = "direct.second.ex",autoDelete = "false", type = ExchangeTypes.DIRECT),
                    key = {
    
    "routing.key.second.1"}
            )
    })
    public void onMessage(String messageBody){
    
    
        System.out.println("第二个消息消费者监听,处理消息:" + messageBody);
    }

to be continued…

Guess you like

Origin blog.csdn.net/m0_56182317/article/details/130228998