Message Queue (7) - Design of virtual host

foreword

We have already stored queues, switches, and bindings in the database and tested them before, and also stored the messages in files, realizing the persistent storage of messages, and we have written these into the memory, and also Provides an interface to the upper layer call. And these contents are all in the middleman server. Next, we write an important class, virtual host: a virtual host is similar to a database. It not only manages data, but also provides some core APIs for upper layer calls. The main process is to connect the virtual host with the previously written API

Design of virtual host

How to express the affiliation between the switch and the virtual host?
Assumption 1: Refer to the design of the database to implement a one-to-many solution.
Assumption 2: Re-agreement, the name of the switch = the name of the virtual host + the name of the switch;
the purpose of the virtual host is to ensure that the content between different virtual hosts is mutually does not affect. Here we take the realization of assumption 2, which is relatively simple

accomplish

Create a virtualHost class, in which switches, queues, bindings, messages are managed, and exceptions are handled at the same time
For virtual hosts, we design the following attributes
Host name: virtuaHostName
Memory control class: MemoryDataCenter
Hard disk control class: DiskDataCenter
Switch forwarding rule class: Router (not implemented)
core functionality of consumers: ConsumerManager (not implemented)

Let’s talk about locks here: because we will write to the hard disk and files next, so in order to prevent problems in multi-threaded situations, we will add locks uniformly. Here we design two lock objects

// 操作交换机的锁对象
    private final Object exchangeLocker = new Object();
    // 操作队列的锁对象
    private final Object queueLocker = new Object();

core method

  1. Initialization: Initialize the memory control class, and the hard disk control class
 public VirtuaHost(String name){
    
    
        this.virtuaHostName = name;
        diskDataCenter.init();
        // 如果存在数据, 将硬盘里面的数据, 恢复到内存中
        try {
    
    
            memoryDataCenter.recovery(diskDataCenter);
        } catch (IOException | MqException | ClassNotFoundException e) {
    
    
            e.printStackTrace();
            System.out.println("virtualHost  恢复内存数据失败");
        }
    }
  1. create switch
 public boolean exchangeDeclare(String exchangeName , ExchangeType exchangeType ,
                                   boolean durable, boolean autoDelete , Map<String ,Object> argument){
    
    
        exchangeName = virtuaHostName+exchangeName;
        try {
    
    
            synchronized (exchangeLocker){
    
    
                Exchange existsExchange = memoryDataCenter.getExchange(exchangeName);
                if (existsExchange != null){
    
    
                    // 该交换机已经存在!
                    System.out.println("[VirtualHost] 交换机已经存在! exchangeName=" + exchangeName);
                    return true;
                }
                Exchange exchange = new Exchange();
                exchange.setName(exchangeName);
                exchange.setType(exchangeType);
                exchange.setDurable(durable);
                exchange.setAutoDelete(autoDelete);
                exchange.setArguments(argument);
                // 写入硬盘
                if (durable){
    
    
                    diskDataCenter.insertExchange(exchange);
                }
                // 写入内存
                memoryDataCenter.insertExchange(exchange);
                System.out.println("[VirtualHost] 交换机创建完成! exchangeName=" + exchangeName);
                // 上述逻辑, 先写硬盘, 后写内存. 目的就是因为硬盘更容易写失败. 如果硬盘写失败了, 内存就不写了.
                // 要是先写内存, 内存写成功了, 硬盘写失败了, 还需要把内存的数据给再删掉. 就比较麻烦了.
            }
            return true;
        }catch (Exception e) {
    
    
            System.out.println("[VirtualHost] 交换机创建失败! exchangeName=" + exchangeName);
            e.printStackTrace();
            return false;
        }
    }
  1. delete switch
