官网介绍:https://www.rabbitmq.com/getstarted.html
(1) 简单模式:一个生产者,一个消费者
(2) work模式:一个生产者,多个消费者,每个消费者获取到的消息唯一。
(3) 订阅模式:一个生产者发送的消息会被多个消费者获取。
(4) 路由模式:发送消息到交换机并且要指定路由key ,消费者将队列绑定到交换机时需要指定路由key
(5) topic模式:将路由键和某模式进行匹配,此时队列需要绑定在一个模式上,“#”匹配一个词或多个词,“*”只匹配一个词
(6) RPC模式:使用RabbitMQ构建RPC系统:客户端和可伸缩RPC服务器。
(7)发布确认:与发布者进行可靠的发布确认
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=rabbitadmin
spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=/vhost_rabbitmq
public class ConnectionUitls {
public static Connection getConnection() throws IOException, TimeoutException{
//定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置连接地址
factory.setHost("127.0.0.1");
//AMQP 5672
factory.setPort(5672);
//vhost
factory.setVirtualHost("/vhost_jetsen");
//username
factory.setUsername("rabbitadmin");
//password
factory.setPassword("123456");
Connection newConnection = factory.newConnection();
return newConnection;
}
}
(1) 简单模式:一个生产者,一个消费者
package com.jetsen.mq.q1simplequeue;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
//Java操作simple简单队列
//1.耦合性高,生产者一一对应,(如果想有多个消费者 ,不可行)
//2.队列名称变更,生产者、消费者需同时变更。
public class Sender {
private static final String QUEUE_NAME="test_simple_queueu";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
//获取通道
Channel channel = connection.createChannel();
//队列声明
//String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "send message !";
//String exchange, String routingKey, BasicProperties props, byte[] body
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println("===>send msg:"+message);
channel.close();
connection.close();
}
}
队列的申明
Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
queue:队列名称
durable:是否持久化, 队列的声明默认是存放到内存中的,如果rabbitmq重启会丢失,如果想重启之后还存在就要使队列持久化,保存到Erlang自带的Mnesia数据库中,当rabbitmq重启之后会读取该数据库。
exclusive:是否排外的,有两个作用,一:当连接关闭时connection.close()该队列是否会自动删除;二:该队列是否是私有的private,如果不是排外的,可以使用两个消费者都访问同一个队列,没有任何问题,如果是排外的,会对当前队列加锁,其他通道channel是不能访问的,如果强制访问会报异常
autoDelete:是否自动删除,当最后一个消费者断开连接之后队列是否自动被删除,可以通过RabbitMQ Management,查看某个队列的消费者数量,当consumers = 0时队列就会自动删除
arguments: 队列中的消息什么时候会自动被删除
void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;
package com.jetsen.mq.q1simplequeue;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
//Java操作simple简单队列
public class Recv {
private static final String QUEUE_NAME="test_simple_queueu";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUitls.getConnection();
//创建频道
Channel channel = connection.createChannel();
//QUEUE_NAME, durable, exclusive, autoDelete, arguments 队列声明
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//事件模型
DefaultConsumer consumerCallback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer receive:=>"+messageStr);
}
};
//String queue, boolean autoAck, Consumer callback
channel.basicConsume(QUEUE_NAME,true, consumerCallback);
}
}
(2) work模式:一个生产者,多个消费者,每个消费者获取到的消息唯一。
RabbitMq消费分发机制及主题消费分发(公平分发 + 轮训分发)
公平分发 :现在有2个消费者,我们使用basicQos( prefetchCount = 1)方法,来限制RabbitMQ只发不超过1条的消息给同一个消费者。当消息处理完毕后,有了反馈,才会进行第二次发送。
生产者每次发送间隔为20ms,消费者1消费间隔为2s,消费者2消费间隔为1s,代表消费者2消费能力高。
公平分发
//每个消费者,在发送【确认消息】之前,消息队列不发送下一个消息到消费者,一次只处理一个消息
//限制发送给同一个消费者不得超过1条消息
int prefetchCount = 1;
channel.basicQos(prefetchCount);
//手动回执消息
channel.basicAck(envelope.getDeliveryTag(), false);
//公平队列将自动应答修改为false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck, consumer);
package com.jetsen.mq.q2workqueue.fairdispatch;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
* 工作队列
* |----cl 2s
* p-----------queue------|
* |----c2 1s
*/
public class Sender {
private static final String QUEUE_NAME="test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUitls.getConnection();
//获取channel
Channel channel = connection.createChannel();
//申明队列String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//每个消费者,在发送【确认消息】之前,消息队列不发送下一个消息到消费者,一次只处理一个消息
//限制发送给同一个消费者不得超过1条消息
int prefetchCount = 1;
channel.basicQos(prefetchCount);
for (int i = 0; i < 50; i++) {
String sendMsg = "Sender==>send message"+i;
System.out.println("Sender==>send message"+i);
channel.basicPublish("", QUEUE_NAME, null, sendMsg.getBytes());
Thread.sleep(i*20);
}
channel.close();
connection.close();
}
}
package com.jetsen.mq.q2workqueue.fairdispatch;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作队列之Round-robin:消费者1、消费者2处理的消息一样多,进行的是轮询分发
public class Recv1 {
private static final String QUEUE_NAME="test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUitls.getConnection();
//创建频道
Channel channel = connection.createChannel();
//申明队列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//保证一次只分发一个
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[1] receive:=>"+messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
System.out.println("Consumer[1] done!");
//手动回执消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//String queue, boolean autoAck, Consumer callback
//公平队列将自动应答修改为false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck, consumer);
}
}
package com.jetsen.mq.q2workqueue.fairdispatch;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作队列之Round-robin:消费者1、消费者2处理的消息一样多,进行的是轮询分发
public class Recv2 {
private static final String QUEUE_NAME="test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUitls.getConnection();
//创建频道
Channel channel = connection.createChannel();
//申明队列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//保证一次只分发一个
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[2] receive:=>"+messageStr);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
System.out.println("Consumer[2] done!");
//手动回执消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//String queue, boolean autoAck, Consumer callback
//公平队列将自动应答修改为false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck, consumer);
}
}
轮训分发
手动改为自动回执消息 channel.basicAck(envelope.getDeliveryTag(), true)
自动应答修改为autoAck true channel.basicConsume(QUEUE_NAME,true, consumer);
去掉流浪控制channel.basicQos(prefetchCount);
package com.jetsen.mq.q2workqueue.roundrobin;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
* 工作队列
* |----cl 2s
* p-----------queue------|
* |----c2 1s
*/
public class Sender {
private static final String QUEUE_NAME="test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUitls.getConnection();
//获取channel
Channel channel = connection.createChannel();
//申明队列String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
for (int i = 0; i < 50; i++) {
String sendMsg = "Sender==>send message"+i;
System.out.println("Sender==>send message"+i);
channel.basicPublish("", QUEUE_NAME, null, sendMsg.getBytes());
Thread.sleep(i*20);
}
channel.close();
connection.close();
}
}
package com.jetsen.mq.q2workqueue.roundrobin;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作队列之Round-robin:消费者1、消费者2处理的消息一样多,进行的是轮询分发
public class Recv1 {
private static final String QUEUE_NAME="test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUitls.getConnection();
//创建频道
Channel channel = connection.createChannel();
//申明队列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[1] receive:=>"+messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
System.out.println("Consumer[1] done!");
}
}
};
//String queue, boolean autoAck, Consumer callback
channel.basicConsume(QUEUE_NAME,true, consumer);
}
}
package com.jetsen.mq.q2workqueue.roundrobin;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作队列之Round-robin:消费者1、消费者2处理的消息一样多,进行的是轮询分发
public class Recv2 {
private static final String QUEUE_NAME="test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUitls.getConnection();
//创建频道
Channel channel = connection.createChannel();
//申明队列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[2] receive:=>"+messageStr);
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
System.out.println("Consumer[2] done!");
}
}
};
//String queue, boolean autoAck, Consumer callback
channel.basicConsume(QUEUE_NAME,true, consumer);
}
}
(3) 订阅模式:一个生产者发送的消息会被多个消费者获取。
解读:
1、1个生产者,多个消费者
2、每一个消费者都有自己的一个队列
3、生产者没有将消息直接发送到队列,而是发送到了交换机
4、每个队列都要绑定到交换机
5、生产者发送的消息,经过交换机,到达队列,实现,一个消息被多个消费者获取的目的
注意:一个消费者队列可以有多个消费者实例,只有其中一个消费者实例会消费
package com.jetsen.mq.q3pubsub;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
* publish_subscribe订阅模式
*
*
* ""匿名转发: channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
*
* Exchange(一方面是接收生产者的消息,另一方面将消息推送至各自的队列)
*
*
* (1)fanout:(不处理路由键)
* |---queue1----c1
* p----exchange-Bindings-|
* |---queue2----c2
*/
public class Sender {
private static final String EXCHANGE_NAME="test_exchage_fanout";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");//分发类型
//发送消息
String message = "send pub sub message ....";
//exchange, routingKey, props, body
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
System.out.println("Send message:"+message);
channel.close();
connection.close();
}
}
package com.jetsen.mq.q3pubsub;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv1 {
private static final String EXCHANGE_NAME="test_exchage_fanout";
private static final String QUEUE_NAME="test_queue_fanout_email";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
//queue, durable, exclusive, autoDelete, arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//保证一次只分发一个
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[1] receive:=>"+messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
System.out.println("Consumer[1] done!");
//手动回执消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//String queue, boolean autoAck, Consumer callback
//公平队列将自动应答修改为false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck, consumer);
}
}
package com.jetsen.mq.q3pubsub;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv2 {
private static final String EXCHANGE_NAME="test_exchage_fanout";
private static final String QUEUE_NAME="test_queue_fanout_msm";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
//queue, durable, exclusive, autoDelete, arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//保证一次只分发一个
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[2] receive:=>"+messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
System.out.println("Consumer[2] done!");
//手动回执消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//String queue, boolean autoAck, Consumer callback
//公平队列将自动应答修改为false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck, consumer);
}
}
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");//分发类型
(4) 路由模式:发送消息到交换机并且要指定路由key ,消费者将队列绑定到交换机时需要指定路由key
package com.jetsen.mq.q4routing;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
*
* ""匿名转发: channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
*
* Exchange(一方面是接收生产者的消息,另一方面将消息推送至各自的队列)'
*
*
* (2)Direct:(处理路由键)
* type=direct |---queue1(routing key)----c1
* p(routing key)----exchange-Bindings-|
* |---queue2----c2
* |
* |---queue3----c3
*/
public class Sender {
private static final String EXCHANGE_NAME="test_exchange_direct";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, "direct");//分发类型
//发送消息
String message = "send direct message ....";
String routingKey="error";
channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes());
System.out.println("Send message:"+message);
channel.close();
connection.close();
}
}
package com.jetsen.mq.q4routing;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv1 {
private static final String EXCHANGE_NAME = "test_exchange_direct";
private static final String QUEUE_NAME = "test_queue_direct_1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
// queue, durable, exclusive, autoDelete, arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String routingKey = "error";
// 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);
// 保证一次只分发一个
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body, "UTF-8");
System.out.println("Consumer[1] receive:=>" + messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("Consumer[1] done!");
// 手动回执消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
// 公平队列将自动应答修改为false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
package com.jetsen.mq.q4routing;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv2 {
private static final String EXCHANGE_NAME = "test_exchange_direct";
private static final String QUEUE_NAME = "test_queue_direct_2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
// queue, durable, exclusive, autoDelete, arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String routingKeyInfo = "info";
String routingKeyWarning = "warning";
String routingKeyError = "error";
// 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKeyInfo);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKeyWarning);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKeyError);
// 保证一次只分发一个
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body, "UTF-8");
System.out.println("Consumer[2] receive:=>" + messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("Consumer[2] done!");
// 手动回执消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
// 公平队列将自动应答修改为false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
由于Exchange绑定的两个队列中, Bingdings四个钟,有两个是订阅了errro的消息,所以两个消费者都可以消费到消息
(5) topic模式:将路由键和某模式进行匹配,此时队列需要绑定在一个模式上,“#”匹配一个词或多个词,“*”只匹配一个词
package com.jetsen.mq.q5topic;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
*
* ""匿名转发: channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
*
* Exchange(一方面是接收生产者的消息,另一方面将消息推送至各自的队列)'
*
*
* (3)topic:(topic主题模式)
* type=topic |---queue1----c1
* p----exchange-Bindings--------------|
* |---queue2----c2
* |
* |---queue3----c3
* 将路由和某个模式匹配(符号) *匹配一个 #匹配多个
*/
public class Sender {
private static final String EXCHANGE_NAME="test_exchange_topic";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String msgString="商品....";
channel.basicPublish(EXCHANGE_NAME, "goods.add", null, msgString.getBytes());
System.out.println("----send" +msgString);
channel.close();
connection.close();
}
}
package com.jetsen.mq.q5topic;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv1 {
private static final String EXCHANGE_NAME = "test_exchange_topic";
private static final String QUEUE_NAME = "test_queue_topic_1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
// queue, durable, exclusive, autoDelete, arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String routingKey = "goods.add";
// 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);
// 保证一次只分发一个
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body, "UTF-8");
System.out.println("Consumer[2] receive:=>" + messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("Consumer[2] done!");
// 手动回执消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
// 公平队列将自动应答修改为false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
package com.jetsen.mq.q5topic;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv2 {
private static final String EXCHANGE_NAME = "test_exchange_topic";
private static final String QUEUE_NAME = "test_queue_topic_2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
// queue, durable, exclusive, autoDelete, arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String routingKey = "goods.#";
// 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);
// 保证一次只分发一个
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body, "UTF-8");
System.out.println("Consumer[1] receive:=>" + messageStr);
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("Consumer[1] done!");
// 手动回执消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
// 公平队列将自动应答修改为false;
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
String routingKey = "goods.#";
// 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);
(6) RPC模式:使用RabbitMQ构建RPC系统:客户端和可伸缩RPC服务器。
RPC的处理流程
- 当客户端启动时,创建一个匿名的回调队列。
- 客户端为RPC请求设置2个属性:replyTo,设置回调队列名字;correlationId,标记request。
- 请求被发送到请求队列中。
- RPC服务器端监听请求队列中的请求,当请求到来时,服务器端会处理并且把带有结果的消息发送给客户端。接收的队列就是replyTo设定的回调队列。
- 客户端监听回调队列,当有消息时,检查correlationId属性,如果与request中匹配,则返回。
package com.jetsen.mq.q11.rpc;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class RpcClient {
private static final String RPC_QUEUE_NAME = "test_rpc_queueu";
public static void main(String[] args) {
Connection connection = null;
// 获取通道
Channel channel = null;
try {
connection = ConnectionUitls.getConnection();
// 获取通道
channel = connection.createChannel();
// 创建回调队列
String callBackQueue = channel.queueDeclare().getQueue();
// 创建带有correlationId的消息属性
String correlationId = UUID.randomUUID().toString();
AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder().correlationId(correlationId)
.replyTo(callBackQueue).build();
// 消费者从回调队列中接受服务端传递的消息
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException {
String recvcorrelationId = properties.getCorrelationId();
if (correlationId.equals(recvcorrelationId)) {
System.out.println("客户单接收消息:" + new String(body) + ",correlationId=" + correlationId);
}
}
};
// 监听队列
channel.basicConsume(callBackQueue, true, consumer);
String message = "rpc rabbitmq";
channel.basicPublish("", RPC_QUEUE_NAME, basicProperties, message.getBytes());
System.out.println("客户端发送消息:" + message + ",correlationId=" + correlationId);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
}
}
}
package com.jetsen.mq.q11.rpc;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class RpcServer {
private static final String RPC_QUEUE_NAME="test_rpc_queueu";
public static void main(String[] args) {
try {
Connection connection = ConnectionUitls.getConnection();
//获取通道
Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(RPC_QUEUE_NAME,false,false,false,null);
//申明消费者预取数量
channel.basicQos(1);
//创建消费者
// 消费者从队列中接受服务端传递的消息
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException {
System.out.println("服务端接收消息:" + new String(body));
channel.basicAck(envelope.getDeliveryTag(), false);
String correlationId = properties.getCorrelationId();
AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder()
.correlationId(correlationId)
.build();
channel.basicPublish("", properties.getReplyTo(), basicProperties, "RPC SERVER:11111111".getBytes());
}
};
// 监听队列
channel.basicConsume(RPC_QUEUE_NAME, false, consumer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(7)发布确认:与发布者进行可靠的发布确认
Confirm发送方确认模式使用和事务类似,也是通过设置Channel进行发送方确认的。
package com.jetsen.mq.q7confirm;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class Recv {
private static final String QUEUE_NAME = "test_queue_confirm1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
//String queue, boolean autoAck, Consumer callback
channel.basicConsume(QUEUE_NAME,true, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String messageStr = new String(body,"UTF-8");
System.out.println("Consumer[test_queue_confirm1] receive:=>"+messageStr);
}
});
}
}
Confirm的三种实现方式:
方式一:channel.waitForConfirms()普通发送方确认模式;
package com.jetsen.mq.q7confirm;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
* rabbitmq消息确认机制之confirm
*/
public class ConfirmSender {
private static final String QUEUE_NAME = "test_queue_confirm1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String msgString =" send confirm message";
//生产者调用将channel设为confirm模式,注意
channel.confirmSelect();
channel.basicPublish("", QUEUE_NAME, null, msgString.getBytes());
System.out.println("----send" +msgString);
if(!channel.waitForConfirms()){
System.out.println("send confirm failed");
}else{
System.out.println("send confirm success");
}
channel.close();
connection.close();
}
}
我们只需要在推送消息之前,channel.confirmSelect()声明开启发送方确认模式,再使用channel.waitForConfirms()等待消息被服务器确认即可。
方式二:channel.waitForConfirmsOrDie()批量确认模式;
package com.jetsen.mq.q7confirm;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
* rabbitmq消息确认机制之confirm
*/
public class ConfirmBatchSender {
private static final String QUEUE_NAME = "test_queue_confirm1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String msgString =" send confirm message";
//生产者调用将channel设为confirm模式,注意
channel.confirmSelect();
for (int i = 0; i < 10; i++) {
channel.basicPublish("", QUEUE_NAME, null, msgString.getBytes());
}
// if(!channel.waitForConfirms()){
// System.out.println("send confirm failed");
// }else{
// System.out.println("send confirm success");
// }
channel.waitForConfirmsOrDie(); //直到所有信息都发布,只要有一个未确认就会IOException
channel.close();
connection.close();
}
}
以上代码可以看出来channel.waitForConfirmsOrDie(),使用同步方式等所有的消息发送之后才会执行后面代码,只要有一个消息未被确认就会抛出IOException异常。
方式三:channel.addConfirmListener()异步监听发送方确认模式;
package com.jetsen.mq.q7confirm;
import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.TimeoutException;
import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
/**
* 异步模式
* Channel 对象提供的ConfirmListener()回调方法之包含deliveryTag(当前Channel发出
* 的消息序号),我们需要自己为每一个Channel维护一个unconfirm的消息序号集合,每publish
* 一条数据,集合中元素就+1,每回调一次handlerAck方法,unconfirm结合删掉相应的一条(multiple=false)
* 或者多条(multiple=true)记录,从程序运行效率上看,这个unconfirm集合最好采用有序集合
* SortedSet存储结构
* @author jetsen
*
*/
public class ConfirmAysnSender {
private static final String QUEUE_NAME = "test_queue_confirm3";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUitls.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//生产者调用confirmSelect将channel设置为Confirm模式
channel.confirmSelect();
//未确认消息的标识
final SortedSet<Long> confirmSet=Collections.synchronizedSortedSet(new TreeSet<Long>());
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
if(multiple){
System.out.println("----handlerNack-----multiple");
confirmSet.headSet(deliveryTag+1).clear();
}else{
System.out.println("----handlerNack-----multiple----false");
confirmSet.remove(deliveryTag);
}
}
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
if(multiple){
System.out.println("----handleAck-----multiple");
confirmSet.headSet(deliveryTag+1).clear();
}else{
System.out.println("----handleAck-----multiple----false");
confirmSet.remove(deliveryTag);
}
}
});
String msgString =" send confirm Aysn message";
while(true){
long segNo = channel.getNextPublishSeqNo();
channel.basicPublish("", QUEUE_NAME, null, msgString.getBytes());
confirmSet.add(segNo);
}
}
}
异步模式的优点,就是执行效率高,不需要等待消息执行完,只需要监听消息即可。