RabbitMQ学习笔记(2)

RabbitMQ学习笔记(2)

1、工作队列Work Queues

由一个生产者进行生产,经由消息队列,被多个消费者消费。

发送端代码如下:

public class NewTask {
    private static final String QUEUE_NAME = "work_queue";
    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        String message = String.join(" ",args);
        channel.basicPublish("",QUEUE_NAME,null,message.getBytes());

        System.out.println(" [x] sent '"+ message + "'");

        channel.close();
        connection.close();
    }
}

接收端代码如下:

public class Worker {

    private final static String QUEUE_NAME = "work_queue";

    private static void doWork(String task) throws InterruptedException {
        for (char ch: task.toCharArray()) {
            if (ch == '.') Thread.sleep(10000);
        }
    }

    public static void main(String[] args) throws Exception{
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        System.out.println("[*] waiting for messages To exit Press CTRL + c");
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [x] Received '" + message + "'");
            try {
                doWork(message);
            }catch (InterruptedException e){

            }finally {
                System.out.println(" [x] Done");
            }
        };
        boolean autoAck = true; 
        channel.basicConsume(QUEUE_NAME, autoAck, deliverCallback, consumerTag -> { });
    }
}

2、代码测试

  • 启动两个work的main函数
  • 通过编辑NewTask的main函数参数,多次启动NewTask函数

idea工具如下:

运行结果如下:

结果显示,MQ容器把消息平均的分配给两个消费者进行了消费。

3、消息回执

自此,很容易想到,当消息(任务)传递到Queue之后,它立马会由某一个Worker进行标记。可能会有这种情形,如果某一个Worker处理Task耗时太久,或者该Worker被我们kill掉了。那么由他标记的消息将会丢失。

这种情形我们不想丢失消息,RabbitMQ提供了一种消息回执(Message Acknowledgment)机制。

boolean autoAck = true;//表示关闭消息回执

4、消息持久化

当Worker 挂掉之后正在处理的该条消息仍然会丢失。那么这个时候我们可以通过消息的持久化来处理这种情形。

boolean durable = true;
channel.queueDeclare("task_queue", durable, false, false, null);

channel.basicPublish("", "task_queue",
            MessageProperties.PERSISTENT_TEXT_PLAIN,
            message.getBytes());

*已经定义的队列不能更改他的属性为持久化,必须重新定义一个新的队列。

5、公平分发策略

由上面的打印图可以看出,MQ是把第n条消息轮流分给每个队列。如果每个队列处理消息的时间不一致,就会出现有的worker很闲,有的很忙。要充分利用能者多劳的机制。
加上:

int prefetchCount = 1;
channel.basicQos(prefetchCount);

最终代码:

public class NewTask {
    private static final String QUEUE_NAME = "work_queue";
    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        boolean durable = true ;
        channel.queueDeclare(QUEUE_NAME,durable,false,false,null);
        String message = String.join(" ",args);
        channel.basicPublish("", "task_queue",
                MessageProperties.PERSISTENT_TEXT_PLAIN,
                message.getBytes());
        System.out.println(" [x] sent '"+ message + "'");
        channel.close();
        connection.close();
    }
}

public class Worker {

    private final static String QUEUE_NAME = "work_queue";

    private static void doWork(String task) throws InterruptedException {
        for (char ch: task.toCharArray()) {
            if (ch == '.') Thread.sleep(10000);
        }
    }

    public static void main(String[] args) throws Exception{
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.basicQos(1);
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        System.out.println("[*] waiting for messages To exit Press CTRL + c");
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" [x] Received '" + message + "'");
            try {
                doWork(message);
            }catch (InterruptedException e){

            }finally {
                System.out.println(" [x] Done");
            }
        };
        boolean autoAck = false;//true表示关闭消息回执
        channel.basicConsume(QUEUE_NAME, autoAck, deliverCallback, consumerTag -> { });
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值