public boolean exchangeDelete(String exchangeName) {
    
    
        exchangeName = virtuaHostName+exchangeName;
        try {
    
    
            synchronized (exchangeLocker){
    
    
                // 1. 先找到对应的交换机.
                Exchange existsExchange = memoryDataCenter.getExchange(exchangeName);
                if (existsExchange == null){
    
    
                    throw new MqException("[VirtualHost] 交换机不存在无法删除!");
                }
                // 2 看硬盘中是否有数据
                if (existsExchange.isDurable()){
    
    
                    diskDataCenter.deleteExchange(exchangeName);
                }
                // 3. 删除内存中的交换机数据
                memoryDataCenter.deleteExchange(exchangeName);
                System.out.println("[VirtualHost] 交换机删除成功! exchangeName=" + exchangeName);
            }
            return true;
        } catch (Exception e) {
    
    
            System.out.println("[VirtualHost] 交换机删除失败! exchangeName=" + exchangeName);
            e.printStackTrace();
            return false;
        }
    }
  1. create queue
  // 创建队列
    public boolean queueDeclare(String queueName, boolean durable, boolean exclusive, boolean autoDelete,
                                Map<String, Object> arguments) {
    
    
        queueName = virtuaHostName+queueName;
        try {
    
    
            synchronized (queueLocker){
    
    
                // 1查找队列是否存在
                MSGQueue existsQueue = memoryDataCenter.getQueue(queueName);
                if (existsQueue != null){
    
    
                    return true;
                }
                // 2. 创建队列对象
                MSGQueue queue = new MSGQueue();
                queue.setName(queueName);
                queue.setDurable(durable);
                queue.setExclusive(exclusive);
                queue.setAutoDelete(autoDelete);
                queue.setArguments(arguments);
                // 3. 写硬盘
                if (durable) {
    
    
                    diskDataCenter.insertQueue(queue);
                }
                // 4. 写内存
                memoryDataCenter.insertQueue(queue);
                System.out.println("[VirtualHost] 队列创建成功! queueName=" + queueName);
            }
            return true;
        } catch (Exception e) {
    
    
            System.out.println("[VirtualHost] 队列创建失败! queueName=" + queueName);
            e.printStackTrace();
            return false;
        }
    }
  1. delete queue
  // 删除队列
    public boolean queueDelete(String queueName) {
    
    
        queueName = virtuaHostName+queueName;// 删除队列
       try{
    
    
           synchronized (queueLocker){
    
    
               MSGQueue existsQueue = memoryDataCenter.getQueue(queueName);
               if (existsQueue == null){
    
    
                   throw new MqException("[VirtualHost] 队列不存在! 无法删除! queueName=" + queueName);
               }
               // 2 删除 硬盘数据
               if (existsQueue.isDurable()){
    
    
                   diskDataCenter.deleteQueue(queueName);
               }
               // 3. 删除内存数据
               memoryDataCenter.deleteQueue(queueName);
               System.out.println("[VirtualHost] 删除队列成功! queueName=" + queueName);
           }
           return true;
       } catch (MqException | IOException e) {
    
    
           System.out.println("[VirtualHost] 删除队列失败! queueName=" + queueName);
           e.printStackTrace();
           return false;
       }
    }
  1. create binding
 // 确认消息的编写
    public boolean basicPublish(String exchangeName, String routingKey, BasicProperties basicProperties, byte[] body) {
    
    
        // 1. 转换交换机的名字
        exchangeName = virtuaHostName+exchangeName;
        try {
    
    
        // 2. 检查routingkey是否合法
            if (!router.checkRoutingKey(routingKey)){
    
    
                throw new MqException("[VirtualHost] routingKey 非法! routingKey=" + routingKey);
            }
            // 3. 查找交换机的看是否存在
            Exchange exchange = memoryDataCenter.getExchange(exchangeName);
            if (exchange == null){
    
    
                throw new MqException("[VirtualHost] 交换机不存在! exchangeName=" + exchangeName);
            }
            // 4.判断交换机的类型
           if (exchange.getType() == ExchangeType.direct){
    
    
               // 按照直接交换机的方式来转发消息
               // 以 routingKey 作为队列的名字, 直接把消息写入指定的队列中.
               // 此时, 可以无视绑定关系.
               String queueName =  virtuaHostName+routingKey;
               // 5. 构造消息对象
               Message message = Message.createMessageWithId(routingKey,basicProperties,body);
               // 6. 查找该队列名对应的对象
               MSGQueue queue = memoryDataCenter.getQueue(queueName);
               if (queue == null){
    
    
                   throw new MqException("[VirtualHost] 队列不存在! queueName=" + queueName);
               }
               // 7. 队列存在, 直接给队列中写入消息
               sendMessage(queue,message);
           }else {
    
    
                // 用其他俩种方式来转发
               // 找到交换机 关联的所有绑定, 并遍历这些绑定对象
               ConcurrentHashMap<String,Binding> bindingMap = memoryDataCenter.getBindings(exchangeName);
               for (Map.Entry<String,Binding> entry:bindingMap.entrySet()) {
    
    
                   Binding binding = entry.getValue();
                   MSGQueue queue = memoryDataCenter.getQueue(binding.getMsgQueueName());
                   if (queue == null){
    
    
                       // 此处咱们就不抛出异常了. 可能此处有多个这样的队列.
                       // 希望不要因为一个队列的失败, 影响到其他队列的消息的传输.
                       System.out.println("[VirtualHost] basicPublish 发送消息时, 发现队列不存在! queueName=" + binding.getMsgQueueName());
                       continue;
                   }
                   // 构造消息对象
                   Message message = Message.createMessageWithId(routingKey,basicProperties,body);
                   // 判断是什么规则 能否转发

                   if (!router.route(exchange.getType(),binding,message)){
    
    
                       continue;
                   }
                   sendMessage(queue,message);
               }
           }
           return true;
        } catch (Exception e) {
    
    
            System.out.println("[VirtualHost] 消息发送失败!");
            e.printStackTrace();
            return false;
        }
    }
  1. delete binding
 public boolean queueUnbind(String queueName, String exchangeName) {
    
    
        queueName = virtuaHostName + queueName;
        exchangeName = virtuaHostName + exchangeName;
        try {
    
    
            synchronized (exchangeLocker) {
    
    
                synchronized (queueLocker) {
    
    
                    // 1. 获取 binding 看是否已经存在~
                    Binding binding = memoryDataCenter.getBinding(exchangeName, queueName);
                    if (binding == null) {
    
    
                        throw new MqException("[VirtualHost] 删除绑定失败! 绑定不存在! exchangeName=" + exchangeName + ", queueName=" + queueName);
                    }
                    // 2. 无论绑定是否持久化了, 都尝试从硬盘删一下. 就算不存在, 这个删除也无副作用.
                    diskDataCenter.deleteBinding(binding);
                    // 3. 删除内存的数据
                    memoryDataCenter.deleteBinding(binding);
                    System.out.println("[VirtualHost] 删除绑定成功!");
                }
            }
            return true;
        } catch (Exception e) {
    
    
            System.out.println("[VirtualHost] 删除绑定失败!");
            e.printStackTrace();
            return false;
        }
    }
  1. subscribe news
   // 订阅消息.
    // 添加一个队列的订阅者, 当队列收到消息之后, 就要把消息推送给对应的订阅者.
    // consumerTag: 消费者的身份标识
    // autoAck: 消息被消费完成后, 应答的方式. 为 true 自动应答. 为 false 手动应答.
    // consumer: 是一个回调函数. 此处类型设定成函数式接口. 这样后续调用 basicConsume 并且传实参的时候, 就可以写作 lambda 样子了.
    public boolean basicConsume(String consumerTag, String queueName, boolean autoAck, Consumer consumer) {
    
    
      // 构造一个ConsumEnv对象, 将这个对象的队列找到, 然后将consume 添加到这个队列里面
        queueName = virtuaHostName+ queueName;
        try {
    
    
            consumerManager.addConsumer(consumerTag,queueName,autoAck,consumer);
            System.out.println("[VirtualHost] basicConsume 成功! queueName=" + queueName);
            return true;
        } catch (Exception e) {
    
    
            System.out.println("[VirtualHost] basicConsume 失败! queueName=" + queueName);
            e.printStackTrace();
            return false;
        }
    }
  1. Confirm ACK

