零:连接
public class RabbitUtils {
private static ConnectionFactory connectionFactory = new ConnectionFactory();
static {
connectionFactory.setHost("");
connectionFactory.setPort(5671);//5672是RabbitMQ的默认端口号
connectionFactory.setUsername("");
connectionFactory.setPassword("");
//connectionFactory.setVirtualHost("/");
}
public static Connection getConnection(){
Connection conn = null;
try {
conn = connectionFactory.newConnection();
return conn;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
一、简单模式
图解:
代码:
生产者:
package com.baiqi.rabbitmq.helloworld;
import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author 白起老师
*/
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//获取TCP长连接
Connection conn = RabbitUtils.getConnection();
//创建通信“通道”,相当于TCP中的虚拟连接
Channel channel = conn.createChannel();
//创建队列,声明并创建一个队列,如果队列已存在,则使用这个队列
//第一个参数:队列名称ID
//第二个参数:是否持久化,false对应不持久化数据,MQ停掉数据就会丢失
//第三个参数:是否队列私有化,false则代表所有消费者都可以访问,true代表只有第一次拥有它的消费者才能一直使用,其他消费者不让访问
//第四个:是否自动删除,false代表连接停掉后不自动删除掉这个队列
//其他额外的参数, null
channel.queueDeclare(RabbitConstant.QUEUE_HELLOWORLD,true, false, false, null);
String message = "成功";
//四个参数
//exchange 交换机,暂时用不到,在后面进行发布订阅时才会用到
//队列名称
//额外的设置属性
//最后一个参数是要传递的消息字节数组
channel.basicPublish("", RabbitConstant.QUEUE_HELLOWORLD, null,message.getBytes());
channel.close();
conn.close();
System.out.println("===发送成功===");
}
}
消费者:
package com.baiqi.rabbitmq.helloworld;
import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
//获取TCP长连接
Connection conn = RabbitUtils.getConnection();
//创建通信“通道”,相当于TCP中的虚拟连接
Channel channel = conn.createChannel();
//创建队列,声明并创建一个队列,如果队列已存在,则使用这个队列
//第一个参数:队列名称ID
//第二个参数:是否持久化,false对应不持久化数据,MQ停掉数据就会丢失
//第三个参数:是否队列私有化,false则代表所有消费者都可以访问,true代表只有第一次拥有它的消费者才能一直使用,其他消费者不让访问
//第四个:是否自动删除,false代表连接停掉后不自动删除掉这个队列
//其他额外的参数, null
channel.queueDeclare(RabbitConstant.QUEUE_HELLOWORLD,true, false, false, null);
//从MQ服务器中获取数据
//创建一个消息消费者
//第一个参数:队列名
//第二个参数代表是否自动确认收到消息,false代表手动编程来确认消息,这是MQ的推荐做法
//第三个参数要传入DefaultConsumer的实现类
channel.basicConsume(RabbitConstant.QUEUE_HELLOWORLD, false, new Reciver(channel));
}
}
class Reciver extends DefaultConsumer {
private Channel channel;
//重写构造函数,Channel通道对象需要从外层传入,在handleDelivery中要用到
public Reciver(Channel channel) {
super(channel);
this.channel = channel;
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body);
System.out.println("消费者接收到的消息:"+message);
System.out.println("消息的TagId:"+envelope.getDeliveryTag());
//false只确认签收当前的消息,设置为true的时候则代表签收该消费者所有未签收的消息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
二、Work queues 工作队列模式
图解:
代码:
发送者:
package com.baiqi.rabbitmq.workqueue;
import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.google.gson.Gson;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
*
* 发送者
*/
public class OrderSystem {
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = RabbitUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(RabbitConstant.QUEUE_SMS, false, false, false, null);
for(int i = 1 ; i <= 100 ; i++) {
SMS sms = new SMS("乘客" + i, "13900000" + i, "您的车票已预订成功");
String jsonSMS = new Gson().toJson(sms);
channel.basicPublish("" , RabbitConstant.QUEUE_SMS , null , jsonSMS.getBytes());
}
System.out.println("发送数据成功");
channel.close();
connection.close();
}
}
要发送的信息:
public class SMS {
private String name;
private String mobile;
private String content;
public SMS(String name, String mobile, String content) {
this.name = name;
this.mobile = mobile;
this.content = content;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
消费者1:
package com.baiqi.rabbitmq.workqueue;
import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @author 白起老师
* 消费者
*/
public class SMSSender1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitUtils.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(RabbitConstant.QUEUE_SMS, false, false, false, null);
//如果不写basicQos(1),则自动MQ会将所有请求平均发送给所有消费者
//basicQos,MQ不再对消费者一次发送多个请求,而是消费者处理完一个消息后(确认后),在从队列中获取一个新的
channel.basicQos(1);//处理完一个取一个
channel.basicConsume(RabbitConstant.QUEUE_SMS , false , new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String jsonSMS = new String(body);
System.out.println("SMSSender1-短信发送成功:" + jsonSMS);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
channel.basicAck(envelope.getDeliveryTag() , false);
}
});
}
}
消费者2:
package com.baiqi.rabbitmq.workqueue;
import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @author 白起老师
* 消费者
*/
public class SMSSender2 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitUtils.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(RabbitConstant.QUEUE_SMS, false, false, false, null);
//如果不写basicQos(1),则自动MQ会将所有请求平均发送给所有消费者
//basicQos,MQ不再对消费者一次发送多个请求,而是消费者处理完一个消息后(确认后),在从队列中获取一个新的
channel.basicQos(1);//处理完一个取一个
channel.basicConsume(RabbitConstant.QUEUE_SMS , false , new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String jsonSMS = new String(body);
System.out.println("SMSSender2-短信发送成功:" + jsonSMS);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
channel.basicAck(envelope.getDeliveryTag() , false);
}
});
}
}
三、Pub/Sub 订阅模式
图解:
代码:
注:此代码提前创建号交换机
发布者:
public class WeatherBureau {
public static void main(String[] args) throws Exception {
Connection connection = RabbitUtils.getConnection();
//键盘输入要发送的信息
String input=new Scanner(System.in).next();
//获取一个通道
Channel channel = connection.createChannel();
//发送消息
channel.basicPublish(RabbitConstant.EXCHANGE_WEATHER , "" , null , input.getBytes());
//关闭连接
channel.close();
connection.close();
}
}
接收者1:
public class Baidu {
public static void main(String[] args) throws Exception
{
//获取TCP长连接
Connection connection = RabbitUtils.getConnection();
//创建通信“通道”,相当于TCP中的虚拟连接
final Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(RabbitConstant.QUEUE_BAIDU,false, false, false, null);
//绑定队列到交换机,同时指定需要匹配的routing key。
channel.queueBind(RabbitConstant.QUEUE_BAIDU, RabbitConstant.EXCHANGE_WEATHER, "");
//设置每次发送数据个数
channel.basicQos(1);
//接收数据
channel.basicConsume(RabbitConstant.QUEUE_BAIDU, false, new DefaultConsumer(channel){
@Override
// 消息到达,触发这个方法
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("百度天气收到气象信息:" + new String(body));
// 手动ACK
channel.basicAck(envelope.getDeliveryTag(), false);
}
});
}
}
接收者2:
public class Sina {
public static void main(String[] args) throws Exception
{
//获取TCP长连接
Connection connection = RabbitUtils.getConnection();
//创建通信“通道”,相当于TCP中的虚拟连接
final Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(RabbitConstant.QUEUE_SINA, false, false, false, null);
//绑定队列到交换机,同时指定需要匹配的routing key。
channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER, "");
//设置每次发送数据个数
channel.basicQos(1);
//接收数据
channel.basicConsume(RabbitConstant.QUEUE_SINA, false, new DefaultConsumer(channel){
@Override
// 消息到达,触发这个方法
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("新浪天气收到气象信息:" + new String(body));
// 手动ACK
channel.basicAck(envelope.getDeliveryTag(), false);
}
});
}
}
四、Routing 路由模式
图解:
注:此代码提前创建号交换机
代码:
发送者:
package com.baiqi.rabbitmq.routing;
import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;
public class WeatherBureau {
public static void main(String[] args) throws Exception {
//创建一个存储省市的map集合
//前面这个key充当消息的routing key
Map area=new LinkedHashMap<String,String>();
area.put("beijing","北京");
area.put("shanghai","上海");
area.put("guangzhou","广州");
area.put("shenzhen","深圳");
area.put("chongqing","重庆");
area.put("hangzhou","杭州");
area.put("wuhan","武汉");
area.put("ningbo","宁波");
Connection connection = RabbitUtils.getConnection();
//键盘输入要发送的信息
String input=new Scanner(System.in).next();
//获取一个通道
Channel channel = connection.createChannel();
//发送消息
//1.获取迭代器
Iterator<Map.Entry<String, String> > iterator = area.entrySet().iterator();
//2.遍历集合
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
//发送消息
channel.basicPublish(RabbitConstant.EXCHANGE_WEATHER_ROUTING , entry.getKey() , null , entry.getValue().getBytes());
}
//关闭连接
channel.close();
connection.close();
}
}
接收者1:
package com.baiqi.rabbitmq.routing;
import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
public class Baidu {
public static void main(String[] args) throws Exception
{
//获取TCP长连接
Connection connection = RabbitUtils.getConnection();
//创建通信“通道”,相当于TCP中的虚拟连接
final Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(RabbitConstant.QUEUE_BAIDU,false, false, false, null);
//绑定队列到交换机,同时指定需要匹配的routing key。
channel.queueBind(RabbitConstant.QUEUE_BAIDU, RabbitConstant.EXCHANGE_WEATHER, "chongqing");
//设置每次发送数据个数
channel.basicQos(1);
//接收数据
channel.basicConsume(RabbitConstant.QUEUE_BAIDU, false, new DefaultConsumer(channel){
@Override
// 消息到达,触发这个方法
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("百度天气收到气象信息:" + new String(body));
// 手动ACK
channel.basicAck(envelope.getDeliveryTag(), false);
}
});
}
}
接收者2:
package com.baiqi.rabbitmq.routing;
import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
public class Sina {
public static void main(String[] args) throws Exception
{
//获取TCP长连接
Connection connection = RabbitUtils.getConnection();
//创建通信“通道”,相当于TCP中的虚拟连接
final Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(RabbitConstant.QUEUE_SINA, false, false, false, null);
//绑定队列到交换机,同时指定需要匹配的routing key。
channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER, "beijing");
channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER, "shanghai");
//设置每次发送数据个数
channel.basicQos(1);
//接收数据
channel.basicConsume(RabbitConstant.QUEUE_SINA, false, new DefaultConsumer(channel){
@Override
// 消息到达,触发这个方法
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("新浪天气收到气象信息:" + new String(body));
// 手动ACK
channel.basicAck(envelope.getDeliveryTag(), false);
}
});
}
}
五、Topics 通配符模式
Topic 类型与 Direct 相比,都是可以根据 RoutingKey 把消息路由到不同的队列。只不过 Topic 类型Exchange 可以让队列在绑定 Routing key 的时候使用通配符!
Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert
通配符规则:# 匹配一个或多个词,* 匹配不多不少恰好1个词,例如:item.# 能够匹配 item.insert.abc 或者 item.insert,item.* 只能匹配 item.insert
图解:
注:此代码提前创建号交换机
代码:
package com.baiqi.rabbitmq.topic;
import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;
public class WeatherBureau {
public static void main(String[] args) throws Exception {
//创建一个存储省市的map集合
//前面这个key充当消息的routing key
Map area=new LinkedHashMap<String,String>();
area.put("China.huhan.changsha.20250101", "中国-湖南-长沙-2025年1月1日");
area.put("China.guangdong.guangzhou.20250101", "中国-广东-广州-2025年1月1日");
area.put("China.guangdong.shenzhen.20250101", "中国-广东-深圳-2025年1月1日");
area.put("China.jiangsu.nanjing.20250101", "中国-江苏-南京-2025年1月1日");
Connection connection = RabbitUtils.getConnection();
//键盘输入要发送的信息
String input=new Scanner(System.in).next();
//获取一个通道
Channel channel = connection.createChannel();
//发送消息
//1.获取迭代器
Iterator<Map.Entry<String, String> > iterator = area.entrySet().iterator();
//2.遍历集合
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
//发送消息
channel.basicPublish(RabbitConstant.EXCHANGE_WEATHER_TOPIC , entry.getKey() , null , entry.getValue().getBytes());
}
//关闭连接
channel.close();
connection.close();
}
}
消费者1:
package com.baiqi.rabbitmq.topic;
import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
public class Sina {
public static void main(String[] args) throws Exception
{
//获取TCP长连接
Connection connection = RabbitUtils.getConnection();
//创建通信“通道”,相当于TCP中的虚拟连接
final Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(RabbitConstant.QUEUE_SINA, false, false, false, null);
//绑定队列到交换机,同时指定需要匹配的routing key。
channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER_TOPIC, "China.#");
//设置每次发送数据个数
channel.basicQos(1);
//接收数据
channel.basicConsume(RabbitConstant.QUEUE_SINA, false, new DefaultConsumer(channel){
@Override
// 消息到达,触发这个方法
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("新浪天气收到气象信息:" + new String(body));
// 手动ACK
channel.basicAck(envelope.getDeliveryTag(), false);
}
});
}
}
消费者2:
package com.baiqi.rabbitmq.topic;
import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
public class Baidu {
public static void main(String[] args) throws Exception
{
//获取TCP长连接
Connection connection = RabbitUtils.getConnection();
//创建通信“通道”,相当于TCP中的虚拟连接
final Channel channel = connection.createChannel();
//创建队列
channel.queueDeclare(RabbitConstant.QUEUE_BAIDU,false, false, false, null);
//绑定队列到交换机,同时指定需要匹配的routing key。
channel.queueBind(RabbitConstant.QUEUE_BAIDU, RabbitConstant.EXCHANGE_WEATHER_TOPIC, "*.*.*.20250101");
//设置每次发送数据个数
channel.basicQos(1);
//接收数据
channel.basicConsume(RabbitConstant.QUEUE_BAIDU, false, new DefaultConsumer(channel){
@Override
// 消息到达,触发这个方法
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("百度天气收到气象信息:" + new String(body));
// 手动ACK
channel.basicAck(envelope.getDeliveryTag(), false);
}
});
}
}
总结
1.
简单模式 HelloWorld
一个生产者、一个消费者,不需要设置交换机(使用默认的交换机)。
2.
工作队列模式 Work Queue
一个生产者、多个消费者(竞争关系),不需要设置交换机(使用默认的交换机)。
3.
发布订阅模式 Publish/subscribe
需要设置类型为 fanout 的交换机,并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列。
4.
路由模式 Routing
需要设置类型为 direct 的交换机,交换机和队列进行绑定,并且指定 routing key,当发送消息到交换机后,交换机会根据 routing key 将消息发送到对应的队列。
5.
通配符模式 Topic
需要设置类型为 topic 的交换机,交换机和队列进行绑定,并且指定通配符方式的 routing key,当发送消息到交换机后,交换机会根据 routing key 将消息发送到对应的队列。
消息确认机制
Broker:RabbitMQ
package com.baiqi.rabbitmq.confirm;
import com.baiqi.rabbitmq.utils.RabbitConstant;
import com.baiqi.rabbitmq.utils.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;
public class WeatherBureau {
public static void main(String[] args) throws Exception {
//创建一个存储省市的map集合
//前面这个key充当消息的routing key
Map area=new LinkedHashMap<String,String>();
area.put("China.huhan.changsha.20250101", "中国-湖南-长沙-2025年1月1日");
area.put("China.guangdong.guangzhou.20250101", "中国-广东-广州-2025年1月1日");
area.put("China.guangdong.shenzhen.20250101", "中国-广东-深圳-2025年1月1日");
area.put("China.jiangsu.nanjing.20250101", "中国-江苏-南京-2025年1月1日");
//获取连接
Connection connection = RabbitUtils.getConnection();
//获取一个通道
Channel channel = connection.createChannel();
//开启confirm监听模式
channel.confirmSelect();
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long l, boolean b) throws IOException {
//第二个参数表示是否批量接收
System.out.println("消息已经被Broker接收:" + l);
}
@Override
public void handleNack(long l, boolean b) throws IOException {
System.out.println("消息被Broker拒绝接收:" + l);
}
});
channel.addReturnListener(new ReturnCallback() {
@Override
public void handle(Return aReturn) {
System.err.println("Return编码:" + aReturn.getReplyCode()+ " Return信息:" + aReturn.getReplyText());
System.err.println("交换机:" + aReturn.getExchange()+ " RoutingKey:" + aReturn.getRoutingKey());
System.err.println("消息:" + new String(aReturn.getBody()));
}
});
Iterator<Map.Entry<String, String> > iterator = area.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
//发送消息
//第三个参数表示是否持久化,如果为false,消息会存到内存中,重启后丢失,直接将消息放弃
channel.basicPublish(RabbitConstant.EXCHANGE_WEATHER_TOPIC , entry.getKey() ,true, null , entry.getValue().getBytes());
}
//不能关,保持监听
/*channel.close();
connection.close();*/
}
}