rabbitmq 工作队列(java 实现)

rabbitmq HelloWorld 做了一个简单的发送和接收消息(http://blog.csdn.net/convict_eva/article/details/52291774)。

工作队列(又称:任务队列——Task Queues)是为了避免等待一些占用大量资源、时间的操作。当我们把任务(Task)当作消息发送到队列中,一个运行在后台的工作者(worker)进程就会取出任务然后处理。当你运行多个工作者(workers),任务就会在它们之间共享。
这个概念在网络应用中是非常有用的,它可以在短暂的HTTP请求中处理一些复杂的任务。


准备:

    使用Thread.sleep()模拟耗时操作,发送消息中有一个 . 接收消息就sleep() 1秒。

工程搭建:

        


    RabbitmqConfigure 配置:
        public class RabbitmqConfigure {
            //队列名称
            public final static String QUEUE_NAME = "task_queue";
            //是否是持久化的queue
            public final static boolean DURABLE=true; 
            //受当前连接限制的queue,true:当与消费者(consumer)断开连接的时候,这个队列应当被立即删除。
            public final static boolean EXCLUSIVE=false;
            //是否自动删除,如果长时间没有用到自动删除
            public final static boolean AUTOD_ELETE=false;
            //自动确认消息,   false 不自动确认,要手动确认消息
            public final static boolean AUTOACK = false;
            public final static String HOST = "192.168.174.128";
            //用户要提前创建
            public final static String PASS_WORD="convict_eva";
            public final static String USER_NAME="convict_eva";
            // virtual host 要提前创建  /convict_eva
            public final static String VIRTUAL_HOST="/convict_eva";

        }


    Recv 消费者,消费消息


        public class Recv {

            public static void main(String[] args)  throws Exception{
                //设置和send 相同,
                ConnectionFactory factory = new ConnectionFactory();
                factory.setHost(RabbitmqConfigure.HOST);
                factory.setPassword(RabbitmqConfigure.PASS_WORD);
                factory.setUsername(RabbitmqConfigure.USER_NAME);
                factory.setVirtualHost(RabbitmqConfigure.VIRTUAL_HOST);
                //打开connection 和 channel
                Connection connection = factory.newConnection();
                final Channel channel = connection.createChannel();
                /**
                 * 声明要消费的queue。可能消费都先被执行,在消费消息之前要确保queue存在。
                 * RabbitmqConfigure.DURABLE 设置消息持久化,设置了持久化也不能保证持久化
                 *
                 */
                channel.queueDeclare(RabbitmqConfigure.QUEUE_NAME, RabbitmqConfigure.DURABLE, RabbitmqConfigure.EXCLUSIVE, RabbitmqConfigure.AUTOD_ELETE, null);
                //公平调度:告诉RabbitMQ,再同一时刻,不要发送超过1条消息给一个工作者(worker),直到它已经处理了上一条消息并且作出了响应。
                channel.basicQos(1);
                System.out.println(" [*] Waiting for messages.");
                /**
                 * consumer 接收消息回调方法,DefaultConsumer提供一个方法可以缓存发送的消息,直到消息被消费。
                 */
                final Consumer consumer = new DefaultConsumer(channel) {
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
                            throws IOException {
                        String message = new String(body, "UTF-8");
                        System.out.println("Received '" + message + "'");
                        try {
                            doWork(message);
                        }catch(Exception e){
                            e.printStackTrace();
                        } finally {
                            System.out.println("basicAck  Done");
                            //手动确认
                            channel.basicAck(envelope.getDeliveryTag(), false);
                        }
                    }
                };
                //设置自动确认为false
                channel.basicConsume(RabbitmqConfigure.QUEUE_NAME,  RabbitmqConfigure.AUTOACK, consumer);
            }
            private static void doWork(String task) throws InterruptedException {
                for (char ch: task.toCharArray()) {
                    if (ch == '.') Thread.sleep(1000);
                }
            }
        }
    RecvB 是Recv的复制的,模拟两个消费者。
    Send  生产者,发送消息:
        public class Send {
            public static void main(String[] argv) throws Exception{
                ConnectionFactory factory = new ConnectionFactory();
                factory.setHost(RabbitmqConfigure.HOST);
                factory.setPassword(RabbitmqConfigure.PASS_WORD);
                factory.setUsername(RabbitmqConfigure.USER_NAME);
                //VirtualHost 要在控制台提前创建
                factory.setVirtualHost(RabbitmqConfigure.VIRTUAL_HOST);
                Connection connection = factory.newConnection();
                Channel channel = connection.createChannel();
                /**
                 * 声明queue
                 * RabbitmqConfigure.DURABLE 为true 持久化队列
                 */
                channel.queueDeclare(RabbitmqConfigure.QUEUE_NAME, RabbitmqConfigure.DURABLE,
                        RabbitmqConfigure.EXCLUSIVE, RabbitmqConfigure.AUTOD_ELETE, null);
                for(int i=0;i<10;i++){
                    String message = "hello...."+i;
                    //发送消息,设置消息为持久化消息:MessageProperties.PERSISTENT_TEXT_PLAIN(deliveryMode=2)
                    channel.basicPublish("", "task_queue", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
                    System.out.println(" [x] Sent '" + message + "'");
                }
                channel.close();
                connection.close();
            }
        }
    
循环调度:
    使用工作队列的一个好处就是它能够并行的处理队列。如果堆积了很多任务,我们只需要添加更多的工作者(workers)就可以了,扩展很简单。
    默认来说,RabbitMQ会按顺序得把消息发送给每个消费者(consumer)。平均每个消费者都会收到同等数量得消息。这种发送消息得方式叫做——轮询(round-robin)。
    先运行 Recv 和 RecvB,再运行Send。消息在Recv 和 RecvB中平均消费。

消息确认:
    一个worker 正在处理一个耗时的任务,此时如果机器宕机或者进程被kill,那么这个消息就没有处理完,就要求消息重发。
    rabbitmq 消息确认机制默认是自动确认的。Recv    channel.basicConsume(RabbitmqConfigure.QUEUE_NAME,  true, consumer);第二个参数设置为true,自动确认消息。
    修改手动提交(消费者Recv):
        1、绑定consumer 设置autoAck 为false: channel.basicConsume(RabbitmqConfigure.QUEUE_NAME,  RabbitmqConfigure.AUTOACK, consumer);
        2、手动确认消息:channel.basicAck(envelope.getDeliveryTag(), false);

    忘记确认的消息会在你的程序退出之后就会重新发送,如果不能够释放没响应的消息,RabbitMQ就会占用越来越多的内存。
    可以通过管理页面看到没有确认的消息,也可以通过命令查看:./rabbitmqctl list_queues task_queue messages_ready messages_unacknowledged

    测试:启动 Recv和RecvB等待消费消息,启动Send 发送消息。

      每个消息会运行4秒(sleep() 4秒),如下图,当在消费“hello....5”消息时,强制停止运行。

 

停止后,Recv消费消息如下:

               

           没有确认的消息“hello....5”,被重发到Recv worker 消费。

                  

消息持久化:
    如果没有指定消息持久化,那么rabbitmq 在退出或者重启的时候会丢失所有的队列和消息。
    指定消息持久化必须把“队列”和“消息”都设置为持久化。
    Send:
        1、
            /**
            * 声明queue
            * RabbitmqConfigure.DURABLE 为true 持久化队列,在RabbitMq重启之后队列不会丢失
            */
            channel.queueDeclare(RabbitmqConfigure.QUEUE_NAME, RabbitmqConfigure.DURABLE,RabbitmqConfigure.EXCLUSIVE, RabbitmqConfigure.AUTOD_ELETE, null);
            注意:Send 和 Recv 都要声明为持久化的队列
        2、//发送消息,设置消息为持久化消息:MessageProperties.PERSISTENT_TEXT_PLAIN(deliveryMode=2)
            channel.basicPublish("", "task_queue", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
            
        测试:
            发送消息后,不运行消费者。重启rabbitmq,队列和消息都不丢失。
公平调度:
    Recv:
    //公平调度:告诉RabbitMQ,再同一时刻,不要发送超过1条消息给一个工作者(worker),直到它已经处理了上一条消息并且作出了响应。
    //这样,RabbitMQ就会把消息分发给下一个空闲的工作者(worker)。

    channel.basicQos(1);


rabbitmq 官网:http://www.rabbitmq.com/tutorials/tutorial-two-java.html

rabbitmq 中文:http://rabbitmq.mr-ping.com/tutorials_with_python/[2]Work_Queues.html

工程代码:http://download.csdn.net/detail/convict_eva/9610778



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值