Hello Word
/*
* 连接RabbitMQ
*/
public Connection getConn() throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
//创建连接
Connection connection = connectionFactory.newConnection();
return connection;
}
/*
* 发送消息
*/
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("localhost");
try {
Connection connection = connectionFactory.newConnection();
//创建通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare("MyQUEUE",false, false, false, null);
String message = "my message is LiGuangXi";
//发布消息
channel.basicPublish("", "MyQUEUE", null, message.getBytes("UTF-8"));
System.out.println("sent is up and message" + message);
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 消费者 消费消息
*/
public static void main(String[] args) {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try {
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
/*
* 请注意,我们也在这里声明了队列。因为我们可能在发布者之前启动消费者,所以我们希望在尝试使用消息之前确保队列存在。
* 声明队列:
* para1.队列名字
* para2.是否持久化(rabbirmq不允许修改已存在的队列状态,修改时需要创建新的队列)重启服务也不会都是消息
* para3.独有的
* para4.自动删除
* para5.Map<String, Object>参数
* */
channel.queueDeclare("MyQUEUE", false, false, false, null);
/*
* 我们即将告诉服务器从队列中传递消息。因为它会异步地向我们发送消息,
* 所以我们以对象的形式提供一个回调,它将缓冲消息,直到我们准备好使用它们。这就是DeliverCallback子类的作用。
* */
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" 接受消息: '" + message + "'");
};
channel.basicConsume("MyQUEUE", true, deliverCallback, consumerTag -> { });
} catch (Exception e) {
e.printStackTrace();
}
}
消息队列
工作队列(任务队列):背后的主要思想是避免立即执行资源密集型任务,并且必须等待它完成。相反,我们安排任务稍后完成。我们将任务封装为消息并将其发送到队列。在后台运行的工作进程将弹出任务并最终执行作业。当你运行许多工作程序时,他们之间将共享任务。
这里出现了一个小插曲 由于一个空格的原因我调试了将近2个小时
循环调度(默认)
rabbitmq 默认情况下是按顺序将任务推送到下一个消费者。平均而言,每个消费者将获得相同数量的消息。这种分发机制称之为循环法。
// 发送者
public static void main(String[] args) throws Exception {
Connection conn = connUtil.getConn();
Channel channel = conn.createChannel();
channel.queueDeclare(queuesName,false,false,false,null);
for ( int i =0 ; i < 10; i++ ){
Thread.sleep(1000);// 假装它很忙!!
//允许从命令行发送任意消息,将任务放到我们的工作队列中
String message = i+"test value";
channel.basicPublish("", queuesName,null,message.getBytes());
System.out.println("发送:"+message);
}
channel.close();
conn.close();
}
// 接收者
//启动多个 可以看到rabbitmq 默认情况下是按顺序将任务推送到下一个消费者。平均而言,每个消费者将获得相同数量的消息。这种分发机制称之为循环法。
public static void main(String[] args) throws Exception {
Connection conn = connUtil.getConn();
Channel channel = conn.createChannel();
channel.queueDeclare(quenename, false, false, false, null);
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" 接受请求: '" + message + "'");
};
//手动消息确认关闭。
boolean autoAck = true;
channel.basicConsume(quenename, autoAck, deliverCallback, consumerTag -> {});
}
消息确认
为了确保消息永不丢失,RabbitMQ支持确认消息。消息者发送ACK(nowledgement)告诉RabbitMQ已经收到了,处理了特定消息,RabbitMQ可以自由的删除它。
如果消费者死亡(其通道关闭,连接关闭或TCP连接丢失)而不能正确发送的,RabbitMQ将理解消息未完全处理并将重新排队。如果同时有其他在线消费者,则会迅速将其重新发送给其他消费者。
默认情况下 手动消息确认已打开。上面代码中 autoACK=true 标志明确关闭了。
public static void main(String[] args) throws Exception {
Connection conn = connUtil.getConn();
Channel channel = conn.createChannel();
channel.queueDeclare(quenename, false, false, false, null);
//告诉rabbitMQ一次只能向一个工作者发送一条消息,在处理并确认前一个消息之前,不要给工作着发送新消息
// 一次只能接收一个未包装的消息
channel.basicQos(1);
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" 接受请求: '" + message + "'");
try{
doWork(message);
}catch (Exception e){
e.printStackTrace();
System.out.println("异常信息");
}finally {
System.out.println("Done");
//确认必须在收到交付的同一渠道上发送。尝试使用不同的通道进行确认将导致通道协议异常。
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
};
//打开手动确认消息
boolean autoAck = false;
channel.basicConsume(quenename, autoAck, deliverCallback, consumerTag -> {});
}
private static void doWork(String task) throws Exception {
for (char ch : task.toCharArray()){
if(ch == '。')
Thread.sleep(1000);
}
}
确认必须在收到交付的同一渠道上发送。尝试使用不同的通道进行确认将导致通道协议异常
消息持久性
RabbitMQ退出或者崩溃后,数据将会丢失,除非你这样做。
1.将队列声明成持久性的
首先保证我的队列是持久性的,为此我们需要声明一个持久性的队列
rabbitmq 不允许你使用不同的参数重新定义一个现有的对列,并且会向试图执行此操作的程序放回错误信息
此代码 应用于生产者 和 消费者
boolean durable = true;
channel.queueDeclare("new_hello", durable, false, false, null);
2.将消息声明成持久性的
现在我们需要将消息标记为持久化 - 通过MessageProperties(实现BasicProperties)设置为值PERSISTENT_TEXT_PLAIN
channel.basicPublish("", "new_hello", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
注意:将消息标记为持久性并不能完全保证消息不会丢失,虽然他告诉rabbitmq将消息保存到磁盘,但是当rabbitmq接受消息时,仍然有一个短暂的空窗口。此外,rabbitmq不会为每一条消息执行fsync(2),他可能只是保存到缓存而不是真正得写入磁盘。持久性不保证强。发布者模式将会弥补这一缺憾。
公平派遣
在处理并确认一个消息前,不要向消费者发起新的消息。上面例子已经使用了
告诉rabbitMQ一次只能向一个工作者发送一条消息,在处理并确认前一个消息之前,不要给工作着发送新消息
// 一次只能接收一个未包装的消息
channel.basicQos(1);