rabbitmq个人笔记

记录自己入门rabbitmq的笔记。下载安装好rabbitmq之后,打开http://localhost:15672/账号和密码都是guest,然后创建新的用户user_mmr,然后创建新的vhost为/vhost_mmr,然后就可以进行如下代码的编写。

详细代码可见:https://github.com/gzh1026/Rabbit_Demo

创建与rabbitmq的连接:

public class ConnectionUtils {
    //获取mq链接
    public static Connection getConnection() throws IOException, TimeoutException {
        //定义连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //设置服务地址
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/vhost_mmr");
        connectionFactory.setUsername("user_mmr");
        connectionFactory.setPassword("123");
        return connectionFactory.newConnection();
    }
}

simple模式

img

生产者产生消息,然后将消息放入队列内,消费者consumer监听队列,如果有消息,则拿走,然后消息自动从队列中删除,(隐患:消息可能没有被消费者正确处理,已经从队列中消失了,造成消息的丢失,这里可以设置成手动的ack,但如果设置成手动ack,处理完后要及时发送ack消息给队列,否则会造成内存溢出)

Send:

public class Send {
    private static final String QUEUE_NAME = "test_simple";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        String msg = "hello world";
        channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
        System.out.println("send msg..."+msg);
        channel.close();
        connection.close();
    }
}

消费者:

public class Recv {
    private static final String TEST_NAME = "test_simple";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(TEST_NAME,false,false,false,null);
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
                String msgstring = new String(body, StandardCharsets.UTF_8);
                System.out.println("new recv is  [  " + msgstring+"  ]");
            }
        };
        channel.basicConsume(TEST_NAME,true,consumer);
    }
}

work模式(资源竞争)

消息产生者将消息放入队列消费者可以有多个,消费者1,消费者2同时监听同一个队列,消息被消费。C1 C2共同争抢当前的消息队列内容,谁先拿到谁负责消费消息(隐患:高并发情况下,默认会产生某一个消息被多个消费者共同使用,可以设置一个开关(syncronize) 保证一条消息只能被一个消费者使用)。

img

多消费者启动顺序为:先启动所有消费者,然后启动生产者。

生产者:

//发布50条消息进队列
public class send {
    private static final String QUEUE_NAME="test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel=connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        for (int i = 0; i < 50; i++) {
            String msg="hello "+i;
            System.out.println("send [  "+msg+"  ]");
            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
            Thread.sleep(i );
        }
        channel.close();
        connection.close();
    }
}

轮询方式(round-robin)

生产者将消息放到队列后,消费者有多个,依次拿消息,你一个,我一个,我们不争不抢。

消费者1(设置线程休眠时间为500ms):

public class Recv1 {
    private static final String QUEUE_NAME="test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        Consumer Consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgstring = new String(body, StandardCharsets.UTF_8);
                System.out.println("[1] --- new recv is  [  " + msgstring+"  ]");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("[1] --- done");
                }
            }
        };
        channel.basicConsume(QUEUE_NAME,true,Consumer);
    }
}

消费者2(设置线程休眠时间为1000ms):

public class Recv2 {
    private static final String QUEUE_NAME="test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        Consumer Consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgstring = new String(body, StandardCharsets.UTF_8);
                System.out.println("[2] --- new recv is  [  " + msgstring+"  ]");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("[2] --- done");
                }
            }
        };
        channel.basicConsume(QUEUE_NAME,true,Consumer);
    }
}

运行结果为

image-20200715232558045

image-20200715232612220

image-20200715232640656

公平分发(fair dispatch)

与轮询不同的是,轮询是无论你性能如何,都依次来,一人一个,但是公平分发有点负载均衡的意思,你干得快,你就多接收几个消息,他干的慢,那他就少接收。充分利用消费者。公平分发需要设置 **channel.basicQos(perferchCount);**就是控制消息发送给消费者数量,如果设置为1,表示消费者只能收到一条消息,处理完后,才能继续处理下一条消息。该消费者在接收到队列里的消息但没有返回确认结果之前,队列不会将新的消息分发给该消费者。队列中没有被消费的消息不会被删除,还是存在于队列中。使用此模式还需要关闭自动应答。改为手动应答。即,改成如下:

boolean autoAck=false
channel.basicConsume(QUEUE_NAME,autoAck,Consumer);

boolean autoAck=true(自动确认模式)一旦rabbitmq将消息分发给消费者,就会从内存中删除,这种情况下,如果杀死正在执行的消费者,就会丢失正在处理的消息

