之前生产者的消息是发个一个消费者的,如果想多个消费者都收到消息(系统告知所有用户今天促销)就需要使用rabbitmq中交换机(Exchange)功能了。
简示图:
代码:
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 生产者
* 订阅模式 Publish/Subscribe
* 通过交换机把消息发送给所有绑定该交换机的队列
* Created by py
* 2020/4/20
*/
public class SendRabbit {
private static String exchange = "test_exchange_fanout";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
//确认每次只发送一条信息
channel.basicQos(1);
//声明一个交换机,该类型交换机对生产者的消息不做任何处理,所有绑定该交换机的队列都会收到生产者的消息
channel.exchangeDeclare(exchange,"fanout");
for (int i = 0; i < 50; i++) {
String msg = "This is test_exchange_fanout:"+i;
/**
* basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body)
* exchange:交换机名称
* routingKey:队列绑定的交换机的key:不指定的时候所有绑定该交换机的队列都能收到消息
* body: 信息载体
*/
channel.basicPublish(exchange,"",null,msg.getBytes());
}
System.out.println("发送完成");
channel.close();
instance.close();
}
}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消费者1
* Created by py
* 2020/4/20
*/
public class FristConsumer {
private static String exchange = "test_exchange_fanout";
private static String queue = "test_exchange_fanout_queue1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
channel.basicQos(1);
//设置让rabbit消息持久化
boolean durable = true;
channel.queueDeclare(queue,durable,false,false,null);
channel.queueBind(queue,exchange,"");
// 定义一个消费者
Consumer consumer = new DefaultConsumer(channel) {
// 消息到达 触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
try {
String msg = new String(body,"utf-8");
System.out.println("FristConsumer:"+msg);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
boolean autoAck = false;
channel.basicConsume(queue, autoAck, consumer);
}
}
/**
* 消费者2
* Created by py
* 2020/4/20
*/
public class SecondConsumer {
private static String exchange = "test_exchange_fanout";
private static String queue = "test_exchange_fanout_queue2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
channel.basicQos(1);
//设置让rabbit消息持久化
boolean durable = true;
channel.queueDeclare(queue,durable,false,false,null);
channel.queueBind(queue,exchange,"");
// 定义一个消费者
Consumer consumer = new DefaultConsumer(channel) {
// 消息到达 触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
try {
String msg = new String(body,"utf-8");
System.out.println("SecondConsumer:"+msg);
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
boolean autoAck = false;
channel.basicConsume(queue, autoAck, consumer);
}
}
PS:生产者将消息发送到交换机中,哪个消费者的队列绑定了这个交换机,就把消息发送到哪个队列,这样一个消息就会发送个不同的队列让多个消费者接收到了。
fanout:交换机的类型,该类型时所有绑定该交换机的队列都会收到该交换机发出的消息
channel.exchangeDeclare(exchange,"fanout");
QA:若想生产者第一条消息消费者都能接收,第二条消息只让部分消费者接收,应该如何实现?
此时需要使用Exchange的另一种类型:direct,同时队列与交换机使用routingKey进行绑定。
简示图:
此时生产者向exchange推送消息时指定发送给哪个routingKey所绑定的队列。
如指定发送个routingA绑定的队列时两个服务都能收到消息,发送消息给routingC绑定的队列时则只有服务B能收到消息。
/**
* 生产者
* 路由模式
* 交换机的信息发送到指定的key,队列绑定交换机的时候指定的该key才能收到信息
* Created by py
* 2020/4/20
*/
public class SendRabbit {
private static String exchange = "test_exchange_direct";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
//确认每次只发送一条信息
//prefetchSize:设置传输过来的消息的大小
//prefetchCount:一次传输多少消息
//global:是否是全局消息,true:整个channel都使用该设置,false只应用于当前的customer
//PS:global:目前设置为false;
channel.basicQos(1);
//声明一个交换机,该类型交换机对生产者的消息进行处理,绑定该交换机的队列中存在和交换机发送信息时使用的key的队列才能收到信息
//交换机名称,交换机类型,是否持久化:消息未完成消费时是否写到磁盘里防止数据丢失
channel.exchangeDeclare(exchange,"direct",true);
for (int i = 0; i < 50; i++) {
String msg = "This is test_exchange_direct:"+i;
/**
* basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body)
* exchange:交换机名称
* routingKey:队列绑定的交换机的key:指定的时候那个队列有这个key哪个队列就会收到交换机的信息
* body: 信息载体
*/
//todo routingKey:rabbit==》两个队列都绑定了rabbit所以交换机的信息两个队列都能收到
channel.basicPublish(exchange,"rabbit",null,msg.getBytes());
//todo routingKey:monkey==》队列2绑定了monkey所以交换机的信息只有队列2能收到
//channel.basicPublish(exchange,"monkey",null,msg.getBytes());
}
System.out.println("发送完成");
channel.close();
instance.close();
}
}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消费者1
* Created by py
* 2020/4/20
*/
public class FristConsumer {
private static String exchange = "test_exchange_direct";
private static String queue = "test_exchange_direct_queue1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
channel.basicQos(1);
//设置让rabbit消息持久化
boolean durable = true;
channel.queueDeclare(queue,durable,false,false,null);
//rabbit:绑定的路由key
channel.queueBind(queue,exchange,"rabbit");
//定义一个消费者
Consumer consumer = new DefaultConsumer(channel) {
//消息到达 触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
try {
String msg = new String(body,"utf-8");
System.out.println("FristConsumer:"+msg);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
boolean autoAck = false;
channel.basicConsume(queue, autoAck, consumer);
}
}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消费者2
* Created by py
* 2020/4/20
*/
public class SecondConsumer {
private static String exchange = "test_exchange_direct";
private static String queue = "test_exchange_direct_queue2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
channel.basicQos(1);
//设置让rabbit消息持久化
boolean durable = true;
channel.queueDeclare(queue,durable,false,false,null);
channel.queueBind(queue,exchange,"rabbit");
channel.queueBind(queue,exchange,"cat");
channel.queueBind(queue,exchange,"dog");
channel.queueBind(queue,exchange,"bird");
channel.queueBind(queue,exchange,"monkey");
// 定义一个消费者
Consumer consumer = new DefaultConsumer(channel) {
// 消息到达 触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
try {
String msg = new String(body,"utf-8");
System.out.println("SecondConsumer:"+msg);
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
boolean autoAck = false;
channel.basicConsume(queue, autoAck, consumer);
}
}
//将交换机和队列通过routingKey:rabbit进行绑定 ==》队列和交换机之间可绑定多个routingKey
channel.queueBind(queue,exchange,"rabbit");
PS:生产者发送消息时写的routingKey要和消费者绑定的routingKey完全一致,才能接收到消息
QA:每个routingKey都要完全一致,假设服务A是订单模块,服务B是库存模块,那按照上述的方式我们要绑定海量的routingKey才可以,直接增大了开发量和业务的复杂性,如何简化这样的模式呢?
使用Exchange的topic模式,topic主要是实现对routingKey的模糊匹配,从而更加灵活的指定我们的消息发送给哪台服务器。
简示图:
#:表示0个或多个
例:服务A推送信息的routingKey
order.delete.yesterday queueB可以接收
order.list 可以接收 queueB可以接收
*:表示一个
warehouse.query queueA可以接收
warehouse.delete.yesterday queueA无法接收
服务A发送信息推送至routingA 时,队列queueA和队列queueB都可以接收到信息
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 生产者
* Topic Exchange
* 此时队列需要绑定要一个模式上。
* 符号“#”匹配一个或多个词,符号“*”匹配一个词。
* 因此“audit.#”能够匹配到“audit.irs.corporate”
* 但是“audit.*” 只会匹配到“audit.irs”。
* Created by py
* 2020/4/20
*/
public class SendRabbit {
private static String exchange = "test_exchange_topic";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
//确认每次只发送一条信息
channel.basicQos(1);
//声明一个交换机,队列中的key可以和交换机发送信息的key进行匹配时就能收到消息
channel.exchangeDeclare(exchange,"topic",true);
String msg = "This is test_exchange_topic";
/**
* basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body)
* exchange:交换机名称
* routingKey:队列绑定的交换机的key:指定的时候那个队列有这个key哪个队列就会收到交换机的信息
* body: 信息载体
* */
//todo routingKey:rabbit==》两个队列都绑定了rabbit所以交换机的信息两个队列都能收到
channel.basicPublish(exchange,"order.aaa.bbb",null,msg.getBytes());
System.out.println("发送完成");
channel.close();
instance.close();
}
}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消费者1
* Created by py
* 2020/4/20
*/
public class FristConsumer {
private static String exchange = "test_exchange_topic";
private static String queue = "test_exchange_topic_queue1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
channel.basicQos(1);
//设置让rabbit消息持久化
boolean durable = true;
channel.queueDeclare(queue,durable,false,false,null);
channel.queueBind(queue,exchange,"order.list");
channel.queueBind(queue,exchange,"order.add");
channel.queueBind(queue,exchange,"order.#");
channel.queueBind(queue,exchange,"audit.*");
//定义一个消费者
Consumer consumer = new DefaultConsumer(channel) {
//消息到达 触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
try {
String msg = new String(body,"utf-8");
System.out.println("FristConsumer:"+msg);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
boolean autoAck = false;
channel.basicConsume(queue, autoAck, consumer);
}
}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消费者2
* Created by py
* 2020/4/20
*/
public class SecondConsumer {
private static String exchange = "test_exchange_topic";
private static String queue = "test_exchange_topic_queue2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
channel.basicQos(1);
//设置让rabbit消息持久化
boolean durable = true;
channel.queueDeclare(queue,durable,false,false,null);
channel.queueBind(queue,exchange,"order.del");
channel.queueBind(queue,exchange,"audit.#");
channel.queueBind(queue,exchange,"order.*");
//定义一个消费者
Consumer consumer = new DefaultConsumer(channel) {
//消息到达 触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
try {
String msg = new String(body,"utf-8");
System.out.println("SecondConsumer:"+msg);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
boolean autoAck = false;
channel.basicConsume(queue, autoAck, consumer);
}
}