[SpringCloud] Introduction to Microservice Technology Stack 4 - A Preliminary Study of RabbitMQ

RabbitMQ


Install rabbitmq

First make sure you have installed docker

It is docker that pulls the image file:docker pull rabbitmq:3-management

After pulling, open the container

docker run \
 -e RABBITMQ_DEFAULT_USER=itcast \
 -e RABBITMQ_DEFAULT_PASS=123321 \
 --name mq \
 --hostname mq1 \
 -p 15672:15672 \
 -p 5672:5672 \
 -d \
 rabbitmq:3-management

Open the browser to port 15672 of the virtual machine and you will see the rabbitmq management interface.

Insert image description here

We can add new users in the admin tab

wherecan access virtual hosts represents the virtual host corresponding to the current user
It is recommended that different users correspond to different virtual hosts to achieve isolation effect

Virtual host can be created by clicking the virtual hosts button on the right side of the picture above


SpringAMQP basic queue

Since the official native method of operating rabbitmq is too crude and has a lot of code, it is not suitable for daily development. It is recommended to use SpringAMQP to simplify the operation.

Import coordinates

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<!--AMQP依赖,包含RabbitMQ-->
<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>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

Configure rabbitmq link

logging:
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS
spring:
  rabbitmq:
    host: 192.168.113.146 # rabbitMQ的ip地址
    port: 5672 # 端口
    username: itcast
    password: 123321
    virtual-host: /

publisher writes test classes to test AMQP

Since our rabbitmq does not create a queue by defaultsimple.queue, you will not receive any information if you send it directly. You must make a judgment first. If the queue does not exist, create the corresponding queue first. Only after sending it can you receive it!

@Test
public void testSendMessage2SimpleQueue() {
    
    
  RabbitAdmin admin = new RabbitAdmin(rabbitTemplate);
  String queueName = "simple.queue";
  String message = "hello, spring amqp!";

  // 队列是否存在的判断
  if (Objects.isNull(admin.getQueueProperties(queueName))) {
    
    
      Queue queue = new Queue(queueName);
      admin.declareQueue(queue);
  }

  // 发送消息到消息队列
  rabbitTemplate.convertAndSend(queueName, message);
}

If nothing else, you can see the newly created simple.queue queue in the queue option of the rabbitmq console, which contains the first message we sent.


consumer consumes messages in the corresponding queue

Before listening, you must also configure the same application.yaml as the publisher so that you can connect to rabbitmq.

Create a new monitoring consumer class SpringRabbitListener and pass in the following code to execute monitoring

@Component
public class SpringRabbitListener {
    
    
  // 设置消费者需要监听的队列
  @RabbitListener(queues = "simple.queue")
  public void listenWorkQueue1(String msg) throws InterruptedException {
    
    
    // 获取队列中信息
      System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());
      Thread.sleep(20);
  }
}

WorkQueue

Insert image description here

The above figure shows the flow chart of the work queue. In fact, a consumer is added to consume the messages in the queue.

Since we have already created simple.queue in the previous section, there is no need to judge here. We can directly insert a piece of information every 20ms into it.

@Test
public void testSendMessage2WorkQueue() throws InterruptedException {
    
    
  String queueName = "simple.queue";
  String message = "hello, message__";
  for (int i = 1; i <= 50; i++) {
    
    
      rabbitTemplate.convertAndSend(queueName, message + i);
      Thread.sleep(20);
  }
}

Similarly, follow the instructions in the flow chart to set up two consumer listeners
Attention! The first consumer receives messages every 20ms, while the second consumer receives messages every 200ms

@RabbitListener(queues = "simple.queue")
public void listenWorkQueue1(String msg) throws InterruptedException {
    
    
    System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());
    Thread.sleep(20);
}

@RabbitListener(queues = "simple.queue")
public void listenWorkQueue2(String msg) throws InterruptedException {
    
    
    System.err.println("消费者2........接收到消息:【" + msg + "】" + LocalTime.now());
    Thread.sleep(200);
}