boolean autoAck=false(手动模式)如果有一个消费者挂掉,就会交付给其他消费者,rabbitmq支持消息应答,消费者发送一个消息应答,告诉rabbitmq这个消息我已经处理完成,你可以删了,然后rabbitmq删除内存中的消息。

如果rabbitmq挂了,我们的消息仍然会丢失

Send:

public class send {
    private static final String QUEUE_NAME="test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel=connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        int perferchCount=1;
        channel.basicQos(perferchCount);
        for (int i = 0; i < 50; i++) {
            String msg="hello "+i;
            System.out.println("send [  "+msg+"  ]");
            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
            Thread.sleep(i );
        }
        channel.close();
        connection.close();
    }
}

Recv1:

public class Recv1 {
    private static final String QUEUE_NAME="test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel  channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        channel.basicQos(1);
        Consumer Consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgstring = new String(body, StandardCharsets.UTF_8);
                System.out.println("[1] --- new recv is  [  " + msgstring+"  ]");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("[1] --- done");
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        channel.basicConsume(QUEUE_NAME,false,Consumer);
    }
}

Recv2:

public class Recv2 {
    private static final String QUEUE_NAME="test_work_queue";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        channel.basicQos(1);
        Consumer Consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgstring = new String(body, StandardCharsets.UTF_8);
                System.out.println("[2] --- new recv is  [  " + msgstring+"  ]");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("[2] --- done");
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        channel.basicConsume(QUEUE_NAME,false,Consumer);
    }
}

运行结果如下:

image-20200715234528525

image-20200715234551539

image-20200715234602707

消息持久化

boolean durable=false;
channel.queueDeclare(QUEUE_NAME, durable, false, false, null);

不可以将程序中false改为true,尽管代码正确,也不会运行成功,因为我们已经定义了test_word_queue,这个queue是未持久化的,rabbitmq不允许重新定义一个(不同参数)已存在的队列

订阅模式publish/subscribe

image-20200715222116836

解读:

  • 一个生产者,多个消费者
  • 每个消费者都有自己的队列
  • 生产者没有直接把消息发送到队列,而是发到了交换机 转发器 exchange
  • 每个队列都要绑定到交换机上
  • 生产者发送的消息,经过交换机,到达队列,就能实现一个消息被多个消费者消费

注册->邮件->短信

Send:

public class Send {
    private static final String exchang_name = "test_exchang_fanout";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(exchang_name, "fanout");
        String msg = "hello ps";
        channel.basicPublish(exchang_name, "", null, msg.getBytes());
        System.out.println("send ... :"+msg);
        channel.close();
        connection.close();
    }
}

image-20200715220142469

消息丢失了,因为交换机没有存储的能力,rabbitmq只有队列有存储的能力,这个时候没有队列绑定到这个交换机,所以数据丢失了。

消费者1:

public class Recv1 {
    private static final String QUEUE_NAME = "text_queue_fanout_email";
    private static final String exchang_name = "test_exchang_fanout";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //队列声明
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //绑定队列到交换机
        channel.queueBind(QUEUE_NAME, exchang_name, "");
        channel.basicQos(1);
        Consumer Consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgstring = new String(body, StandardCharsets.UTF_8);
                System.out.println("[1] --- new recv is  [  " + msgstring+"  ]");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("[1] --- done");
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        boolean autoACK=false;
        channel.basicConsume(QUEUE_NAME, false,Consumer);
    }
}

消费者2:

public class Recv2 {
    private static final String QUEUE_NAME = "text_queue_fanout_sms";
    private static final String exchang_name = "test_exchang_fanout";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //队列声明
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //绑定队列到交换机
        channel.queueBind(QUEUE_NAME,exchang_name,"");
        channel.basicQos(1);
        Consumer Consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgstring = new String(body, StandardCharsets.UTF_8);
                System.out.println("[2] --- new recv is  [  " + msgstring+"  ]");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("[2] --- done");
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        boolean autoACK=false;
        channel.basicConsume(QUEUE_NAME, false,Consumer);
    }
}

image-20200715222001671

交换机/转发器exchange

一方面是接收生产者的消息,另一方面是向队列推送消息

匿名转发“”

fanout(不处理路由键)

一个发送到exchange的消息都会被转发到与该exchange绑定的所有队列上

img

direct

发送的时候,需要带一个路由的key,如果绑定的队列,有路由key相匹配,就会把消息转到相应的队列。

img

image-20200716004147816

