rabbitmq HelloWorld 做了一个简单的发送和接收消息(http://blog.csdn.net/convict_eva/article/details/52291774)。
工作队列(又称:任务队列——Task Queues)是为了避免等待一些占用大量资源、时间的操作。当我们把任务(Task)当作消息发送到队列中,一个运行在后台的工作者(worker)进程就会取出任务然后处理。当你运行多个工作者(workers),任务就会在它们之间共享。
这个概念在网络应用中是非常有用的,它可以在短暂的HTTP请求中处理一些复杂的任务。
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";
//设置和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
如果没有指定消息持久化,那么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)。
工作队列(又称:任务队列——Task Queues)是为了避免等待一些占用大量资源、时间的操作。当我们把任务(Task)当作消息发送到队列中,一个运行在后台的工作者(worker)进程就会取出任务然后处理。当你运行多个工作者(workers),任务就会在它们之间共享。
这个概念在网络应用中是非常有用的,它可以在短暂的HTTP请求中处理一些复杂的任务。
准备:
使用Thread.sleep()模拟耗时操作,发送消息中有一个 . 接收消息就sleep() 1秒。工程搭建:
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