1. 简单模式(实现一发一接的简单结构,应用场景:手机短信、邮件单发)
一个队列queue只被一个消费者监听消费。生产者生成消息发送到交换机,交换机根据消息属性将消息发送给队列,消费者监听这个队列发现消息后,获取消息执行消费逻辑。
public class SimpleMode {
//准备连接对象连接rabbitmq
private Channel channel;
@Before
public void initChannel() throws IOException, TimeoutException {
//连接需要4个必要属性 ip port username password
ConnectionFactory factory=new ConnectionFactory();
factory.setHost(ip);
factory.setPort(port);
factory.setUsername(username );
factory.setPassword(password);
//从连接工厂获取一个长连接对象
Connection connection = factory.newConnection();
channel=connection.createChannel();//从长连接获取短连接
}
@Test
//声明一个交换机和一个队列
public void declare() throws IOException {
//声明交换机,可以使用默认的交换机(AMQP default)此交换机不需要完成自定义的队列绑定逻辑
//声明队列
channel.queueDeclare(
//新建自定义队列,如果队列已存在会使用本次声明的属性覆盖原有队列
"simple-queue01",
//boolean类型,表示是否对队列进行持久化的机制
true,
//boolean类型,表示队列是否专属于当前的连接
false,
//boolean类型,表示是否自动删除,会在最后一个channel断开队列连接后自动从rabbtimq删除
false,
//声明队列时,自定义的一些属性 map类型对象
null);
//须提供固定的key值,可以表示队列最大长度,最大存活时间,最大容量byte等属性
}
//生产端
@Test
public void producer() throws IOException {
//向交换机发送一条消息,必须清楚这个消息应该会被交换机发送到哪个队列
String msg="hello world simple mode";//准备一个发送的消息
//调用channel的api将消息发送给默认交换机
channel.basicPublish(
//交换机名字,(AMQP default) 名字就是空字符串
"",
//队列名称
"simple-queue01",
//BasicProperties 设置消息属性值,内容类型,内容编解码,优先级,用户id,集群id,应用id等
null,
//消息二进制byte数组
msg.getBytes());
}
//消费端
@Test
public void consumer() throws Exception {
//消费端,通过连接chennel对象,绑定到rabbitmq队列中,实现监听拿到的内容
//创建一个消费对象,绑定到channel
QueueingConsumer consumer=new QueueingConsumer(channel);
//实现consumer监听 simple-queue01
channel.basicConsume("simple-queue01",true,consumer);
//监听获取消息
while(true){//目的是让当前测试进程不停止
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
//nextDelivery会根据目前消费逻辑,从queue获取消息,可以一次获取一条,也可以一次获取多条 delivery包含了消息对象的所有内容
//打印消息msg
System.out.println("消费者获取消息:"+new String(delivery.getBody()));
//手动确认收到消息
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
AMQP.BasicProperties properties = delivery.getProperties();
//优先级数字
properties.getPriority();
properties.getAppId();
}
}
}
2. 争抢模式(应用场景:抢红包、资源分配系统)
生产者发送消息到交换机,交换机根据消息属性将消息发送给队列,多个消费者同时绑定监听一个队列,之间形成了争抢消息的效果。
public class WorkMode {
private Channel channel;
@Before
public void initChannel() throws IOException, TimeoutException {
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("6.6.6.6");
factory.setPort(8888);
factory.setUsername("name");
factory.setPassword("password");
Connection connection = factory.newConnection();
channel=connection.createChannel();
}
//声明组件
private static final String queueName="work-queue01";
@Test
public void declare() throws IOException {
channel.queueDeclare(queueName,true,false,false,null);
}
@Test//为了测试被争抢了,发送100条
public void producer() throws IOException {
for(int i=0;i<100;i++){
String msg="hello world work ["+i+"] msgs";
//向交换机发消息
channel.basicPublish("",queueName,null,msg.getBytes());
System.out.println("生产端发送到了第"+i+"条消息");
}
}
@Test//消费端1
public void consume1() throws IOException {
//一个消费端进程空闲时,没拿到消息时最多只给他发送一条消息,默认批量发送
channel.basicQos(1);
DeliverCallback callback=new DeliverCallback() {
@Override
public void handle(String consumerTag, Delivery message) throws IOException {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者1接收:"+new String(message.getBody()));
//上述代码运行成功表示消费逻辑执行正确,手动回执发送ack信号给队列
channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
}
};
//消费监听
channel.basicConsume(qName, false, callback, new CancelCallback() {
@Override
public void handle(String consumerTag) throws IOException {}
});
while (true);
}
@Test//消费端2
public void consume2() throws IOException {
channel.basicQos(1);
DeliverCallback callback=new DeliverCallback() {
@Override
public void handle(String consumerTag, Delivery message) throws IOException {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者2接收:"+new String(message.getBody()));
//上述代码运行成功表示消费逻辑执行正确,手动回执发送ack信号给队列
channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
}
};
//消费监听
channel.basicConsume(qName, false, callback, new CancelCallback() {
@Override
public void handle(String consumerTag) throws IOException {}
});
while (true);
}
}
3. 路由模式
生产端发送的消息携带具体的路由key值。交换机接收路由值,判断和当前交换机绑定后端队列哪个满足路由的匹配将消息发送给这个队列。上面简单模式和争抢模式的代码demo中使用的(AMQP default)就是属于路由类型的交换机,固定了路由就是队列名称。
public class DirectMode {
private Channel channel;
@Before
public void initChannel() throws IOException, TimeoutException {
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("6.6.6.6");
factory.setPort(8888);
factory.setUsername("name");
factory.setPassword("password");
Connection connection = factory.newConnection();
channel=connection.createChannel();
}
//声明组件 2个队列 一个交换机,声明绑定关系
//交换机类型->路由:direct、主题:topic、发布订阅:fanout
private static final String TYPE="direct";//交换机类型
private static final String EXNAME="EX_"+TYPE;//交换机名称
private static final String Q01="QUEUE01_"+TYPE;//队列1名称
private static final String Q02="QUEUE02_"+TYPE;//队列2名称
@Test//声明组件的客户端
public void declare() throws Exception {
//声明队列
channel.queueDeclare(Q01,false,false,false,null);
channel.queueDeclare(Q02,false,false,false,null);
//声明交换机和交换机类型
channel.exchangeDeclare(EXNAME,TYPE);
//队列和交换机绑定
//路由模式代码
channel.queueBind(Q01,EXNAME,"10");
channel.queueBind(Q02,EXNAME,"11");
channel.queueBind(Q02,EXNAME,"12");
/*主题模式代码
channel.queueBind(Q01,EXNAME,"10.#");
channel.queueBind(Q01,EXNAME,"10.11.*");
channel.queueBind(Q02,EXNAME,"*.10.*");
channel.queueBind(Q02,EXNAME,"*.*.*.12");*/
}
@Test//发送消息
public void producer() throws IOException {
//路由模式代码(该消息会被发送到队列Q02中)
channel.basicPublish(EXNAME,"12",null,"hi".getBytes());
/*主题模式代码(该消息会被发送到队列Q01中)
channel.basicPublish(EXNAME,"10.11.12",null,"hi".getBytes());*/
}
}
4. 发布订阅(参考路由模式代码)
声明组件、交换机以及多个队列,只要是多个队列绑定了一个发布订阅类型的交换机,只要交换机接收到生产端消息忽略路由匹配全部同步复制发送。
5. 主题模式(参考路由模式代码)
与路由模式类似,不同的是在主题模式中队列绑定交换机使用的路由名字不再是具体的字符串,而是使用替代符号 ' * '或' # '。