public boolean basicAck(String queueName, String messageId) { queueName = virtuaHostName + queueName; try { // 1. Get the message and queue Message message = memoryDataCenter.getMessage(messageId); if (message == null) { throw new MqException( "[VirtualHost] The message to be acknowledged does not exist! messageId=" + messageId); } MSGQueue queue = memoryDataCenter.getQueue(queueName); if (queue == null) { throw new MqException("[VirtualHost] The queue to be acknowledged does not Exist! queueName=" + queueName); } // 2. Delete the data on the hard disk if (message.getDeliverMode() == 2) { diskDataCenter.deleteMessage(queue, message); } // 3. Delete the message in the message center data memoryDataCenter.removeMessage(messageId);

















// 4. Delete the data in the collection to be confirmed
memoryDataCenter.removeMessageWaitAck(queueName, messageId);
System.out.println("[VirtualHost] basicAck succeeded! The message was successfully confirmed! queueName=" + queueName
+ ", messageId=" + messageId);
return true;
} catch (Exception e) { System.out.println("[VirtualHost] basicAck failed! Message acknowledgment failed! queueName=" + queueName + ", messageId=" + messageId); e.printStackTrace( ); return false; } }





  1. confirmation message
// 确认消息的编写
    public boolean basicPublish(String exchangeName, String routingKey, BasicProperties basicProperties, byte[] body) {
    
    
        // 1. 转换交换机的名字
        exchangeName = virtuaHostName+exchangeName;
        try {
    
    
        // 2. 检查routingkey是否合法
            if (!router.checkRoutingKey(routingKey)){
    
    
                throw new MqException("[VirtualHost] routingKey 非法! routingKey=" + routingKey);
            }
            // 3. 查找交换机的看是否存在
            Exchange exchange = memoryDataCenter.getExchange(exchangeName);
            if (exchange == null){
    
    
                throw new MqException("[VirtualHost] 交换机不存在! exchangeName=" + exchangeName);
            }
            // 4.判断交换机的类型
           if (exchange.getType() == ExchangeType.direct){
    
    
               // 按照直接交换机的方式来转发消息
               // 以 routingKey 作为队列的名字, 直接把消息写入指定的队列中.
               // 此时, 可以无视绑定关系.
               String queueName =  virtuaHostName+routingKey;
               // 5. 构造消息对象
               Message message = Message.createMessageWithId(routingKey,basicProperties,body);
               // 6. 查找该队列名对应的对象
               MSGQueue queue = memoryDataCenter.getQueue(queueName);
               if (queue == null){
    
    
                   throw new MqException("[VirtualHost] 队列不存在! queueName=" + queueName);
               }
               // 7. 队列存在, 直接给队列中写入消息
               sendMessage(queue,message);
           }else {
    
    
                // 用其他俩种方式来转发
               // 找到交换机 关联的所有绑定, 并遍历这些绑定对象
               ConcurrentHashMap<String,Binding> bindingMap = memoryDataCenter.getBindings(exchangeName);
               for (Map.Entry<String,Binding> entry:bindingMap.entrySet()) {
    
    
                   Binding binding = entry.getValue();
                   MSGQueue queue = memoryDataCenter.getQueue(binding.getMsgQueueName());
                   if (queue == null){
    
    
                       // 此处咱们就不抛出异常了. 可能此处有多个这样的队列.
                       // 希望不要因为一个队列的失败, 影响到其他队列的消息的传输.
                       System.out.println("[VirtualHost] basicPublish 发送消息时, 发现队列不存在! queueName=" + binding.getMsgQueueName());
                       continue;
                   }
                   // 构造消息对象
                   Message message = Message.createMessageWithId(routingKey,basicProperties,body);
                   // 判断是什么规则 能否转发

                   if (!router.route(exchange.getType(),binding,message)){
    
    
                       continue;
                   }
                   sendMessage(queue,message);
               }
           }
           return true;
        } catch (Exception e) {
    
    
            System.out.println("[VirtualHost] 消息发送失败!");
            e.printStackTrace();
            return false;
        }
    }

    private void sendMessage(MSGQueue queue, Message message) throws IOException, MqException, InterruptedException {
    
    
        // 此处发送消息就是将消息写入到硬盘 和内存之间
        int delivermode = message.getDeliverMode();
        // 判断是否要持久化
        if (delivermode == 2){
    
    
            diskDataCenter.sendMessage(queue,message);
        }
        // 写入内存
        memoryDataCenter.sendMessage(queue,message);
        // 通知消费者可以消费消息了
        consumerManager.notifyConsume(queue.getName());

    }

