1.Rabbitmq安装(Docker)
- 拉取镜像并启动
# 注意,如果想使用rabbitmq管理平台,则需拉取带有management的tag
$ docker pull rabbitmq:3.8-rc-management-alpine
# 指定端口映射
$ docker run -d -p 5671:5671 -p 5672:5672 -p 15672:15672 -p 15671:15671 -p 25672:25672 -v /data/rabbitmq-data/:/var/rabbitmq/lib --name rabbitmq rabbitmq:3.8-rc-management-alpine
-
通过管理平台配置rabbitmq
(1)浏览器键入
http://localhost:15672
,输出账户名密码,默认均为guest
(2)新增用户:下图展示新增用户步骤,选择新增用户->输入用户名密码->增加用户权限即可
(3)配置虚拟主机:通过虚拟主机进行消息隔离,只有相同的虚拟主机中才能获取所需数据
2.原生方式使用rabbitmq模式介绍
rabbitmq一共包含如下几种模式,下边对每种模式进行案例演示。
- 创建项目,引入依赖,书写基础代码
<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.6.0</version>
</dependency>
</dependencies>
public class ConnectUtils {
public static Connection getConnect() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("admin");
factory.setVirtualHost("basic-rabbit");
Connection connection = factory.newConnection();
return connection;
}
}
- 直连模式
直连模式,生产者直接将消息发送至消息队列,消费者直接从消息队列中取出消息
消息提供方代码:
public class BasicSender {
private static final String BASIC_QUEUE = "basic_queue";
private static Logger logger = LoggerFactory.getLogger(BasicSender.class);
public static void main(String[] args) throws IOException, TimeoutException {
Connection conn = ConnectUtils.getConnect();
Channel channel = conn.createChannel();
channel.queueDeclare(BASIC_QUEUE, false, false, false, null);
String msg = "hello rabbit";
channel.basicPublish("", BASIC_QUEUE, null, msg.getBytes());
logger.info("发送消息:{}", msg);
}
}
说明:
1.channel:所有消息的发送都是通过channel
2.channel.queueDeclare()参数:声明队列。
第一个参数:队列名
,
第二个参数:是否持久化
,
第三个参数:表示消息是否独占
第四个参数:指消息发送完成,就将消息从队列中删除
第五个参数:构造队列中参数
消息接收方代码:
public class BasicReceiv {
private static final String BASIC_QUEUE = "basic_queue";
private static Logger logger = LoggerFactory.getLogger(BasicSender.class);
public static void main(String[] args) throws IOException, TimeoutException {
Connection conn = ConnectUtils.getConnect();
Channel channel = conn.createChannel();
channel.queueDeclare(BASIC_QUEUE, false, false, false, null);
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
BasicProperties properties,
byte[] body) throws UnsupportedEncodingException {
String msg = new String(body, "UTF-8");
logger.info("收到消息=> " + msg);
}
};
channel.basicConsume(BASIC_QUEUE, true,"",defaultConsumer);
}
}
说明:
channel.basicConsume():第二个参数表示是否自动确认消息
,如果为true表示自动确认消息,即消息发送完成就删除,如果设置为false,则需手动处理确认,在后续代码中会讲解
- 工作队列模式
指一个消息生产端发送消息,两个消费端消费消息,那么两个消费端将轮询消费消息,代码依然使用
直连模式
代码,启动两个消费都和一个生产者,生产者生产消息,两个消费者轮训消费消息
- fanout模式
消息提供者将消息发送至
exchange
,交换机根据路由键匹配至具体的队列中,在fanout模式中没有路由键的概念,即会将消息分别投递给订阅了该交换机的消费者
发送端代码:
public class FanoutSender {
private static final String FANOUT_QUEUE = "fanout_queue";
private static final String EXCHANGE_FANOUT = "exchange_fanout";
private static Logger logger = LoggerFactory.getLogger(FanoutSender.class);
public static void main(String[] args) throws IOException, TimeoutException {
Connection conn = ConnectUtils.getConnect();
Channel channel = conn.createChannel();
channel.queueDeclare(FANOUT_QUEUE, false, false, false, null);
channel.exchangeDeclare(EXCHANGE_FANOUT, "fanout");
String msg = "hello rabbit";
channel.basicPublish(EXCHANGE_FANOUT, "", null, msg.getBytes());
logger.info("发送消息:{}", msg);
channel.close();
conn.close();
}
}
说明:通过channel.exchangeDeclare('exchange_name','fanout')
,fanout:交换机模式,消息发送端将消息发送至交换机中
接收端代码
public class FanoutReceiv {
private static final String FANOUT_QUEUE = "fanout_queue";
private static final String EXCHANGE_FANOUT = "exchange_fanout";
private static Logger logger = LoggerFactory.getLogger(BasicSender.class);
public static void main(String[] args) throws IOException, TimeoutException {
Connection conn = ConnectUtils.getConnect();
Channel channel = conn.createChannel();
channel.queueDeclare(FANOUT_QUEUE, false, false, false, null);
channel.queueBind(FANOUT_QUEUE, EXCHANGE_FANOUT, "");
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
BasicProperties properties,
byte[] body) throws UnsupportedEncodingException {
String msg = new String(body, "UTF-8");
logger.info("收到消息=> " + msg);
}
};
channel.basicConsume(FANOUT_QUEUE, true, "", defaultConsumer);
}
}
说明:
1.消息接收者声明通道,声明的交换机与发送端交换机名字相同
2.通过channel.queueBind()将接收端与交换机绑定,接收端将消息从交换机中读取至队列中
- Routing模式
根据不同的路由键,将消息从交换机路由至指定的消息队列中
发送端代码
public class RoutingSender {
private static final String EXCHANGE_DIRECT = "exchange_direct";
private static final String ROUTING_KEY = "apple";
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接
Connection connect = ConnectUtils.getConnect();
Channel channel = connect.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_DIRECT, "direct");
//消息定义
String msg = "hello rabbit --apple";
channel.basicPublish(EXCHANGE_DIRECT, ROUTING_KEY, null, msg.getBytes());
//关闭连接
channel.close();
connect.close();
}
}
接收端代码
public class RoutingReceiv {
private static final String QUEUE_APPLIE = "queue_apple";
private static final String EXCHANGE_DIRECT = "exchange_direct";
private static final String ROUTING_KEY = "apple";
private static Logger logger = LoggerFactory.getLogger(BasicSender.class);
public static void main(String[] args) throws IOException, TimeoutException {
Connection conn = ConnectUtils.getConnect();
Channel channel = conn.createChannel();
channel.queueDeclare(QUEUE_APPLIE, false, false, false, null);
channel.queueBind(QUEUE_APPLIE, EXCHANGE_DIRECT, ROUTING_KEY);
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
BasicProperties properties,
byte[] body) throws UnsupportedEncodingException {
String msg = new String(body, "UTF-8");
logger.info("收到消息=> " + msg);
}
};
channel.basicConsume(QUEUE_APPLIE, true, "", defaultConsumer);
}
}
说明:只接收routing_key
为apple
的消息
- Topic模式
topic模式,会根据通配符进行路由匹配,*:表示匹配一个,#:表示匹配多个,其它操作与Routing模式相同,在此就不做代码演示
3.确认机制
- 事务
当发生端后续代码出现问题时,可以通过事务控制,而回滚消息,主要api如下
channel.txSelect():开启事务channel.txRollback():回滚事务
channel.txCommit():提交事务
发送端代码:
public class RoutingSender {
private static final String EXCHANGE_DIRECT = "exchange_direct";
private static final String ROUTING_KEY = "apple";
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接
Connection connect = ConnectUtils.getConnect();
Channel channel = connect.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_DIRECT, "direct");
//消息定义
String msg = "hello rabbit --apple";
try{
channel.txSelect();
channel.basicPublish(EXCHANGE_DIRECT, ROUTING_KEY, null, msg.getBytes());
channel.txCommit();
}catch (Exception e){
channel.txRollback();
}
int i=1/0;
//关闭连接
channel.close();
connect.close();
}
}
说明:
1.如果没有事务控制,则在发送端后续代码出现问题时,发送端依然能将消息发送成功,且消费端能消费消息
2.使用事务控制,效率不高
- 确认模式
确认模式与事务类型,有如下几个api
channel.confirmSelect():开启发送方确认channel.waitForConfirms():普通发送方确认模式
channel.waitForConfirmsOrDie():批量确认模式
channel.addConfirmListener():异步监听发送方确认模式
(1)普通发送方确认模式
channel.confirmSelect();
channel.basicPublish(EXCHANGE_DIRECT, ROUTING_KEY, null, msg.getBytes());
if(channel.waitForConfirms()){
System.out.println("消息发送成功");
}
(2)批量确认模式
channel.confirmSelect();
for(int i=0;i<10;i++){
channel.basicPublish(EXCHANGE_DIRECT, ROUTING_KEY, null, msg.getBytes());
}
channel.waitForConfirmsOrDie();
(3)异步confirm模式
channel.confirmSelect();
for(int i=0;i<10;i++){
channel.basicPublish(EXCHANGE_DIRECT, ROUTING_KEY, null, msg.getBytes());
}
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.out.println("未确认消息:"+deliveryTag);
}
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.out.println("收到消息:"+deliveryTag);
}
});