一.simple简单队列
1.导入依赖jar包
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.0.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2.获取rabbitmq的连接
ConnectionUtils.java
public class ConnectionUtils {
/**
* 获取rabbitmq的连接
* @return
*/
public static Connection getConnection() throws IOException, TimeoutException {
//定义一个连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务地址
factory.setHost("127.0.0.1");
//AMQP 5672
factory.setPort(5672);
//virtualHost 相当于就是数据库
factory.setVirtualHost("/db1");
//用户名
factory.setUsername("xuanxuanxuan");
//密码
factory.setPassword("12345678");
return factory.newConnection();
}
}
Send.java 生产者生产消息
public class Send {
private static final String QUEUE_NAME = "test_simple_queue";
public static void main(String[] args) throws Exception{
//创建一个连接
Connection connection = ConnectionUtils.getConnection();
//从连接中获取一个通道
Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
String msg = "hello simple !";
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
System.out.println("----send msg:"+msg);
channel.close();
connection.close();
}
}
Receive.java 消费者消费消息
/**
* 消费者获取消息
*/
public class Receive {
private static final String QUEUE_NAME = "test_simple_queue";
public static void main(String[] args) throws Exception{
//获取连接
Connection connection = ConnectionUtils.getConnection();
//根据连接得到通道
Channel channel = connection.createChannel();
//定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);//旧的api
//监听队列
channel.basicConsume(QUEUE_NAME,true,consumer);
while (true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msgString = new String(delivery.getBody());
System.out.println("[receive] msg:"+msgString);
}
}
}
新版api
public static void main(String[] args) throws Exception{
//获取连接
Connection connection = ConnectionUtils.getConnection();
//创建通道
Channel channel = connection.createChannel();
//队列声明
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//定义消费者
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
//获取到到达的消息
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msgString = new String(body, "utf-8");
System.out.println("new api receive:" + msgString);
}
};
//监听队列
channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
}
二.work queues 工作队列 公平分发 轮询分发
简单队列的不足:耦合性高,生产者一一对应消费者(如果我想有多个消费者消费队列中的消息,这时候就不行了),队列名变更,这时候得同时变更。
为什么会出现工作队列?
Simple队列是一一对应的,而且我们实际开发,生产者发送消息是毫不费力的,而消费者一般是要跟业务相结合的。消费者接收到消息之后就需要处理,可能需要花费时间,这时候队列就会积压很多消息,一个消费者不够消费这些消息,这时候就需要多个消费者来消费。
1轮询分发
Send.java 生产者
public class Send {
private static final String QUEUE_NAME = "test_work_queues";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取channel
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
for (int i = 0; i < 50; i++) {
String msg = UUID.randomUUID().toString();
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
TimeUnit.MILLISECONDS.sleep(20);
}
channel.close();
connection.close();
}
}
Receive1.java 消费者1
public class Receive1 {
private static final String QUEUE_NAME = "test_work_queues";
private static int i = 1;
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取通道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//创建消费者
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
//一旦有消息就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String str = new String(body,"utf-8");
System.out.println("[1] receive :"+str+" "+(i++));
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//监听队列
channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
}
}
Receive2.java
public class Receive2 {
private static final String QUEUE_NAME = "test_work_queues";
private static int i = 1;
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取通道
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//创建消费者
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
//一旦有消息就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String str = new String(body,"utf-8");
System.out.println("[2] receive :"+str+" "+(i++));
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//监听队列
channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
}
}
现象:消费者1和消费者2处理的消息是一样的,是均分的。这就是轮询分发(round-robin),结果就是无论忙与不忙,都不会多给一个任务,任务消息总是你一个我一个,轮换着来。
2公平分发 fair dipath
即能者多得
Send.java
public class Send {
private static final String QUEUE_NAME = "test_work_queues";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取channel
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
/**
* 每个消费者发送确认消息之前,消息队列不发送下一个消息到消费者,一次只处理一个消息
* 限制发送给同一个消费者,不得超过一条消息
*/
int prefetchCount = 1;
channel.basicQos(prefetchCount);
for (int i = 0; i < 50; i++) {
String msg = UUID.randomUUID().toString();
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
TimeUnit.MILLISECONDS.sleep(20);
}
channel.close();
connection.close();
}
}
Receive1.java
public class Receive1 {
private static final String QUEUE_NAME = "test_work_queues";
private static int i = 1;
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取通道
final Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.basicQos(1);//保证一次只分发一个
//创建消费者
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
//一旦有消息就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String str = new String(body,"utf-8");
System.out.println("[1] receive :"+str+" "+(i++));
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("[1] done");
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//监听队列 autoAck=false 即第二个参数改为false 不让其自动应答
channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
}
}
Receive2.java
public class Receive2 {
private static final String QUEUE_NAME = "test_work_queues";
private static int i = 1;
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取通道
final Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.basicQos(1);
//创建消费者
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
//一旦有消息就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String str = new String(body,"utf-8");
System.out.println("[2] receive :"+str+" "+(i++));
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("[2] done ");
//手动回执
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//监听队列 不让其自动应答
channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
}
}
3消息应答与消息持久化
消息应答:
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
boolean autoAck = true;//自动确认模式 一旦rabbitmq将消息分发给消费者,就会从内存中删除
这种情况下,如果杀死正在执行的消费者,就会丢失正在处理的消息
boolean autoAck = false;手动模式 如果一个消费者挂掉,就会交付给其他消费者。rabbitmq支持消息应答,消费者发送一个消息应答,告诉rabbitmq这个消息我已经处理完成,你可以删了,然后rabbitmq就删除内存中的消息
消息应答默认是打开的,false。
如果rabbitmq挂掉了,那么消息也会丢失。
消息持久化:
boolean durable = false;
channel.queueDeclare(QUEUE_NAME,durable,false,false,null);
我们将程序中的boolean durable = false;改成true;是不可以的,即使代码是正确的,他也不会运行成功的!因为我们已经定义了一个叫test_work_queue,这个queue是未持久化的。rabbitmq不允许重新定义(不同参数)一个已存在的队列。
解决方案:①在控制台将这个队列删除掉
②修改队列名