Virtual host test

package com.example.demo.mqServer.dataCenter;

import com.example.demo.Common.Consumer;
import com.example.demo.MqApplication;
import com.example.demo.mqServer.VirtuaHost;
import com.example.demo.mqServer.core.BasicProperties;
import com.example.demo.mqServer.core.ExchangeType;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.File;
import java.io.IOException;

@SpringBootTest
public class VirtualHostTests {
    
    
    private VirtuaHost virtualHost = null;

    @BeforeEach
    public void setUp() {
    
    
        MqApplication.context = SpringApplication.run(MqApplication.class);
        virtualHost = new VirtuaHost("default");
    }

    @AfterEach
    public void tearDown() throws IOException {
    
    
        MqApplication.context.close();
        virtualHost = null;
        // 把硬盘的目录删除掉
        File dataDir = new File("./data");
        FileUtils.deleteDirectory(dataDir);
    }

    @Test
    public void testExchangeDeclare() {
    
    
        boolean ok = virtualHost.exchangeDeclare("testExchange", ExchangeType.direct,
                true, false, null);
        Assertions.assertTrue(ok);
    }

    @Test
    public void testExchangeDelete() {
    
    
        boolean ok = virtualHost.exchangeDeclare("testExchange", ExchangeType.direct,
                true, false, null);
        Assertions.assertTrue(ok);

        ok = virtualHost.exchangeDelete("testExchange");
        Assertions.assertTrue(ok);
    }

