本文详细介绍简单模式Simple、工作模式Work、发布订阅模式Publish/Subscribe、路由模式Routing、通配符模式Topics、远程调用模式RPC(暂不对该队列模式进行详解)
模式1:简单模式(Simple / HelloWorld 单生产单消费)
简单的发送与接收,没有特别的处理。
RabbitMQ连接(公共的连接方法,其他模式共用此方法)
package com.sc.queuemode.connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class ConnectionUtil {
public static Connection getConnection() throws IOException, TimeoutException {
//定义连接池
ConnectionFactory connectionFactory = new ConnectionFactory();
//连接地址
connectionFactory.setHost("localhost");
//连接端口
connectionFactory.setPort(5672);
//用户名
connectionFactory.setUsername("guest");
//密码
connectionFactory.setPassword("guest");
//通过连接工厂获取连接
Connection connection = connectionFactory.newConnection();
//返回连接
return connection;
}
}
Producer:
package com.sc.queuemode.simple;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.sc.queuemode.connection.ConnectionUtil;
public class Producter {
private final static String QUEUE_NAME = "hello";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtil.getConnection();
//从连接中声明通道
Channel channel = connection.createChannel();
//队列申明
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//消息内容
String message = "simple queue hello world !";
//推送发布消息
//basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
//信道关闭
channel.close();
//连接关闭
connection.close();
}
}
Consumer:
package com.sc.queuemode.simple;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
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;
import com.sc.queuemode.connection.ConnectionUtil;
public class Consumer1 {
private final static String QUEUE_NAME = "hello_queue";
public static void main(String[] args) throws IOException, TimeoutException{
//获取连接
Connection connection = ConnectionUtil.getConnection();
//声明信道
Channel channel = connection.createChannel();
//声明队列
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 message = new String(body,"UTF-8");
System.out.println("customer 消费消息:"+message);
}
};
channel.basicConsume(QUEUE_NAME, true,consumer);
}
}
模式2:工作模式(Work单发送多接收)
一个生产者端,多个消费者端。示例中为了保证消息发送的可靠性,使队列持久化了。同时为了防止接收端在处理消息时down掉,只有在消息处理完成后才发送消息确认。
Producer:
package com.sc.queuemode.work;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.sc.queuemode.connection.ConnectionUtil;
public class Producter {
private final static String QUEUE_NAME = "work_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtil.getConnection();
//声明信道
Channel channel = connection.createChannel();
//队列申明,durable:true消息持久化
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
//消息内容
String message ="hello work queue";
for(int i = 0; i < 20; i++) {
//发布消息
//basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
channel.basicPublish("", QUEUE_NAME, null, (i + " " +message).getBytes());
}
//关闭通道
channel.close();
//连接关闭
connection.close();
}
Consumer:
消费者1
package com.sc.queuemode.work;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
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;
import com.sc.queuemode.connection.ConnectionUtil;
/*
* 消费者1
*/
public class Consumer1 {
private final static String QUEUE_NAME = "work_queue";
public static void main(String[] args) throws IOException, TimeoutException{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
//保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
//在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
channel.basicQos(1);
//声明队列的消费者O
Consumer consumer1 = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
//channel.basicConsume(QUEUE_NAME, false, consumer1);
String message = new String(body, "UTF-8");
System.out.println("customer1 消费消息:"+message);
//手动返回结果
channel.basicAck(envelope.getDeliveryTag(), false);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//定义的消费者监听队列 autoAck:true自动返回结果,false手动返回
channel.basicConsume(QUEUE_NAME, false,consumer1);
}
}
消费者2
package com.sc.queuemode.work;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
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;
import com.sc.queuemode.connection.ConnectionUtil;
/*
* 消费者2
*/
public class Consumer2 {
private final static String QUEUE_NAME = "work_queue";
public static void main(String[] args) throws IOException, TimeoutException{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
channel.basicQos(1);
//声明队列的消费者
Consumer consumer2 = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
//channel.basicConsume(QUEUE_NAME, false, consumer1);
String message = new String(body, "UTF-8");
System.out.println("customer2 消费消息:"+message);
channel.basicAck(envelope.getDeliveryTag(), false);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//定义的消费者监听队列(第二个参数:true自动返回结果,false手动返回)
channel.basicConsume(QUEUE_NAME, false,consumer2);
}
}
模式3:发布、订阅模式(Publish/Subscribe)
使用场景:发布、订阅模式,生产者端发送消息,多个消费者同时接收所有的消息。
Producer:
package com.sc.queuemode.publish_subscrible;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.sc.queuemode.connection.ConnectionUtil;
public class Producter {
private final static String EXCHANGE_NAME = "publishSubscrible_exchange";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明交换机Exchange类型为fanout
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
String message = "publish/subscrible hello world";
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
System.out.println("发布订阅 生产者 发布消息:"+message);
channel.close();
connection.close();
}
}
生产者端发布消息到交换机,使用“fanout”方式发送,即广播消息,不需要使用queue,发送端不需要关心谁接收。
消费者1:
package com.sc.queuemode.publish_subscrible;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
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.ConsumerCancelledException;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.ShutdownSignalException;
import com.sc.queuemode.connection.ConnectionUtil;
public class Customer1 {
//队列1
private final static String QUEUE_NAME = "publishSubscrible_queue";
private final static String EXCHANGE_NAME = "publishSubscrible_exchange";
public static void main(String[] args) throws IOException, TimeoutException{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
//声明队列
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
//绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
//保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
//在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
channel.basicQos(1);
//申明消费者
Consumer consumer1 = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body, "UTF-8");
System.out.println("发布订阅 消费者1 消费消息:"+message);
//手动返回结果
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
channel.basicConsume(QUEUE_NAME, false, consumer1);
}
}
消费者2:
package com.sc.queuemode.publish_subscrible;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
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;
import com.sc.queuemode.connection.ConnectionUtil;
public class Customer2 {
//队列2
private final static String QUEUE_NAME = "publishSubscrible_queue2";
private final static String EXCHANGE_NAME = "publishSubscrible_exchange";
public static void main(String[] args) throws IOException, TimeoutException{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
//声明队列
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
//绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
//保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
//在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
channel.basicQos(1);
//申明消费者
Consumer consumer2 = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body, "UTF-8");
System.out.println("发布订阅 消费者2 消费消息:"+message);
//手动返回结果
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
channel.basicConsume(QUEUE_NAME, false, consumer2);
}
}
消费者端:
1、声明和生产者端一样的交换机。
2、注意binding queue的时候,channel.queueBind()的第三个参数Routing key为空,即所有的消息都接收。如果这个值不为空,在exchange type为“fanout”方式下该值被忽略!
模式4:路由模式(Routing)
生产者按routing key发送消息,不同的消费者端按不同的routing key接收消息。
Producer:
package com.sc.queuemode.routing;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.sc.queuemode.connection.ConnectionUtil;
public class Producter {
private final static String EXCHANGE_NAME = "routing_exchange";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明交换机Exchange类型为direct
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//发布消息3种routingKey的消息
String message = "hello info";
channel.basicPublish(EXCHANGE_NAME, "info", null, message.getBytes());
System.out.println("路由模式发布info消息:"+message);
message = "hello warning";
channel.basicPublish(EXCHANGE_NAME, "warning", null, message.getBytes());
System.out.println("路由模式发布warning消息:"+message);
message = "hello error";
channel.basicPublish(EXCHANGE_NAME, "error", null, message.getBytes());
System.out.println("路由模式发布error消息:"+message);
}
}
消费者端和模式3(发布订阅模式)的区别:
1、exchange的type为direct
2、发送消息的时候加入了routing key
Consumer:
消费者1
package com.sc.queuemode.routing;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
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;
import com.sc.queuemode.connection.ConnectionUtil;
public class Customer1 {
private final static String QUEUE_NAME = "routing_queue1";
private final static String EXCHANGE_NAME = "routing_exchange";
public static void main(String[] args) throws IOException, TimeoutException{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//申明队列
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
//队列绑定交换机,指定路由routingKey
//结束路由routingKey为info和warning的消息
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "info");
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "warning");
//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
//保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
//在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
channel.basicQos(1);
//声明消费者
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body, "UTF-8");
System.out.println("路由模式 消费者1 消费消息:"+message);
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
channel.basicConsume(QUEUE_NAME, false, consumer);
}
}
消费者2
package com.sc.queuemode.routing;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
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;
import com.sc.queuemode.connection.ConnectionUtil;
public class Customer2 {
private final static String QUEUE_NAME = "routing_queue2";
private final static String EXCHANGE_NAME = "routing_exchange";
public static void main(String[] args) throws IOException, TimeoutException{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//申明队列
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
//队列绑定交换机,指定路由routingKey
//结束路由routingKey为error的消息
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "error");
//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
//保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
//在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
channel.basicQos(1);
//声明消费者
Consumer consumer2 = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body, "UTF-8");
System.out.println("路由模式 消费者2 消费消息:"+message);
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
channel.basicConsume(QUEUE_NAME, false, consumer2);
}
}
消费者端和模式3(发布订阅模式)的区别:
在绑定queue和exchange的时候使用了路由routing key,即从该exchange上只接收routing key指定的消息。
模式5:通配符(或主题)模式(Topics ,按topic发送接收)
生产者端不只按固定的routing key发送消息,而是按字符串“匹配”发送,消费者端同样如此。
与之前的路由模式相比,它将信息的传输类型的key更加细化,以“key1.key2.keyN…”的模式来指定信息传输的key的大类型和大类型下面的小类型,让消费者端可以更加精细的确认自己想要获取的信息类型。而在消费者端,不用精确的指定具体到哪一个大类型下的小类型的key,而是可以使用类似正则表达式(但与正则表达式规则完全不同)的通配符在指定一定范围或符合某一个字符串匹配规则的key,来获取想要的信息。“通配符交换机”(Topic Exchange)将路由键和某模式进行匹配。此时队列需要绑定在一个模式上。符号“#”匹配一个或多个词,符号“*”仅匹配一个词。
Producer:
package com.sc.queuemode.topics;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.sc.queuemode.connection.ConnectionUtil;
public class Producter {
private static final String EXCHANGE_NAME = "topic_exchange";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
Channel channel =connection.createChannel();
//声明交换机类型为topic
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String message = "发布了一条中国新闻消息";
channel.basicPublish(EXCHANGE_NAME, "china.news", null, message.getBytes());
message = "发布了一条中国天气消息";
channel.basicPublish(EXCHANGE_NAME, "china.weather", null, message.getBytes());
message = "发布了一条美国新闻消息";
channel.basicPublish(EXCHANGE_NAME, "usa.news", null, message.getBytes());
message = "发布了一条美国天气消息";
channel.basicPublish(EXCHANGE_NAME, "usa.weather", null, message.getBytes());
}
}
生产者和模式4(路由模式)的区别:
1、交换机exchange的type为topic
2、发送消息的routing key不是固定的单词,而是匹配字符串,如"china.#",*匹配一个单词,#匹配0个或多个单词。因此如“china.#”能够匹配到“china.news.info”,但是“china.* ”只会匹配到“china.news”
Consumer:
消费者1
package com.sc.queuemode.topics;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
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;
import com.sc.queuemode.connection.ConnectionUtil;
public class Customer1 {
private static final String QUEUE_NAME = "topics_queue1";
private static final String EXCHANGE_NAME = "topic_exchange";
public static void main(String[] args) throws IOException, TimeoutException{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//交换机申明
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
//队列声明
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
//队列绑定交换机并制定路由routingKey
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "china.#");
//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
//保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
//在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
channel.basicQos(1);
//申明消费者
Consumer consumer1 = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body, "UTF-8");
System.out.println("通配符模式 消费者1消费:"+message);
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
channel.basicConsume(QUEUE_NAME, false, consumer1);
}
}
消费者2
package com.sc.queuemode.topics;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
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;
import com.sc.queuemode.connection.ConnectionUtil;
public class Customer2 {
private static final String QUEUE_NAME = "topics_queue2";
private static final String EXCHANGE_NAME = "topic_exchange";
public static void main(String[] args) throws IOException, TimeoutException{
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//交换机申明
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
//队列声明
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
//队列绑定交换机并制定路由routingKey
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "usa.#");
//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
//保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
//在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
channel.basicQos(1);
//申明消费者
Consumer consumer2 = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body, "UTF-8");
System.out.println("通配符模式 消费者2消费:"+message);
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
channel.basicConsume(QUEUE_NAME, false, consumer2);
}
}
---------------------
作者:通往架构师的路上
来源:CSDN
原文:https://blog.csdn.net/caiqing116/article/details/84032099
版权声明:本文为博主原创文章,转载请附上博文链接!