当发送error这个路由key的时候,两个队列都可以接收,当只发送info或者warning时,只有下面那个队列可以接收

Send:

public class Send {
    private static final String exchange_name = "TEST_EXCHANG_DIRECT";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(exchange_name, "direct");
        String msg = "hello direct";
        System.out.println(msg+" have sent...");
        //路由key是error
        String routingKey = "info";
        channel.basicPublish(exchange_name, routingKey, null, msg.getBytes());
        channel.close();
        connection.close();
    }
}

消费者1:

public class Recv1 {
    private static final String exchange_name = "TEST_EXCHANG_DIRECT";
    private static final String queue_name = "TEST_QUEUE_DIRECT";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(queue_name, false, false, false, null);
        channel.basicQos(1);
        channel.queueBind(queue_name, exchange_name, "error");
        Consumer Consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgstring = new String(body, StandardCharsets.UTF_8);
                System.out.println("[1] --- new recv is  [  " + msgstring+"  ]");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("[1] --- done");
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        boolean autoACK=false;
        channel.basicConsume(queue_name, false,Consumer);
    }
}

消费者2:

public class Recv2 {
    private static final String exchange_name = "TEST_EXCHANG_DIRECT";
    private static final String queue_name = "TEST_QUEUE_DIRECT--2";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(queue_name, false, false, false, null);
        channel.basicQos(1);
        channel.queueBind(queue_name, exchange_name, "error");
        channel.queueBind(queue_name, exchange_name, "info");
        channel.queueBind(queue_name, exchange_name, "warning");
        Consumer Consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgstring = new String(body, StandardCharsets.UTF_8);
                System.out.println("[2] --- new recv is  [  " + msgstring+"  ]");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("[2] --- done");
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        boolean autoACK=false;
        channel.basicConsume(queue_name, false,Consumer);
    }
}

最主要的差别就是Send中,我们规定了路由key为

String routingKey = "info";
channel.basicPublish(exchange_name, routingKey, null, msg.getBytes());

消费者1中,队列所绑定的路由key为"error"

channel.queueBind(queue_name, exchange_name, "error");

消费者2中,队列所绑定的路由key为"error",“info”,“warning”

channel.queueBind(queue_name, exchange_name, "error");
channel.queueBind(queue_name, exchange_name, "info");
channel.queueBind(queue_name, exchange_name, "warning");

执行结果为:

  • 当我们的routingKey为"info",发送消息时,只有消费者2可以接收。
  • 当我们的routingKey为"error",发送消息时,消费者1和消费者2都可以接收。

Topic exchange:

将路由键与某模式匹配。

img

Topic 模式

  1. 星号井号代表通配符

  2. 星号代表多个单词,井号代表一个单词

  3. 路由功能添加模糊匹配

  4. 消息产生者产生消息,把消息交给交换机

  5. 交换机根据key的规则模糊匹配到对应的队列,由队列的监听消费者接收消息消费

Send:

public class Send {
    private static String Exchange_NAME = "test_exchange_topic";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(Exchange_NAME,"topic");
        String msg="商品";
        channel.basicPublish(Exchange_NAME, "goods.add", null, msg.getBytes());
        System.out.println("send---" + msg);
        channel.close();
        connection.close();
    }
}

消费者1:

public class Recv1 {
    private static final String exchange_name = "test_exchange_topic";
    private static final String queue_name = "TEST_QUEUE_topic_1";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(queue_name, false, false, false, null);
        channel.basicQos(1);
        channel.queueBind(queue_name, exchange_name, "goods.add");
        Consumer Consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgstring = new String(body, StandardCharsets.UTF_8);
                System.out.println("[1] --- new recv is  [  " + msgstring+"  ]");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("[1] --- done");
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        boolean autoACK=false;
        channel.basicConsume(queue_name, false,Consumer);
    }
}

消费者2:

public class Recv2 {
    private static final String exchange_name = "test_exchange_topic";
    private static final String queue_name = "TEST_QUEUE_topic_2";
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(queue_name, false, false, false, null);
        channel.basicQos(1);
        channel.queueBind(queue_name, exchange_name, "goods.#");
        Consumer Consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgstring = new String(body, StandardCharsets.UTF_8);
                System.out.println("[1] --- new recv is  [  " + msgstring+"  ]");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("[1] --- done");
                    channel.basicAck(envelope.getDeliveryTag(),false);
                }
            }
        };
        boolean autoACK=false;
        channel.basicConsume(queue_name, false,Consumer);
    }
}