    @Test
    public void testQueueDeclare() {
    
    
        boolean ok = virtualHost.queueDeclare("testQueue", true,
                false, false, null);
        Assertions.assertTrue(ok);
    }

    @Test
    public void testQueueDelete() {
    
    
        boolean ok = virtualHost.queueDeclare("testQueue", true,
                false, false, null);
        Assertions.assertTrue(ok);

        ok = virtualHost.queueDelete("testQueue");
        Assertions.assertTrue(ok);
    }

    @Test
    public void testQueueBind() {
    
    
        boolean ok = virtualHost.queueDeclare("testQueue", true,
                false, false, null);
        Assertions.assertTrue(ok);

        ok = virtualHost.exchangeDeclare("testExchange", ExchangeType.direct,
                true, false, null);
        Assertions.assertTrue(ok);

        ok = virtualHost.queueBind("testQueue", "testExchange", "testBindingKey");
        Assertions.assertTrue(ok);
    }

    @Test
    public void testQueueUnbind() {
    
    
        boolean ok = virtualHost.queueDeclare("testQueue", true,
                false, false, null);
        Assertions.assertTrue(ok);

        ok = virtualHost.exchangeDeclare("testExchange", ExchangeType.direct,
                true, false, null);
        Assertions.assertTrue(ok);

        ok = virtualHost.queueBind("testQueue", "testExchange", "testBindingKey");
        Assertions.assertTrue(ok);

        ok = virtualHost.queueUnbind("testQueue", "testExchange");
        Assertions.assertTrue(ok);
    }

