什么是RabbitMQ
RabbitMQ是一款开源的消息队列服务软件,它基于AMQP(Advanced Message Queue Protocol)协议实现,由LShift提供的一个高级消息队列协议的开源实现。RabbitMQ服务器是用Erlang语言编写的,集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。
在分布式系统中,消息队列被广泛使用。RabbitMQ作为消息队列的一种,可以提高系统响应速度,通过异步处理方式将耗时长的操作由消息队列通知消息接收方进行异步处理,从而提高应用程序的响应时间。此外,RabbitMQ还可以消除峰值,提高系统稳定性,服务解耦,排序保证,消除峰值等。
RabbitMQ具有以下主要特性:
- 消息持久化:RabbitMQ支持消息持久化,即消息不会因为系统崩溃或重启而丢失。
- 消息可靠性:RabbitMQ支持消息的可靠传输,确保消息不会因为网络故障或节点故障而丢失。
- 消息队列隔离:RabbitMQ支持消息队列的隔离,不同的消费者可以消费不同的消息类型,从而实现解耦和扩展。
- 消息路由:RabbitMQ支持消息路由,可以通过路由键将消息路由到不同的队列或消费者。
- 消息顺序保证:RabbitMQ支持消息顺序保证,即同一消息的多个实例在所有消费者中按照发送顺序进行接收。
- 消息持久化存储:RabbitMQ支持将消息持久化到磁盘上,从而保证即使在系统崩溃的情况下也不会丢失消息。
- 高可用性:RabbitMQ支持高可用性,可以通过集群和备份节点来提高系统的可用性和稳定性。
- 插件式扩展:RabbitMQ支持插件式扩展,可以通过插件来实现各种功能和特性。
- 多语言客户端支持:RabbitMQ支持多种编程语言客户端,包括Java、Python、Ruby、PHP、C#、JavaScript等。
- 开源社区活跃:RabbitMQ有一个活跃的开源社区,用户可以方便地获取帮助和支持。
RabbitMQ主要的业务场景
RabbitMQ在许多业务场景中都有广泛的应用。以下是几个主要的业务场景:
- 单发送单接收:这是最简单的使用场景,一个生产者向队列发送消息,一个消费者从队列接收消息。
- 单发送多接收:一个发送端向多个接收端发送消息,适用于分布式任务派发等场景。为了保证消息的可靠性,消息在发送后被持久化,并且只有当消息处理完成后才会发送确认消息。
- Publish/Subscribe:发布订阅模式,发送端发送广播消息,多个接收端接收。这种模式可以实现消息的广播和订阅,适用于实现事件驱动的系统。
- Routing (按路线发送接收):发送端按routing key发送消息,不同的接收端按不同的routing key接收消息。这种模式可以实现灵活的消息路由,适用于需要根据不同的key进行消息处理的场景。
- Topics (按topic发送接收):发送端不只按固定的routing key发送消息,而是按字符串“匹配”发送,接收端同样如此。这种模式可以实现灵活的消息主题订阅,适用于需要根据不同主题进行消息处理的场景。
- RPC:当客户端启动时,创建一个匿名的回调队列。请求被发送到rpc_queue队列中。这种模式适用于需要异步处理请求的远程过程调用场景。
图解
Work模式(Work Queues)
轮询,公平分发
轮询模式是一种消息分配策略,它按照消费者列表的顺序依次将消息发送给每个消费者。当一个消费者处理完一条消息后,下一个消费者会接收到下一条消息。这种模式确保了每个消费者都会收到相等数量的消息,从而均衡了消费者处理消息的数量。
公平分发模式则是在多个消费者之间按照一定的权重进行消息分配。权重可以根据实际需求进行设置,例如根据消费者的处理能力、可用资源等来分配不同的权重。在这种模式下,权重越高的消费者将会收到更多的消息,从而实现了按能力分配任务的目标。
这两种模式可以根据实际需求进行选择和使用。轮询模式适用于所有消费者处理能力相差不大的场景,而公平分发模式适用于消费者处理能力有明显差异的场景。
RabbitMQ发布订阅模式
RabbitMQ的发布订阅模式是一种消息路由方式,其中发送端(生产者)将消息发布到一个或多个主题(routing key)上,而接收端(消费者)则订阅这些主题并接收消息。
在这种模式下,生产者不需要知道有多少个消费者正在接收消息,它只需要将消息发布到指定的主题上。消费者则通过订阅这些主题来接收消息。这种模式可以实现消息的广播和订阅,适用于需要根据不同主题进行消息处理的场景。
在RabbitMQ中,发布订阅模式的核心是交换器(Exchanges)。交换器是连接生产者和消费者的桥梁,它负责接收来自生产者的消息,并根据路由规则将消息路由到正确的队列上。队列是存储消息的容器,消费者可以从队列中获取消息并进行处理。
在发布订阅模式下,有两种类型的交换器可用:直接交换(Direct exchange)和主题交换(Topic exchange)。直接交换根据路由键将消息路由到正确的队列,而主题交换则根据路由模式将消息路由到符合模式的队列。
RabbitMQ-Direct模式
RabbitMQ的Direct模式是一种消息路由方式,它基于直接交换(Direct exchange)实现。在这种模式下,生产者和消费者需要具有相同的交换机名称、交换机类型和相同的密匙(routingKey),消费者才能成功获取到消息。
在Direct模式中,生产者将消息发送到RabbitMQ的交换中心(Exchange),Exchange的一侧是生产者,另一侧则是一个或多个队列。Exchange根据设置的路由键决定一条消息的生命周期——发送给某些队列,或者直接丢弃掉。消费者则绑定交换器和队列,并指定与消息的路由键相同的绑定键(bindingKey),此时消息发送到交换器,消费者绑定的队列接收到消息,再由路由键决定哪个消费者消费消息。如果没有对应的路由键,则消息会丢失。
RabbitMQ的Direct模式相较于广播模式(fanout),更加精确地控制了消息的路由,因为fanout模式只要交换机名称即可接收到消息。而Direct模式在fanout的基础上,多加了一层密码限制(routingKey)。
RabbitMQ-Topic模式
RabbitMQ的Topic模式是一种消息路由方式,它基于主题交换(Topic exchange)实现。在这种模式下,生产者将消息发送到RabbitMQ的交换中心(Exchange),Exchange的一侧是生产者,另一侧则是一个或多个队列。与Direct模式不同,Topic模式中消费者和生产者之间使用通配符进行匹配,从而实现了基于多个标准的路由。
在Topic模式中,消费者可以监听自己的队列,并设置一个带通配符的RoutingKey。生产者将消息发送给RabbitMQ的交换器,由交换器根据RoutingKey来转发消息到指定的队列。与Direct模式不同的是,Topic模式中的RoutingKey可以使用通配符进行匹配,比如使用“*”来代替一个单词,或者使用“#”来代替零个或多个单词。这种灵活性使得Topic模式可以实现更复杂的消息路由需求。
安装RabbitMQ
下载 RabbitMQ 镜像
docker pull rabbitmq
创建,运行 RabbitMQ 容器
docker run -d -p 15672:15672 -p 5672:5672 \
-e RABBITMQ_DEFAULT_VHOST=my_vhost \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=admin \
--hostname Rabbit \
--name rabbitmq \
rabbitmq
docker run | 这是Docker命令行工具用来创建并启动容器的命令 |
---|---|
-d | 这告诉Docker在后台运行容器,即“分离模式”。 |
-p | 15672:15672 -p 5672:5672:这是将主机(host)的端口映射到容器内的端口。在这种情况下,主机的15672端口将映射到容器的15672端口(RabbitMQ的管理插件端口),主机的5672端口将映射到容器的5672端口(RabbitMQ的AMQP端口)。 |
-e | RABBITMQ_DEFAULT_VHOST=my_vhost:在容器内设置环境变量 |
RABBITMQ_DEFAULT_VHOST | 并将其值设置为my_vhost。默认的虚拟主机是/。 |
-e | RABBITMQ_DEFAULT_USER=admin:在容器内设置环境变量 |
RABBITMQ_DEFAULT_USER | 并将其值设置为admin。这个用户是RabbitMQ的管理员用户。 |
-e | RABBITMQ_DEFAULT_PASS=admin:在容器内设置环境变量 |
RABBITMQ_DEFAULT_PASS | 并将其值设置为admin。这是RabbitMQ的管理员用户的密码。 |
–hostname Rabbit | 为容器设置主机名(hostname)为Rabbit。 |
–name rabbitmq | 为容器设置一个名字为rabbitmq。 |
rabbitmq | 这是要运行的Docker镜像的名称,即RabbitMQ镜像。 |
进入容器
docker exec -it rabbitmq /bin/bash
在容器中配置web管理页面
rabbitmq-plugins enable rabbitmq_management
访问后台管理页面
http://ip:15672
管理页面如下:
web中的一些配置
创建交换机
在指定环境创建队列
在交换机中绑定queue和路由key
java中使用rabbitmq
这里我创建了一个maven项目,然后导入依赖:
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.10.0</version>
</dependency>
work模式
轮询
生产者
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Producer {
public static void main(String[] args) {
// 1: 创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2: 设置连接属性
connectionFactory.setHost("******");//你的服务器地址本机127.0.0.1
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("my_vhost");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
Connection connection = null;
Channel channel = null;
try {
// 3: 从连接工厂中获取连接
connection = connectionFactory.newConnection("生产者");
// 4: 从连接中获取通道channel
channel = connection.createChannel();
// 6: 准备发送消息的内容
//===============================end topic模式==================================
for (int i = 1; i <= 20; i++) {
//消息的内容
String msg = "hello:" + i;
// 7: 发送消息给中间件rabbitmq-server
// @params1: 交换机exchange
// @params2: 队列名称/routingkey
// @params3: 属性配置
// @params4: 发送消息的内容
channel.basicPublish("", "queue1", null, msg.getBytes());
}
System.out.println("消息发送成功!");
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("发送消息出现异常...");
} finally {
// 7: 释放连接关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
消费者
import com.rabbitmq.client.*;
import java.io.IOException;
public class Work1 {
public static void main(String[] args) {
// 1: 创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2: 设置连接属性
connectionFactory.setHost("******");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("my_vhost");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
Connection connection = null;
Channel channel = null;
try {
// 3: 从连接工厂中获取连接
connection = connectionFactory.newConnection("消费者-Work1");
// 4: 从连接中获取通道channel
channel = connection.createChannel();
// 5: 申明队列queue存储消息
/*
* 如果队列不存在,则会创建
* Rabbitmq不允许创建两个相同的队列名称,否则会报错。
*
* @params1: queue 队列的名称
* @params2: durable 队列是否持久化
* @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
* @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
* @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
* */
// 这里如果queue已经被创建过一次了,可以不需要定义
// channel.queueDeclare("queue1", false, false, false, null);
// 同一时刻,服务器只会推送一条消息给消费者
// 6: 定义接受消息的回调
Channel finalChannel = channel;
finalChannel.basicQos(1);
finalChannel.basicConsume("queue1", true, new DeliverCallback() {
@Override
public void handle(String s, Delivery delivery) throws IOException {
try{
System.out.println("Work1-收到消息是:" + new String(delivery.getBody(), "UTF-8"));
Thread.sleep(2000);
}catch(Exception ex){
ex.printStackTrace();
}
}
}, new CancelCallback() {
@Override
public void handle(String s) throws IOException {
}
});
System.out.println("Work1-开始接受消息");
System.in.read();
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("发送消息出现异常...");
} finally {
// 7: 释放连接关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
公平分发
生产者
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Producer {
public static void main(String[] args) {
// 1: 创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2: 设置连接属性
connectionFactory.setHost("********");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("my_vhost");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
Connection connection = null;
Channel channel = null;
try {
// 3: 从连接工厂中获取连接
connection = connectionFactory.newConnection("生产者");
// 4: 从连接中获取通道channel
channel = connection.createChannel();
// 6: 准备发送消息的内容
//===============================end topic模式==================================
for (int i = 1; i <= 20; i++) {
//消息的内容
String msg = "hello:" + i;
// 7: 发送消息给中间件rabbitmq-server
// @params1: 交换机exchange
// @params2: 队列名称/routingkey
// @params3: 属性配置
// @params4: 发送消息的内容
channel.basicPublish("", "queue1", null, msg.getBytes());
}
System.out.println("消息发送成功!");
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("发送消息出现异常...");
} finally {
// 7: 释放连接关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
消费者
import com.rabbitmq.client.*;
import java.io.IOException;
public class Work1 {
public static void main(String[] args) {
// 1: 创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2: 设置连接属性
connectionFactory.setHost("********");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("my_vhost");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
Connection connection = null;
Channel channel = null;
try {
// 3: 从连接工厂中获取连接
connection = connectionFactory.newConnection("消费者-Work1");
// 4: 从连接中获取通道channel
channel = connection.createChannel();
// 5: 申明队列queue存储消息
/*
* 如果队列不存在,则会创建
* Rabbitmq不允许创建两个相同的队列名称,否则会报错。
*
* @params1: queue 队列的名称
* @params2: durable 队列是否持久化
* @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
* @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
* @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
* */
// 这里如果queue已经被创建过一次了,可以不需要定义
// channel.queueDeclare("queue1", false, false, false, null);
// 同一时刻,服务器只会推送一条消息给消费者
// 6: 定义接受消息的回调
Channel finalChannel = channel;
finalChannel.basicQos(1);
finalChannel.basicConsume("queue1", false, new DeliverCallback() {
@Override
public void handle(String s, Delivery delivery) throws IOException {
try{
System.out.println("Work1-收到消息是:" + new String(delivery.getBody(), "UTF-8"));
Thread.sleep(2000);
finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}catch(Exception ex){
ex.printStackTrace();
}
}
}, new CancelCallback() {
@Override
public void handle(String s) throws IOException {
}
});
System.out.println("Work1-开始接受消息");
System.in.read();
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("发送消息出现异常...");
} finally {
// 7: 释放连接关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
fanout模式
生产者:
package org.example.rabbitmq.routing;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @author taritari
* @date 2023-11-22 15:01
* @description
*/
public class Consumer {
private static Runnable runnable = new Runnable() {
@Override
public void run() {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("***");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("my_vhost");
Connection connection = null;
Channel channel = null;
final String queueName = Thread.currentThread().getName();
try {
connection = connectionFactory.newConnection("生产者");
channel = connection.createChannel();
channel.basicConsume(queueName, true, new DeliverCallback() {
@Override
public void handle(String s, Delivery delivery) throws IOException {
System.out.println(delivery.getEnvelope().getDeliveryTag());
System.out.println(queueName+"收到消息是:"+new String(delivery.getBody(),"UTF-8"));
}
}, new CancelCallback() {
@Override
public void handle(String s) throws IOException {
}
});
System.out.println(queueName+"开始接收消息");
System.in.read();
}catch (Exception e){
System.out.println("接收失败了");
e.printStackTrace();
}finally {
if (channel != null && channel.isOpen()){
try {
connection.close();
}catch (Exception exception){
exception.printStackTrace();
}
}
if (connection != null && connection.isOpen()){
try {
connection.close();
}catch (Exception exception){
exception.printStackTrace();
}
}
}
}
};
public static void main(String[] args) {
new Thread(runnable,"queue1").start();
new Thread(runnable,"queue3").start();
new Thread(runnable,"queue2").start();
}
}
消费者:
package org.example.rabbitmq.routing;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @author taritari
* @date 2023-11-22 15:01
* @description
*/
public class Producer {
public static void main(String[] args) {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("*****");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("my_vhost");
Connection connection = null;
Channel channel = null;
try {
connection = connectionFactory.newConnection("生产者");
channel = connection.createChannel();
String message = "HelloWorld!";
String exchangeName = "fanout-exchange";
String routeKey = "";
channel.basicPublish(exchangeName,routeKey,null,message.getBytes());
System.out.println("发送成功");
}catch (Exception e){
e.printStackTrace();
}finally {
if (channel != null && channel.isOpen()){
try {
connection.close();
}catch (Exception exception){
exception.printStackTrace();
}
}
if (connection != null && connection.isOpen()){
try {
connection.close();
}catch (Exception exception){
exception.printStackTrace();
}
}
}
}
}
direct模式
生产者:
package org.example.rabbitmq.direct;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @author taritari
* @date 2023-11-22 15:01
* @description
*/
public class Producer {
public static void main(String[] args) {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("****");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("my_vhost");
Connection connection = null;
Channel channel = null;
try {
connection = connectionFactory.newConnection("生产者");
channel = connection.createChannel();
String message = "HelloWorld!";
String exchangeName = "direct-exchange";
String routeKey = "email";
channel.basicPublish(exchangeName,routeKey,null,message.getBytes());
System.out.println("发送成功");
}catch (Exception e){
e.printStackTrace();
}finally {
if (channel != null && channel.isOpen()){
try {
connection.close();
}catch (Exception exception){
exception.printStackTrace();
}
}
if (connection != null && connection.isOpen()){
try {
connection.close();
}catch (Exception exception){
exception.printStackTrace();
}
}
}
}
}
消费者:
package org.example.rabbitmq.direct;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @author taritari
* @date 2023-11-22 15:01
* @description
*/
public class Consumer {
private static Runnable runnable = new Runnable() {
@Override
public void run() {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("*****");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("my_vhost");
Connection connection = null;
Channel channel = null;
final String queueName = Thread.currentThread().getName();
try {
connection = connectionFactory.newConnection("生产者");
channel = connection.createChannel();
channel.basicConsume(queueName, true, new DeliverCallback() {
@Override
public void handle(String s, Delivery delivery) throws IOException {
System.out.println(delivery.getEnvelope().getDeliveryTag());
System.out.println(queueName+"收到消息是:"+new String(delivery.getBody(),"UTF-8"));
}
}, new CancelCallback() {
@Override
public void handle(String s) throws IOException {
}
});
System.out.println(queueName+"开始接收消息");
System.in.read();
}catch (Exception e){
System.out.println("接收失败了");
e.printStackTrace();
}finally {
if (channel != null && channel.isOpen()){
try {
connection.close();
}catch (Exception exception){
exception.printStackTrace();
}
}
if (connection != null && connection.isOpen()){
try {
connection.close();
}catch (Exception exception){
exception.printStackTrace();
}
}
}
}
};
public static void main(String[] args) {
new Thread(runnable,"queue1").start();
new Thread(runnable,"queue3").start();
new Thread(runnable,"queue2").start();
}
}
topic模式
生产者:
package org.example.rabbitmq.topics;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @author taritari
* @date 2023-11-22 15:01
* @description
*/
public class Producer {
public static void main(String[] args) {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("****");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("my_vhost");
Connection connection = null;
Channel channel = null;
try {
connection = connectionFactory.newConnection("生产者");
channel = connection.createChannel();
String message = "HelloWorld!";
String exchangeName = "topic-exchange";
String routeKey = "com.order.test.xxx";
channel.basicPublish(exchangeName,routeKey,null,message.getBytes());
System.out.println("发送成功");
}catch (Exception e){
e.printStackTrace();
}finally {
if (channel != null && channel.isOpen()){
try {
connection.close();
}catch (Exception exception){
exception.printStackTrace();
}
}
if (connection != null && connection.isOpen()){
try {
connection.close();
}catch (Exception exception){
exception.printStackTrace();
}
}
}
}
}
消费者:
package org.example.rabbitmq.topics;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @author taritari
* @date 2023-11-22 15:01
* @description
*/
public class Consumer {
private static Runnable runnable = new Runnable() {
@Override
public void run() {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("****");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("my_vhost");
Connection connection = null;
Channel channel = null;
final String queueName = Thread.currentThread().getName();
try {
connection = connectionFactory.newConnection("生产者");
channel = connection.createChannel();
channel.basicConsume(queueName, true, new DeliverCallback() {
@Override
public void handle(String s, Delivery delivery) throws IOException {
System.out.println(delivery.getEnvelope().getDeliveryTag());
System.out.println(queueName+"收到消息是:"+new String(delivery.getBody(),"UTF-8"));
}
}, new CancelCallback() {
@Override
public void handle(String s) throws IOException {
}
});
System.out.println(queueName+"开始接收消息");
System.in.read();
}catch (Exception e){
System.out.println("接收失败了");
e.printStackTrace();
}finally {
if (channel != null && channel.isOpen()){
try {
connection.close();
}catch (Exception exception){
exception.printStackTrace();
}
}
if (connection != null && connection.isOpen()){
try {
connection.close();
}catch (Exception exception){
exception.printStackTrace();
}
}
}
}
};
public static void main(String[] args) {
new Thread(runnable,"queue1").start();
new Thread(runnable,"queue3").start();
new Thread(runnable,"queue2").start();
}
}
这是部分代码的详解
连接RabbitMQ服务器:在Java代码中,你需要创建一个连接工厂并使用它来建立与RabbitMQ服务器的连接。连接工厂的实现类通常命名为ConnectionFactory。
需要设置RabbitMQ服务器的地址、端口号、虚拟主机等参数。
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost"); // 设置RabbitMQ服务器地址
factory.setPort(5672); // 设置RabbitMQ服务器端口号
factory.setVirtualHost("/"); // 设置虚拟主机(可选)
创建连接:使用连接工厂创建与RabbitMQ服务器的连接。
Connection connection = factory.newConnection();
创建通道:在连接上创建一个通道,用于发送和接收消息。
Channel channel = connection.createChannel();
声明队列:在发送和接收消息之前,你需要先声明一个队列。这可以通过调用channel.queueDeclare()方法来完成。
String queueName = "my_queue"; // 队列名称
channel.queueDeclare(queueName, false, false, false, null); // 声明队列
发送消息:通过通道,你可以使用AMQP协议发送消息到队列中。这可以通过调用channel.basicPublish()或channel.basicPublish()方法来完成。
String message = "Hello, RabbitMQ!"; // 要发送的消息内容
channel.basicPublish("", queueName, null, message.getBytes("UTF-8")); // 发送消息到队列中
springBoot整合rabbitmq
引入依赖:在pom.xml文件中添加Spring Boot RabbitMQ的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置RabbitMQ:在application.properties或application.yml文件中添加RabbitMQ的配置参数,包括host、port、username、password等。
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
创建消息生产者:通过Spring Boot提供的RabbitTemplate,可以方便地发送消息到队列中。
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage(String exchange, String routingKey, Object message) {
rabbitTemplate.convertAndSend(exchange, routingKey, message);
}
创建消息消费者:通过定义一个消息监听器,在方法上使用@RabbitListener注解来接收队列中的消息。
@RabbitListener(queues = "my_queue")
public void processMessage(String message) {
System.out.println("Received message: " + message);
}
启动应用程序:启动Spring Boot应用程序,并确保RabbitMQ服务器的地址、端口号、用户名和密码等参数正确配置。然后,应用程序将自动与RabbitMQ进行连接,并开始接收和发送消息。
下面是完整代码
连接rabbitmq
server:
port: 8080
spring:
rabbitmq:
username: admin
password: admin
host: ******
port: 5672
virtual-host: my_vhost
生产者
package com.example.mqproduce.services;
import jakarta.annotation.Resource;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.UUID;
/**
* @author taritari
* @date 2023-11-29 16:11
* @description
*/
@Service
public class OrderService {
@Resource
private RabbitTemplate rabbitTemplate;
public void makeOrderFanout(String userId,String productId,int num){
String exchangeName = "fanout_order_exchange";
String routingKey = "";
String orderId = UUID.randomUUID().toString();
System.out.println("订单生成成功:"+orderId);
rabbitTemplate.convertAndSend(exchangeName,routingKey,orderId);
}
public void makeOrderDirect(String userId,String productId,int num){
String exchangeName = "direct_order_exchange";
String routingKey = "";
String orderId = UUID.randomUUID().toString();
System.out.println("订单生成成功:"+orderId);
rabbitTemplate.convertAndSend(exchangeName,"email",orderId);
rabbitTemplate.convertAndSend(exchangeName,"duanxin",orderId);
}
public void makeOrderTopic(String userId,String productId,int num){
String exchangeName = "topic_order_exchange";
String routingKey = "com.email.duanxin";
String orderId = UUID.randomUUID().toString();
System.out.println("订单生成成功:"+orderId);
rabbitTemplate.convertAndSend(exchangeName,routingKey,orderId);
}
public void makeDirectTtl(String userId,String productId,int num){
String exchangeName = "ttl_direct_exchange";
String routingKey = "ttl";
String orderId = UUID.randomUUID().toString();
System.out.println("订单生成成功:"+orderId);
rabbitTemplate.convertAndSend(exchangeName,routingKey,orderId);
}
public void makeDirectTtlMessage(String userId,String productId,int num){
String exchangeName = "ttl_direct_exchange";
String routingKey = "ttlMessage";
String orderId = UUID.randomUUID().toString();
System.out.println("订单生成成功:"+orderId);
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setExpiration("5000");
message.getMessageProperties().setContentEncoding("UTF-8");
return message;
}
};
rabbitTemplate.convertAndSend(exchangeName,routingKey,orderId,messagePostProcessor);
}
}
测试类
package com.example.mqproduce;
import com.example.mqproduce.services.OrderService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class MqProduceApplicationTests {
@Autowired
private OrderService orderService;
@Test
void fanoutTest() {
orderService.makeOrderFanout("1","1",12);
}
@Test
void directTest() {
orderService.makeOrderDirect("1","1",12);
}
@Test
void topicTest(){
orderService.makeOrderTopic("1","1",12);
}
@Test
void ttlTest(){
for (int i=0;i<=10;i++){
orderService.makeDirectTtl("1","1",12);
}
}
@Test
void ttlMessageTest(){
orderService.makeDirectTtlMessage("1","1",12);
}
}
消费者
package com.taritari.mq.mqconsumber.services.fanout;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
/**
* @author taritari
* @date 2023-11-29 16:37
* @description
*/
@Service
@RabbitListener(queues = {"email.fanout.queue"})
public class FanoutEmailConsumer {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("email.fanout.queue--接收到了消息-->"+message);
}
}
代码中完成绑定关系
package com.taritari.mq.mqconsumber.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @author taritari
* @date 2023-11-29 22:09
* @description
*/
@Configuration
public class TtlRabbitMqConfiguration {
//声明交换机注册direct模式的交换机
@Bean
public DirectExchange ttlDirectExchange(){
return new DirectExchange("ttl_direct_exchange",true,false);
}
//声明队列 sms.direct.queue email.direct.queue duanxin.direct.queue
@Bean
public Queue ttlQueue(){
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl",5000);
args.put("x-max-length",5);
args.put("x-dead-letter-exchange","dead_direct_exchange");
args.put("x-dead-letter-routing-key","dead");
return new Queue("ttl.direct.queue",true,false,false,args);
}
@Bean
public Queue ttlQueueMessage(){
return new Queue("ttl.message.direct.queue",true);
}
//完成绑定关系
@Bean
public Binding ttlBingDing(){
return BindingBuilder.bind(ttlQueue()).to(ttlDirectExchange()).with("ttl");
}
@Bean
public Binding ttlMessageBingDing(){
return BindingBuilder.bind(ttlQueueMessage()).to(ttlDirectExchange()).with("ttlMessage");
}
}
- 声明交换机:通过ttlDirectExchange方法声明了一个DirectExchange类型的交换机,并命名为"ttl_direct_exchange"。这种类型的交换机支持直接交换模式,它是最简单的交换类型,也是最常用的交换类型。
- 声明队列:通过ttlQueue方法声明了一个队列,命名为"ttl.direct.queue"。这个队列有一些特殊的参数设置,比如设置了消息的过期时间(x-message-ttl)、最大长度(x-max-length)、死信交换机(x-dead-letter-exchange)和死信路由键(x-dead-letter-routing-key)。另外,通过ttlQueueMessage方法声明了另一个队列,命名为"ttl.message.direct.queue"。
- 绑定关系:通过ttlBingDing和ttlMessageBingDing方法,将之前声明的队列与交换机进行绑定。第一个方法绑定的是"ttl"队列和"ttl_direct_exchange"交换机,第二个方法绑定的是"ttlMessage"队列和"ttl_direct_exchange"交换机。