初识RabbitMq(2)7个案例前两个
Hello World(简单队列)
生产者------》消息队列-------》消费者
生产者
package org.example.simple.send;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.nio.charset.StandardCharsets;
/*
* 生产者
* */
public class Send {
//队列名称
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
//创建连接工厂 port默认不用配置
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("xx.xx.xxx.xxx");
factory.setUsername("****");
factory.setPassword("***");
factory.setVirtualHost("***");
//try cache新写法
try (
//用连接工厂 创建连接 Connection继承了Closeable接口可以自动关闭
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel()) {
/*
* 绑定队列 队列名
* 持久化
* 排他队列(基于连接课件,仅对首次声明它的连接可见,名字不能相同,连接关闭之后排他队列自动删除不管持久化)
* 自动删除
* 参数
* */
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//message内容
String message = "Hello World!";
//发送消息 交换机
channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
System.out.println(" [x] Sent '" + message + "'");
}
}
}
消费者
package org.example.simple.recv;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
/*
* 消费者
* */
public class Recv {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
//创建连接工厂 port默认不用配置
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("xx.xx.xxx.xxx");
factory.setUsername("****");
factory.setPassword("***");
factory.setVirtualHost("***");
//用连接工厂 创建连接 Connection继承了Closeable接口可以自动关闭
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 + "'");
};
//监听队列 消费消息
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
}
}
优缺点
优点:简单
缺点:有可能消息堆积!消息供大于求,久而久之,造成内存或磁盘溢出。
解决方法增加消费者!!!
工作队列
工作队列概念(操作系统中):工作队列 ( workqueue )`是将操作(或回调)延期异步执行的一种机制。工作队列可以把工作推后,交由一个内核线程去执行,并且工作队列是执行在线程上下文中,因此工作执行过程中可以被重新调度、抢占、睡眠。
在RabbitMq中:
工作队列(又称任务队列)的主要思想是避免立即执行资源密集型任务,而不得不等待它完成。相反,我们安排任务在以后完成。我们将任务封装 为消息并将其发送到队列。在后台运行的工作进程 将弹出任务并最终执行作业。当您运行许多工作进程时,任务将在他们之间共享。
用来将耗时的任务分发给多个消费者(工作者),主要解决这样的问题:处理资源密集型任务,并且还要等他完成。有了工作队列,我们就可以将具体的工作放到后面去做,将工作封装为一个消息,发送到队列中,一个工作进程就可以取出消息并完成工作。如果启动了多个工作进程,那么工作就可以在多个进程间共享。
多个消费者一起工作,消费者处理消息的过程中其他消费者也在处理,减少休息时间,就加快了消费速度,达到提高消费能力的目的。
轮询模式
轮询——也可以说是循环,多个消费者一起工作,消费者处理消息的过程中其他消费者也在处理,减少休息时间,就加快了消费速度,达到提高消费能力的目的。
RabbitMQ 将按顺序将每条消息发送给下一个消费者,平均而言,每个消费者将获得相同数量的消息,这种分发消息的方式称为轮询。
就是多了消费者,A消费1号message,同时可以B消费2号message,如果A处理完了1号就可以消费3号message,但是如果B比A快,B不能接3号B接的是4号。
生产者
/*
* 工作队列-轮询-生产者
* */
public class Send {
//队列名称
private final static String QUEUE_NAME = "work_rr";
public static void main(String[] argv) throws Exception {
//创建连接工厂 port默认不用配置
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("*");
factory.setUsername("*");
factory.setPassword("*");
factory.setVirtualHost("*");
//try cache新写法
try (
//用连接工厂 创建连接 Connection继承了Closeable接口可以自动关闭
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel()) {
/*
* 绑定队列 队列名
* 持久化
* 排他队列(基于连接课件,仅对首次声明它的连接可见,名字不能相同,连接关闭之后排他队列自动删除不管持久化)
* 自动删除
* 参数
* */
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//发多条消息
for(int i=0;i<20;i++){
//message内容
String message = "Hello World!Work"+i;
//发送消息 交换机
channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
System.out.println(" [x] Sent '" + message + "'"+i);
}
}
}
}
消费者
/*
* 工作队列-轮询 消费者1
* */
public class Recv {
private final static String QUEUE_NAME = "work_rr";
public static void main(String[] argv) throws Exception {
//创建连接工厂 port默认不用配置
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("**********");
factory.setUsername("***");
factory.setPassword("*");
factory.setVirtualHost("*");
//用连接工厂 创建连接 Connection继承了Closeable接口可以自动关闭
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 + "'");
//手动确认 参数:multiple是否确认多条-》false逐条确认
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
};
//监听队列 消费消息
//队列名称 自动回执
channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> { });
}
}
/*
* 工作队列-轮询 消费者2
* */
public class Recv2 {
private final static String QUEUE_NAME = "work_rr";
public static void main(String[] argv) throws Exception {
//创建连接工厂 port默认不用配置
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("47.94.232.61");
factory.setUsername("liu");
factory.setPassword("liujiaxiang55");
factory.setVirtualHost("/liu");
//用连接工厂 创建连接 Connection继承了Closeable接口可以自动关闭
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) -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//拿到消息
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
//手动确认 参数:multiple是否确认多条-》false逐条确认
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
};
//监听队列 消费消息
//队列名称 自动回执
channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> { });
}
}
优缺点
优点:解决生产者生产能力大于消费的消费能力,减少多余消息堆积。
缺点:水桶效应,工作快的要等工作慢的。上面提到的但是如果B比A快,B不能接3号B接的是4号,队列最终消费完是算消费最慢的时间。
假如有一些非常耗时的任务,某个消费者在缓慢地进行处理,而另一个消费者则空闲,队列不能关闭,显然是非常消耗资源的。
公平模式
能者多劳!
如果B比A快,B能接3号不必等A接3号,没有停歇!谁有空谁就接着干!
做到能者多劳,没有资源浪费。
关键代码:
//在创建信道后 限制消费者每次只能接收一(prefetchCount)条消息,处理完才能接收下一条
int prefetchCount = 1;
channel.basicQos(prefetchCount);
参考
感谢!