    @Test
    public void testBasicPublish() {
    
    
        boolean ok = virtualHost.queueDeclare("testQueue", true,
                false, false, null);
        Assertions.assertTrue(ok);

        ok = virtualHost.exchangeDeclare("testExchange", ExchangeType.direct,
                true, false, null);
        Assertions.assertTrue(ok);

        ok = virtualHost.basicPublish("testExchange", "testQueue", null,
                "hello".getBytes());
        Assertions.assertTrue(ok);
    }

    // 先订阅队列, 后发送消息
    @Test
    public void testBasicConsume1() throws InterruptedException {
    
    
        boolean ok = virtualHost.queueDeclare("testQueue", true,
                false, false, null);
        Assertions.assertTrue(ok);
        ok = virtualHost.exchangeDeclare("testExchange", ExchangeType.direct,
                true, false, null);
        Assertions.assertTrue(ok);

        // 先订阅队列
        ok = virtualHost.basicConsume("testConsumerTag", "testQueue", true, new Consumer() {
    
    
            @Override
            public void handleDelivery(String consumerTag, BasicProperties basicProperties, byte[] body) {
    
    
                // 消费者自身设定的回调方法.
                System.out.println("messageId=" + basicProperties.getMessageId());
                System.out.println("body=" + new String(body, 0, body.length));

                Assertions.assertEquals("testQueue", basicProperties.getRoutingKey());
                Assertions.assertEquals(1, basicProperties.getDeliverMode());
                Assertions.assertArrayEquals("hello".getBytes(), body);
            }
        });
        Assertions.assertTrue(ok);

        Thread.sleep(500);

        // 再发送消息
        ok = virtualHost.basicPublish("testExchange", "testQueue", null,
                "hello".getBytes());
        Assertions.assertTrue(ok);
    }

    // 先发送消息, 后订阅队列.
    @Test
    public void testBasicConsume2() throws InterruptedException {
    
    
        boolean ok = virtualHost.queueDeclare("testQueue", true,
                false, false, null);
        Assertions.assertTrue(ok);
        ok = virtualHost.exchangeDeclare("testExchange", ExchangeType.direct,
                true, false, null);
        Assertions.assertTrue(ok);

        // 先发送消息
        ok = virtualHost.basicPublish("testExchange", "testQueue", null,
                "hello".getBytes());
        Assertions.assertTrue(ok);

        // 再订阅队列
        ok = virtualHost.basicConsume("testConsumerTag", "testQueue", true, new Consumer() {
    
    
            @Override
            public void handleDelivery(String consumerTag, BasicProperties basicProperties, byte[] body) {
    
    
                // 消费者自身设定的回调方法.
                System.out.println("messageId=" + basicProperties.getMessageId());
                System.out.println("body=" + new String(body, 0, body.length));

                Assertions.assertEquals("testQueue", basicProperties.getRoutingKey());
                Assertions.assertEquals(1, basicProperties.getDeliverMode());
                Assertions.assertArrayEquals("hello".getBytes(), body);
            }
        });
        Assertions.assertTrue(ok);

        Thread.sleep(500);
    }