Run the consumer first, then use publisher to insert 50 messages

From the log output, we found that consumers 1 and 2 processed the same amount of data (25 items each), but it is obvious that the second consumer is much slower because it only processes a message every 200ms.

The reason for this situation is消息预取, which means that all consumers get the same number of messages, regardless of how often they process the messages


Solve the problem of uneven message distribution

The prefetch configuration item is added to the configuration file. It means that one message must be processed before the next message can be taken out for processing, which effectively avoids the situation of prefetching all messages and piling them up on one consumer in an instant.

spring:
  rabbitmq:
    host: 192.168.113.146 # rabbitMQ的ip地址
    port: 5672 # 端口
    username: itcast
    password: 123321
    virtual-host: /
    listener:
      simple:
        prefetch: 1

At this time, repeat the above operation and find that the effect of "the capable is more laborous" is achieved. Consumer 1 consumes the vast majority of messages due to its fast processing speed, while consumer 2 processes very few messages.

This will compress the overall processing time to 1s or less.


Route publish subscribe FanoutExchange

Insert image description here

Setting up routing publication and subscription requires three steps: setting up routing Exchange, setting up queue Queue, and binding the queue to routing.

Create the fanout configuration file FanoutConfig. We simply create 1 route and 2 queues according to the following code, and perform the binding operation.

@Configuration
public class FanoutConfig {
    
    
  // 配置路由:itcast.fanout
  @Bean
  public FanoutExchange fanoutExchange(){
    
    
      return new FanoutExchange("itcast.fanout");
  }

  // 配置队列:fanout.queue1
  @Bean
  public Queue fanoutQueue1(){
    
    
      return new Queue("fanout.queue1");
  }

  // 绑定队列1到交换机
  @Bean
  public Binding fanoutBinding1(Queue fanoutQueue1, FanoutExchange fanoutExchange){
    
    
      return BindingBuilder
              .bind(fanoutQueue1)
              .to(fanoutExchange);
  }

  // fanout.queue2
  @Bean
  public Queue fanoutQueue2(){
    
    
      return new Queue("fanout.queue2");
  }

  // 绑定队列2到交换机
  @Bean
  public Binding fanoutBinding2(Queue fanoutQueue2, FanoutExchange fanoutExchange){
    
    
      return BindingBuilder
              .bind(fanoutQueue2)
              .to(fanoutExchange);
  }
}

Since they have all added bean annotations, run the consumer project directly and they will be automatically assembled.

At this time, open the rabbitmq control panel and enter the exchange option. You can find our newly created route and the corresponding queue binding relationship.

Insert image description here


As for the subsequent code for publisher information and consumer consumption information, you can refer to the previous section to complete it yourself, so I won’t go into details here.


DirectExchange

Insert image description here

It can be understood as a routing and forwarding mechanism with rules, and the forwarding operation is implemented through the consistent pairing of bindingkey and routingkey.


Configure consumer monitoring

// value 监听队列
// exchange 监听路由
// key bindingkey
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(name = "direct.queue1"),
        exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
        key = {
    
    "red", "blue"}
))

@RabbitListener(bindings = @QueueBinding(
        value = @Queue(name = "direct.queue2"),
        exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
        key = {
    
    "red", "yellow"}
))

Then the publisher sends the message with routingkey to the corresponding route.

@Test
public void testSendDirectExchange() {
    
    
    // 交换机名称
    String exchangeName = "itcast.direct";
    // 消息
    String message = "hello, red!";
    // 发送消息
    rabbitTemplate.convertAndSend(exchangeName, "red", message);
}

TopicExchange

Insert image description here

Similar to directexchange, but routingkey is a list of multiple words. Please refer to the figure above for the specific format.

As for the writing method of publisher and consumer, it is basically the same as directexchange. What needs to be noted is the writing method of routingkey and bindingkey.


Guess you like

Origin blog.csdn.net/delete_you/article/details/133611740