消息队列 (7)- 虚拟主机的设计

博客围绕虚拟主机展开,此前已将队列、交换机等存入数据库并实现消息持久化存储。介绍了虚拟主机的设计,采用交换机名字等于虚拟主机名字加交换机名字的方案。实现部分创建了virtualHost类管理相关内容,设计了属性和核心方法,还给出确认ACK的代码,最后提及虚拟主机测试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

之前我们已经将,队列,交换机,绑定,存储到了数据库中并测试完毕,而且也将消息存入到了文件中,实现了消息的持久化存储,并且我们将这些都写入了内存,而且还提供了一个给上层调用的接口。而这些内容都是在中间人服务器中的,接下来我们编写重要的一个类,虚拟主机:虚拟主机类似于数据库,不仅仅要管理数据,还要提供一些核心的API供上层调用。主要流程就是将虚拟主机与之前写过的API串联起来

虚拟主机的设计

如何表示交换机与虚拟主机之间的从属关系呢?
假设1:参考数据库的设计,实现一对多的方案,
假设2:重新约定,交换机的名字 = 虚拟主机的名字+交换机的名字;
虚拟主机的目的是为了保证,不同虚拟主机之间的内容互不影响。在这里我们采取假设2的实现,比较简单

实现

创建virtualHost类,在这个类中对交换机,队列,绑定,消息进行管理,同时处理异常
对于虚拟主机我们设计以下属性
主机名称 : virtuaHostName
内存控制类:MemoryDataCenter
硬盘控制类:DiskDataCenter
交换机转发规则类:Router(未实现)
消费者的核心功能:ConsumerManager(未实现)

这里说一下锁:因为我们接下来是对硬盘和文件来进行写入的,所以为了防止多线程情况下出现问题,我们统一加上锁,这里我们设计俩个锁对象

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

核心方法

  1. 初始化:初始化内存控制类,与硬盘控制类
 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. 创建交换机
 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. 删除交换机
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. 创建队列
  // 创建队列
    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. 删除队列
  // 删除队列
    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. 创建绑定
 // 确认消息的编写
    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. 删除绑定
 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. 订阅消息
   // 订阅消息.
    // 添加一个队列的订阅者, 当队列收到消息之后, 就要把消息推送给对应的订阅者.
    // 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. 确认ACK

public boolean basicAck(String queueName, String messageId) {
queueName = virtuaHostName + queueName;
try {
// 1. 获取到消息和队列
Message message = memoryDataCenter.getMessage(messageId);
if (message == null) {
throw new MqException(“[VirtualHost] 要确认的消息不存在! messageId=” + messageId);
}
MSGQueue queue = memoryDataCenter.getQueue(queueName);
if (queue == null) {
throw new MqException(“[VirtualHost] 要确认的队列不存在! queueName=” + queueName);
}
// 2. 删除硬盘上的数据
if (message.getDeliverMode() == 2) {
diskDataCenter.deleteMessage(queue, message);
}
// 3. 删除消息中心中的数据
memoryDataCenter.removeMessage(messageId);
// 4. 删除待确认的集合中的数据
memoryDataCenter.removeMessageWaitAck(queueName, messageId);
System.out.println(“[VirtualHost] basicAck 成功! 消息被成功确认! queueName=” + queueName
+ “, messageId=” + messageId);
return true;
} catch (Exception e) {
System.out.println(“[VirtualHost] basicAck 失败! 消息确认失败! queueName=” + queueName
+ “, messageId=” + messageId);
e.printStackTrace();
return false;
}
}

  1. 确认消息
// 确认消息的编写
    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());

    }

虚拟主机测试

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);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值