rabbitmq的消息确认机制(事务+confirm)

在rabbitmq中,我们可以通过持久化数据,解决rabbitmq服务器异常的数据丢失问题。

问题:生产者将消息发送出去之后,消息到底有没有到达rabbitmq服务器,默认是不知道的。

两种方法:

  • AMQP实现了事务机制
  • confirm模式

事务机制

txSelect txCommit txRolllback

txSelect:用户将当前channel设置为transation模式

txCommit:用于提交事务

txRollback:回滚事务

txSend:

public class Send {
    private static final String QUEUE_NAME = "test_queue_tx";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        String msg = "hello tx";
        try {
            channel.txSelect();
            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
            channel.txCommit();
        } catch (IOException e) {
            channel.txRollback();
            System.out.println("error massage txRollback");
        }
        channel.close();
        connection.close();
    }
}

消费者:

public class Recv {
    private static final String QUEUE_NAME = "test_queue_tx";

    public static void main(String[] args) throws IOException, TimeoutException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        channel.basicConsume(QUEUE_NAME,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msgstring = new String(body, StandardCharsets.UTF_8);
                System.out.println("[tx] --- new recv is  [  " + msgstring+"  ]");
            }
        });
    }
}

Confirm模式

生产者端confirm模式的实现原理

生产者将信道设置成 confirm模式,一旦信道进入 confirm模式,所有在该信道上面发布的消息都会被指派一个唯一的ID(从1开始)一旦消息被投遥到所有匹配的队列之后,broker就会发送一个确认给生产者(包含消息的唯-ID),这就使得生产者知道消息已经正确到达目的队列了,如果消息和队列是可持久化的,那么确认消息会将消息写入磁盘之后发出, broker回传给生产者的确认消息。中 deliver-tag域包含了确认消息的序列号,此外broker也可以设置 basic.ack的 multiple城,表示到这个序列号之前的所有消息都已经得到了处理。

confirm最大好处是,异步

开启confirm模式:

  • channel.ConfirmSelect()

    编程模式

    1. 普通 发一条,waitforconfirms()
    2. 批量的 发一批 waitforconfirms()
    3. 异步 confirm模式 提供一个回调方法
confirm单条:
public class Send {
    private static final String QUEUE_NAME = "test_queue_confirm";
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //生产者调用,将channel设为confirm模式
        channel.confirmSelect();
        String msg = "hello confirm";
        channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
        if (!channel.waitForConfirms()) {
            System.out.println("massage send failed");
        }
        else{
            System.out.println("send ok");
        }
        channel.close();
        connection.close();
    }
}
confirm批量
public class Send {
    private static final String QUEUE_NAME = "test_queue_confirm";
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //生产者调用,将channel设为confirm模式
        channel.confirmSelect();
        String msg = "hello confirm batch";
        //批量发送
        for (int i = 0; i < 20; i++) {
            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
        }
        //确认
        if (!channel.waitForConfirms()) {
            System.out.println("massage send failed");
        }
        else{
            System.out.println("send ok");
        }
        channel.close();
        connection.close();
    }
}
异步模式

・异步模式
Channel对象提供的 Confirmlistener()回调方法只包含 delivery Tag(当前 Chanel发出的消息序号),我们需要自己为每一个 Channe維护一个 unconfirm的消息序号集合,每publish一条数据,集合中元素加1,毎回调一次 handlealck方法, confirm集合朋掉相应的一条( multiple= false)或多条( multiple=tue)记录。从程序运行效率上看,这个 unconfirm集合最好采用有序集合 Sortedset存储结构

hannel设为confirm模式
channel.confirmSelect();
String msg = “hello confirm batch”;
//批量发送
for (int i = 0; i < 20; i++) {
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
}
//确认
if (!channel.waitForConfirms()) {
System.out.println(“massage send failed”);
}
else{
System.out.println(“send ok”);
}
channel.close();
connection.close();
}
}


#### 异步模式

・异步模式
Channel对象提供的 Confirmlistener()回调方法只包含 delivery Tag(当前 Chanel发出的消息序号),我们需要自己为每一个 Channe維护一个 unconfirm的消息序号集合,每publish一条数据,集合中元素加1,毎回调一次 handlealck方法, confirm集合朋掉相应的一条( multiple= false)或多条( multiple=tue)记录。从程序运行效率上看,这个 unconfirm集合最好采用有序集合 Sortedset存储结构

这个待更,了解不是很清楚。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值