    @Test
    public void testBasicConsumeFanout() throws InterruptedException {
    
    
        boolean ok = virtualHost.exchangeDeclare("testExchange", ExchangeType.fanout, false, false, null);
        Assertions.assertTrue(ok);

        ok = virtualHost.queueDeclare("testQueue1", false, false, false, null);
        Assertions.assertTrue(ok);
        ok = virtualHost.queueBind("testQueue1", "testExchange", "");
        Assertions.assertTrue(ok);

        ok = virtualHost.queueDeclare("testQueue2", false, false, false, null);
        Assertions.assertTrue(ok);
        ok = virtualHost.queueBind("testQueue2", "testExchange", "");
        Assertions.assertTrue(ok);

        // 往交换机中发布一个消息
        ok = virtualHost.basicPublish("testExchange", "", null, "hello".getBytes());
        Assertions.assertTrue(ok);

        Thread.sleep(500);

        // 两个消费者订阅上述的两个队列.
        ok = virtualHost.basicConsume("testConsumer1", "testQueue1", true, new Consumer() {
    
    
            @Override
            public void handleDelivery(String consumerTag, BasicProperties basicProperties, byte[] body) {
    
    
                System.out.println("consumerTag=" + consumerTag);
                System.out.println("messageId=" + basicProperties.getMessageId());
                Assertions.assertArrayEquals("hello".getBytes(), body);
            }
        });
        Assertions.assertTrue(ok);

        ok = virtualHost.basicConsume("testConsumer2", "testQueue2", true, new Consumer() {
    
    
            @Override
            public void handleDelivery(String consumerTag, BasicProperties basicProperties, byte[] body) {
    
    
                System.out.println("consumerTag=" + consumerTag);
                System.out.println("messageId=" + basicProperties.getMessageId());
                Assertions.assertArrayEquals("hello".getBytes(), body);
            }
        });
        Assertions.assertTrue(ok);

        Thread.sleep(500);
    }

    @Test
    public void testBasicConsumeTopic() throws InterruptedException {
    
    
        boolean ok = virtualHost.exchangeDeclare("testExchange", ExchangeType.topic, false, false, null);
        Assertions.assertTrue(ok);

        ok = virtualHost.queueDeclare("testQueue", false, false, false, null);
        Assertions.assertTrue(ok);

        ok = virtualHost.queueBind("testQueue", "testExchange", "aaa.*.bbb");
        Assertions.assertTrue(ok);

        ok = virtualHost.basicPublish("testExchange", "aaa.ccc.bbb", null, "hello".getBytes());
        Assertions.assertTrue(ok);

        ok = virtualHost.basicConsume("testConsumer", "testQueue", true, new Consumer() {
    
    
            @Override
            public void handleDelivery(String consumerTag, BasicProperties basicProperties, byte[] body) {
    
    
                System.out.println("consumerTag=" + consumerTag);
                System.out.println("messageId=" + basicProperties.getMessageId());
                Assertions.assertArrayEquals("hello".getBytes(), body);
            }
        });
        Assertions.assertTrue(ok);

        Thread.sleep(500);
    }

    @Test
    public void testBasicAck() throws InterruptedException {
    
    
        boolean ok = virtualHost.queueDeclare("testQueue", true,
                false, false, null);
        Assertions.assertTrue(ok);
        ok = virtualHost.exchangeDeclare("testExchange", ExchangeType.direct,
                true, false, null);
        Assertions.assertTrue(ok);

        // 先发送消息
        ok = virtualHost.basicPublish("testExchange", "testQueue", null,
                "hello".getBytes());
        Assertions.assertTrue(ok);

        // 再订阅队列 [要改的地方, 把 autoAck 改成 false]
        ok = virtualHost.basicConsume("testConsumerTag", "testQueue", false, new Consumer() {
    
    
            @Override
            public void handleDelivery(String consumerTag, BasicProperties basicProperties, byte[] body) {
    
    
                // 消费者自身设定的回调方法.
                System.out.println("messageId=" + basicProperties.getMessageId());
                System.out.println("body=" + new String(body, 0, body.length));

                Assertions.assertEquals("testQueue", basicProperties.getRoutingKey());
                Assertions.assertEquals(1, basicProperties.getDeliverMode());
                Assertions.assertArrayEquals("hello".getBytes(), body);

                // [要改的地方, 新增手动调用 basicAck]
                boolean ok = virtualHost.basicAck("testQueue", basicProperties.getMessageId());
                Assertions.assertTrue(ok);
            }
        });
        Assertions.assertTrue(ok);

        Thread.sleep(500);
    }
}

Guess you like

Origin blog.csdn.net/qq_56454895/article/details/132180278