1、simple
producer-------------------->|queue|---------------->consumer
producer:
public static void main(String[] args) throws Exception {
Connection connection = ConnectUtil.getConnection();
Channel channel = connection.createChannel();
//声明队列
String queryName="simple_queue";
channel.queueDeclare(queryName, false, false, false,null);
//发送消息
String msg="hello world";
channel.basicPublish("", queryName,null, msg.getBytes());
System.out.println("Sender:" + msg);
//关闭通道和连接
channel.close();
connection.close();
}
consumer:
public static void main(String[] args) throws Exception {
Connection connection = ConnectUtil.getConnection();
Channel channel = connection.createChannel();
//声明队列
String queryName="simple_queue";
channel.queueDeclare(queryName, false, false, false,null);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body);
// int i=6/0;
System.out.println(" received : " + msg + "!");
//true:将小于当前消息的下标的消息全处理掉 false:反之
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
//第二个参数 手动ack
//ack=true: 如果消费信息过程中出现中出现异常 消息也会被销毁 false:出现异常不会被销毁
channel.basicConsume(queryName, true, consumer);
}
2、work:多个消费者消费同一个队列
producer-------------------->|queue|---------------->多consumer(消费同一个队列)
producer:
public static void main(String[] args) throws Exception {
Connection connection = ConnectUtil.getConnection();
Channel channel = connection.createChannel();
//声明队列
String queryName="work_queue";
channel.queueDeclare(queryName, false, false, false,null);
//发送消息 多发送几条 观察消费者的处理消息情况
for (int i = 0; i < 50; i++) {
String msg="hello world---"+i;
channel.basicPublish("", queryName,null, msg.getBytes());
System.out.println("Sender:" + msg);
}
//关闭通道和连接
channel.close();
connection.close();
}
consumer1:处理过程中睡2秒 模仿性能低的一个消费者
public static void main(String[] args) throws Exception {
Connection connection = ConnectUtil.getConnection();
Channel channel = connection.createChannel();
//声明队列
String queryName="work_queue";
channel.queueDeclare(queryName, false, false, false,null);
//能者多劳
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
String msg = new String(body);
System.out.println(" received1 : " + msg + "!");
//true:将小于当前消息的下标的消息全处理掉 false:反之
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
//第二个参数 手动ack
//ack=true: 如果消费信息过程中出现中出现异常 消息也会被销毁 false:出现异常不会被销毁
channel.basicConsume(queryName, false, consumer);
}
consumer2:消费同一个队列,模仿没有延迟的性能高的消费者
public static void main(String[] args) throws Exception {
Connection connection = ConnectUtil.getConnection();
Channel channel = connection.createChannel();
//声明队列
String queryName="work_queue";
channel.queueDeclare(queryName, false, false, false,null);
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
rows IOException
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body);
System.out.println(" received2 : " + msg + "!");
//true:将小于当前消息的下标的消息全处理掉 false:反之
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
//第二个参数 手动ack
//ack=true: 如果消费信息过程中出现中出现异常 消息也会被销毁 false:出现异常不会被销毁
channel.basicConsume(queryName, false, consumer);
}
结果:由于添加了channel.basicQos(1);能者多劳
consumer1 只处理了一条信息,consumer2处理了其余的四十九条信息。
3、fanout: 多个队列,消息发送不在发送到队列,直接发送到交换机上
bind
producer------------->exchange---------------->|多queue|---------------->多consumer
注意:exchange只负责分发消息,若没有queue绑定到exchange则消息会被丢弃
producer:
public static void main(String[] args) throws Exception {
Connection connection = ConnectUtil.getConnection();
Channel channel = connection.createChannel();
//声明交换机 不再需要声明队列
String exchange = "fanout_exchange";
// 交换机名 交换机类型
channel.exchangeDeclare(exchange, "fanout");
//声明队列
// String queryName="work_queue";
// channel.queueDeclare(queryName, false, false, false,null);
//发送消息
String msg="hello world---";
// 交换机名 队列名 basicproperties msg
channel.basicPublish(exchange, "",null, msg.getBytes());
System.out.println("Sender:" + msg);
//关闭通道和连接
channel.close();
connection.close();
}
consumer1:
public static void main(String[] args) throws Exception {
Connection connection = ConnectUtil.getConnection();
Channel channel = connection.createChannel();
String queryName="fanout_queue_1";
String exchange="fanout_exchange";
//声明队列
channel.queueDeclare(queryName, false, false, false,null);
//将队列绑定到交换机上
channel.queueBind(queryName, exchange, "");
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body);
System.out.println(" received1 : " + msg + "!");
//true:将小于当前消息的下标的消息全处理掉 false:反之
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
//第二个参数 手动ack
//ack=true: 如果消费信息过程中出现中出现异常 消息也会被销毁 false:出现异常不会被销毁
channel.basicConsume(queryName, false, consumer);
}
consumer2:
代码部分只是变成: queryName="fanout_queue_2"
结果:producer发送消息 consumer1和consumer2都能接收到消息
4、direct:指定routingkey 分发消息
bind(routingkey)
producer------------->exchange-------------------->|多queue|---------------->多consumer
routingkey:灵活控制消息分发
producer:
public static void main(String[] args) throws Exception {
Connection connection = ConnectUtil.getConnection();
Channel channel = connection.createChannel();
//声明交换机 不再需要声明队列
String exchange = "direct-exchange";
// 交换机名 交换机类型 routingKey
channel.exchangeDeclare(exchange, "direct");
//发送消息
String msg="hello world";
//交换机名 队列名 basicproperties msg
channel.basicPublish(exchange, "test",null, msg.getBytes());
System.out.println("Sender:" + msg);
//关闭通道和连接
channel.close();
connection.close();
}
consumer1:
public static void main(String[] args) throws Exception {
Connection connection = ConnectUtil.getConnection();
Channel channel = connection.createChannel();
String queryName="direct_queue_1";
String exchange="direct-exchange";
//声明队列
channel.queueDeclare(queryName, false, false, false,null);
//将队列绑定到交换机上
channel.queueBind(queryName, exchange, "");
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body);
System.out.println(" received1 : " + msg + "!");
//true:将小于当前消息的下标的消息全处理掉 false:反之
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
//第二个参数 手动ack
//ack=true: 如果消费信息过程中出现中出现异常 消息也会被销毁 false:出现异常不会被销毁
channel.basicConsume(queryName, false, consumer);
}
consumer2:
相对于consumer1修改部分:
1)队列名称:queryName="direct_queue_2";
2)channel.queueBind(queryName, exchange, "test");
结果:producer发送的时候携带的routingkey时“test”,consumer1不匹配,consumer2匹配
consumer2接收到了消息,但是consumer1没有
5、topic:通配符匹配,接受分发的消息
bind(#.routingkey.*)
producer------------->exchange-------------------->|多queue|---------------->多consumer
通配符:
#:匹配n个单词
*:匹配1个单词
producer:
public static void main(String[] args) throws Exception {
Connection connection = ConnectUtil.getConnection();
Channel channel = connection.createChannel();
//声明交换机 不再需要声明队列
String exchange = "topic-exchange";
// 交换机名 交换机类型 routingKey
channel.exchangeDeclare(exchange, "topic");
//发送消息
String msg="hello world";
//交换机名 队列名 basicproperties msg
channel.basicPublish(exchange, "topic.ww.add",null, msg.getBytes());
System.out.println("Sender:" + msg);
//关闭通道和连接
channel.close();
connection.close();
}
consumer1:
public static void main(String[] args) throws Exception {
Connection connection = ConnectUtil.getConnection();
Channel channel = connection.createChannel();
String queryName="topic_queue_1";
String exchange="topic-exchange";
//声明队列
channel.queueDeclare(queryName, false, false, false,null);
//将队列绑定到交换机上
channel.queueBind(queryName, exchange, "*.add");
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body);
System.out.println(" received1 : " + msg + "!");
//true:将小于当前消息的下标的消息全处理掉 false:反之
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
//第二个参数 手动ack
//ack=true: 如果消费信息过程中出现中出现异常 消息也会被销毁 false:出现异常不会被销毁
channel.basicConsume(queryName, false, consumer);
}
consumer2:
相对于consumer1修改部分:
1)队列名称:queryName="topic_queue_2";
2)channel.queueBind(queryName, exchange, "#.add");
结果:consumer1没有接收到消息,consumer2接收到了。
问题1:如何保证消费者把消息成功消费?手动ack(理论)
手动ack:
channel.basicConsume(, false, );//不自动ack
// true:就会处理掉比当前信息下标小的所有信息
try {
//业务逻辑
channel.basicAck(envelope.getDeliveryTag(), false);//手动ack
// true:无论是否出现异常执行之后都会将消息处理掉
}catch (Exception e){
e.printStackTrace();
}
问题2:如何防止消息堆积?能者多劳+多consumer
能者多劳:
channel.basicQos(1);
问题3:再修改和routingKey有关的代码,需要先删除原来的通配符匹配规则
修改后的匹配规则,不会覆盖原来的,而是追加。会对匹配规则造成影响。
问题4:如果交换机没有创建,但是开启消费者会报错
可以先执行生产者发送一个空消息,建立好交换机之后再启动消费者。
问题5:为什么使用rabbitmq
1)解耦:分布式项目中,一个模块需要调用其他模块的接口(商品,订单),当新增加业务时(聊天),就需要这个调用其他模块的内容进修添加修改,耦合度很高。使用消息队列之后:所有模块信息包括 --”新增加业务模块“-- 全发送到消息队列中,调用者直接接收队列的消息即可,不需要修改任何原来模块的代码,只需要将新的消息发送到队列中即可。
2)异步:消息写入队列之后,队列处理一些非必要业务逻辑采用异步的方式,加快响应时间。
3)削峰:数据库无法保证在高并发的情况下正常运行。中间件会根据数据库可以处理请求的最大并发量,有序的从队列中拉取数据。