RabbitMQ 笔记
来源:互联网 发布:南丹县政府网络问政 编辑:程序博客网 时间:2024/05/19 20:44
本文出自于EumJi个人博客,仅限于学习用途的转载,转载请注明出处http://www.eumji025.com/article/details/258142
前言
前面一节我们介绍如何编写一个发送和接收来自制定名称的队列。在本节中,我们将介绍如何创建一个消息队列供多个消费者使用。如下图所示。
工作队列介绍
工作队列(又称:任务队列),其主要思想是避免立即执行且需要等待完成的资源密集型任务。我们将后续完成的任务封装成一个消息,并将其发送到队列。在后台运行的工作进程将弹出任务并最终执行作业。当你运行很多消费者(worker)时,这些任务将在它们之间共享。
这个概念在Web应用程序中特别有用,在短时间HTTP请求窗口中无法处理复杂的任务。
代码展示
本节的代码都是基于hello world
案例进行稍微的改动。
生产者
我们对前面一节的hello world
的生产者进行扩展,主要是生产多条消息,以便供多个消费者使用。
主要代码如下:
//1.创建连接工厂 ConnectionFactory factory = new ConnectionFactory(); //2.配置工厂的RabbitMQ server的主机 factory.setHost("localhost"); Connection connection = null; Channel channel = null; try { //3.新建一个连接 connection = factory.newConnection(); //4.获取一个连接频道 channel = connection.createChannel(); /** * 5.配置一个队列的信息 */ channel.queueDeclare(QUEUE_WORK,true,false,false,null); for (int i = 0; i < 10; i++) { String message = getMessage()+"这是第"+i+"个!!!" ; /** * 6.将消息放在队列中(字节数组形式) * PERSISTENT_TEXT_PLAIN Content-type "text/plain", deliveryMode 2 (persistent), priority zero */ channel.basicPublish("",QUEUE_WORK, MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes()); System.out.println("product Sent message is: '" + message + "'"); } }catch(Exception e) { e.pringtStackTrace(); }finally { try { //7.关闭连接 channel.close(); connection.close(); } catch (IOException e) { logger.error("rabbitMQ关闭发生IO异常"); e.printStackTrace(); } catch (TimeoutException e) { logger.error("rabbitMQ发生关闭超时异常"); e.printStackTrace(); } } }
getMessage()方法,模拟设置消息。
channel.queueDeclare(QUEUE_WORK,true,false,false,null)的第二参数设置为true,表明将消息持久化。
主要代码如下:
private static String getMessage() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return String.valueOf(System.currentTimeMillis()); }
使用sleep方法模拟消息处理需要耗费一定的时间。
消费者
消费者基本和上一节雷同,主要是使用到了线程,以便模拟多个消费者。
//保证只有一个worker处理一个任务channel.basicQos(1);final Channel finalChannel = channel; Consumer consumer = new DefaultConsumer(finalChannel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { String message = new String(body, 0, body.length, "UTF-8"); System.out.println(workName+" "+Thread.currentThread().getName()+" get message:"+message); try { Thread.sleep(800); //消息处理完成确认 finalChannel.basicAck(envelope.getDeliveryTag(),false); System.out.println(workName+" "+Thread.currentThread().getName()+"处理消息完毕"); } catch (InterruptedException e) { e.printStackTrace(); } } }; //消息消费完成确认 channel.basicConsume(QUEUE_WORK,false,consumer);
上述代码主要是消费者的主要逻辑,相比上一节,主要多添加了消息公平分配和消息处理完成确认。下面来介绍这两个特性。
公平分配
我们在多线程中应该经常遇到,因为权重不同的问题,部分线程一直是空闲的,而部分线程却很忙碌。
RabbitMQ为了公平的工作,通过basicQos方法公平分配。
消息确认
执行任务可能需要一定的时间。如果一个消费者开始一个长期的任务,并且仅仅部分地完成它,会发生什么?使用我们当前的代码,一旦RabbitMQ向客户发送消息,它立即将其从内存中删除。在这种情况下,如果一个worker被杀死,我们将丢失正在处理的消息。我们还会丢失所有发送给该特定worker但尚未处理的消息。
但是我们不想失去任何任务。如果一个worker死亡,我们希望把这个任务交给另一个worker。
为了确保消息永远不会丢失,RabbitMQ支持消息确认。从消费者发送一个确认信息(告示)告诉RabbitMQ已经收到,处理了特定的消息,并且RabbitMQ可以自由删除它。
如果消费者死机(其通道关闭,连接关闭或TCP连接丢失),而不发送确认信息,RabbitMQ将会明白消息未被完全处理并重新排队。如果同时有其他消费者在线,则会迅速将其重新提供给另一个消费者。这样就可以确保没有消息丢失,即使worker偶尔出现问题。
消息确认默认情况下打开。在前面的例子中,我们通过autoAck = true 标志明确地将它们关闭。现在是一旦完成任务,将此标志设置为false,并向worker发送正确的确认。
测试
首先运行消费者
,等待消息
customer2 wait message!!!customer wait message!!!
然后运行生产者
,发送消息
product Sent message is: '1496553686995这是第0个!!!'product Sent message is: '1496553688001这是第1个!!!'product Sent message is: '1496553689001这是第2个!!!'product Sent message is: '1496553690002这是第3个!!!'product Sent message is: '1496553691003这是第4个!!!'
我们再观察消费者
customer2 pool-1-thread-4 get message:1496553686995这是第0个!!!customer2 pool-1-thread-4处理消息完毕customer pool-2-thread-4 get message:1496553688001这是第1个!!!customer2 pool-1-thread-5 get message:1496553689001这是第2个!!!customer pool-2-thread-4处理消息完毕customer2 pool-1-thread-5处理消息完毕customer pool-2-thread-5 get message:1496553690002这是第3个!!!customer pool-2-thread-5处理消息完毕customer2 pool-1-thread-6 get message:1496553691003这是第4个!!!
我们从上面的结果中看出,分配是公平的。
结语
本文是根据RabbitMQ的官方教程翻译并加以修改而成,如果存在什么不足和纰漏欢迎指正。
只是简单的介绍多个worker的工作队列的工作原理。
与君共勉!!!
参考资料
RabbitMQ文档-work queue
源码
work queue源码
- RabbitMQ笔记
- rabbitmq 笔记
- rabbitmq笔记
- rabbitmq笔记
- RabbitMQ 笔记
- RabbitMQ 笔记
- RabbitMQ 笔记
- RabbitMQ笔记
- RabbitMQ 学习笔记
- RabbitMQ概念笔记
- rabbitmq学习笔记一
- RabbitMQ使用笔记
- RabbitMQ学习笔记
- Rabbitmq学习笔记
- RabbitMQ学习笔记
- RabbitMQ 使用笔记
- rabbitmq学习笔记
- RabbitMQ学习笔记
- C#实现listview Group收缩扩展的方法
- LintCode题解(1)--A+B问题
- 解决ubuntu14.04系统没有声音的问题
- web.xml is missing and <failonmissingwebxml> is set to true
- B-1003
- RabbitMQ 笔记
- variational autoEncoder介绍
- Centos 7.3 Install LNMP
- Python中的logging模块
- Python函数式编程(高阶函数、map()、reduce()、filter()、sorted()、lambda、decorator装饰器)
- ++i和i++的区别
- 碰撞检测算法(2D矩形)
- matlab imfill函数
- HDU