RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。
安装RabbitMQ(Mac环境)
首先,想要使用RabbitMQ,需要先将RabbitMQ下载到本地,具体方式如下:
# 先检查brew的更新
brew update
# 安装RabbitMQ
brew install rabbitmq
接着需要配置一下RabbitMQ的环境变量:
export PATH=$PATH:/usr/local/opt/rabbitmq/sbin
接着我们就可以启动RabbitMQ了:
# 前台启动
rabbitmq-server
# 后台启动
brew service start rabbitmq
# 后台退出
rabbitmqctl stop
安装RabbitMQ(Ubuntu18.04环境)
步骤如下:
# 先安装RabbitMQ的语言环境,即需要安装Erlang
sudo apt-get install erlang
# 接着安装RabbitMQ
sudo apt-get install rabbitmq-server
# 最后安装RabbitMQ开机自启插件
sudo rabbitmq-plugins enable rabbitmq_management
# 安装完之后系统会默认启动RabbitMQ
常规RabbitMQ操作:
# 启动
sudo rabbitmq-server start
# 重启
sudo rabbitmq-server restart
# 后台退出
sudo rabbitmq-server stop
RabbitMQ的使用
RabbitMQ模式介绍
- ①简单模式——Hello World
- ②工作队列模式——Work Queue
- ③订阅(广播)模式——Publish / subscribe
- ④路由模式——Routing
- ⑤通配符模式——Topic
交换机类型介绍(分别对应后三种模式)
- ①fanout——广播类型,不需要使用RoutingKey
- ②direct——路由类型,需要使用RoutingKey
- ③topics——通配符类型,需要使用RoutingKey
在基础使用RabbitMQ时,需要现在项目中添加如下依赖:
com.rabbitmq
amqp-client
5.7.3
- ①②关于“简单模式”和“工作队列模式”,这两个模式都是不包含交换机的,只需要有生产者(Producer)和消费者(Consumer)就可以了,其中工作队列采用轮询的机制,相关示例如下:
//*****
//生产者Producer
//*****
public class RabbitProducer {
public static void main(String[] args) throws IOException, TimeoutException {
//创建RabbitMQ的连接
ConnectionFactory connectionFactory = new ConnectionFactory();
//设定RabbitMQ的服务主机地址,默认为localhost
connectionFactory.setHost("localhost");
//设定RabbitMQ的服务端口,默认为5672
connectionFactory.setPort(5672);
//设定RabbitMQ的虚拟主机,默认为"/"
connectionFactory.setVirtualHost("/");
//设定RabbitMQ的用户名,默认为guest
connectionFactory.setUsername("guest");
//设定RabbitMQ的密码,默认为guest
connectionFactory.setPassword("guest");
//创建连接,会抛出两个异常
Connection connection = connectionFactory.newConnection();
//创建频道,对要发送的信息进行进一步配置封装
Channel channel = connection.createChannel();
//声明队列
/**
* 参数1:队列的名称
* 参数2:消息是否持久化,建议为true
* 参数3:当前消息是否为当前连接对象独有,建议为false
* 参数4:消息使用之后是否自动删除,建议为false
* 参数5:额外参数,没有置为null
*/
channel.queueDeclare("demo_queue",true,false,false,null);
//创建消息
String msg = "Hello RabbitMQ!";
//消息的发送
/**
* 参数1:指定消息发送的交换机,默认为"",代表DefaultExchange
* 参数2:当前消息的路由地址RoutingKey,在简单消息中可以写成跟队列名称一致
* 参数3:附加参数,若在执行持久化操作时需要写上“MessageProperties.PERSISTENT_TEXT_PLAIN”
* 参数4:消息对象,记得类型要转为字节数组
*/
channel.basicPublish("","demo_queue",MessageProperties.PERSISTENT_TEXT_PLAIN,msg.getBytes());
//关闭资源
channel.close();
connection.close();
}
}
//*****
//消费者Consumer
//*****
public class RabbitConsumer {
public static void main(String[] args) throws IOException, TimeoutException {
//创建RabbitMQ的连接
ConnectionFactory connectionFactory = new ConnectionFactory();
//设定RabbitMQ的服务主机地址,默认为localhost
connectionFactory.setHost("localhost");
//设定RabbitMQ的服务端口,默认为5672
connectionFactory.setPort(5672);
//设定RabbitMQ的虚拟主机,默认为"/"
connectionFactory.setVirtualHost("/");
//设定RabbitMQ的用户名,默认为guest
connectionFactory.setUsername("guest");
//设定RabbitMQ的密码,默认为guest
connectionFactory.setPassword("guest");
//创建连接
Connection connection = connectionFactory.newConnection();
//创建频道,对要发送的信息进行进一步配置封装
Channel channel = connection.createChannel();
//创建消费者,对消息进行接收和处理
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
/**
* 消息处理
* @param consumerTag :消费者标签,用来区分多个消费者
* @param envelope :消息相关信息的封装
* @param properties :额外参数
* @param body :消息本体
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag,Envelope envelope,AMQP.BasicProperties properties,byte[] body) throws IOException {
String msg = new String(body, "UTF-8");
System.out.println("收到的信息内容为:" + msg);
}
};
//执行消息的监听
/**
* 参数1:设置监听的队列名称
* 参数2:设置信息应答模式,为true则代表该语句执行完之后消息自动进行确认,那么此时消息就会自动删除,也可以设置为false随后再执行手动删除
* 参数3:消费者对象,处理信息
*/
channel.basicConsume("demo_queue",false,defaultConsumer);
//手动确认消息的方式如下,其中第二个参数是值是否批量操作
channel.basicAck(envelope.getDeliveryTag(), false);
//可以执行关闭资源,但是接收方一般不建议执行关闭
//channel.close();
//connection.close();
}
}
- ③在使用“订阅模式”时,就需要加入交换机(Exchange)了,且交换机的设定一般存在于生产者中
//*****
//生产者Producer
//*****
public class RabbitProducer {
public static void main(String[] args) throws IOException, TimeoutException {
//创建RabbitMQ的连接
ConnectionFactory connectionFactory = new ConnectionFactory();
//设定RabbitMQ的服务主机地址,默认为localhost
connectionFactory.setHost("localhost");
//设定RabbitMQ的服务端口,默认为5672
connectionFactory.setPort(5672);
//设定RabbitMQ的虚拟主机,默认为"/"
connectionFactory.setVirtualHost("/");
//设定RabbitMQ的用户名,默认为guest
connectionFactory.setUsername("guest");
//设定RabbitMQ的密码,默认为guest
connectionFactory.setPassword("guest");
//创建连接,会抛出两个异常
Connection connection = connectionFactory.newConnection();
//创建频道,对要发送的信息进行进一步配置封装
Channel channel = connection.createChannel();
//声明交换机,第一个参数是指交换机名称,第二个参数是指交换机的类型
channel.exchangeDeclare("my_exchange", BuiltinExchangeType.FANOUT);
//声明队列
channel.queueDeclare("demo_queue_1",true,false,false,null);
channel.queueDeclare("demo_queue_2",true,false,false,null);
//执行交换机和队列的绑定,第三个参数是RoutingKey,广播模式不需要填写
channel.queueBind("demo_queue_1","my_exchange","");
channel.queueBind("demo_queue_2","my_exchange","");
//创建消息
String msg = "Hello RabbitMQ!";
//消息的发送
channel.basicPublish("my_exchange","",MessageProperties.PERSISTENT_TEXT_PLAIN,msg.getBytes());
//关闭资源
channel.close();
connection.close();
}
}
- ④在使用“路由模式”时,此时需要使用上RoutingKey这个参数,用于在原有广播模式的基础上再根据RoutingKey进行定向绑定队列
//*****
//生产者Producer
//*****
public class RabbitProducer {
public static void main(String[] args) throws IOException, TimeoutException {
//创建RabbitMQ的连接
ConnectionFactory connectionFactory = new ConnectionFactory();
//设定RabbitMQ的服务主机地址,默认为localhost
connectionFactory.setHost("localhost");
//设定RabbitMQ的服务端口,默认为5672
connectionFactory.setPort(5672);
//设定RabbitMQ的虚拟主机,默认为"/"
connectionFactory.setVirtualHost("/");
//设定RabbitMQ的用户名,默认为guest
connectionFactory.setUsername("guest");
//设定RabbitMQ的密码,默认为guest
connectionFactory.setPassword("guest");
//创建连接,会抛出两个异常
Connection connection = connectionFactory.newConnection();
//创建频道,对要发送的信息进行进一步配置封装
Channel channel = connection.createChannel();
//声明交换机,此时交换机的类型为direct
channel.exchangeDeclare("my_exchange", BuiltinExchangeType.DIRECT);
//声明队列
channel.queueDeclare("demo_queue_1",true,false,false,null);
channel.queueDeclare("demo_queue_2",true,false,false,null);
//执行交换机和队列的绑定,此时需要携带上特定的RoutingKey
channel.queueBind("demo_queue_1","my_exchange","user.save");
channel.queueBind("demo_queue_2","my_exchange","user.show");
//创建消息
String msg1 = "Hello RabbitMQ!_for_save";
String msg2 = "Hello RabbitMQ!_for_show";
//消息的发送,此时消息1只会发送给队列1,而消息2只会发送给队列2
channel.basicPublish("my_exchange","user.save",MessageProperties.PERSISTENT_TEXT_PLAIN,msg1.getBytes());
channel.basicPublish("my_exchange","user.show",MessageProperties.PERSISTENT_TEXT_PLAIN,msg2.getBytes());
//关闭资源
channel.close();
connection.close();
}
}
- ⑤关于“通配符模式”时,它相当于路由模式的细分优化版本,通过特定的通配符对形式相近的RoutingKey达到统一操作的目的
//*****
//生产者Producer
//*****
public class RabbitProducer {
public static void main(String[] args) throws IOException, TimeoutException {
//创建RabbitMQ的连接
ConnectionFactory connectionFactory = new ConnectionFactory();
//设定RabbitMQ的服务主机地址,默认为localhost
connectionFactory.setHost("localhost");
//设定RabbitMQ的服务端口,默认为5672
connectionFactory.setPort(5672);
//设定RabbitMQ的虚拟主机,默认为"/"
connectionFactory.setVirtualHost("/");
//设定RabbitMQ的用户名,默认为guest
connectionFactory.setUsername("guest");
//设定RabbitMQ的密码,默认为guest
connectionFactory.setPassword("guest");
//创建连接,会抛出两个异常
Connection connection = connectionFactory.newConnection();
//创建频道,对要发送的信息进行进一步配置封装
Channel channel = connection.createChannel();
//声明交换机,此时交换机的类型为topic
channel.exchangeDeclare("my_exchange", BuiltinExchangeType.TOPIC);
//声明队列
channel.queueDeclare("demo_queue_1",true,false,false,null);
channel.queueDeclare("demo_queue_2",true,false,false,null);
channel.queueDeclare("demo_queue_3",true,false,false,null);
//执行交换机和队列的绑定,此时需要携带上特定的RoutingKey
channel.queueBind("demo_queue_1","my_exchange","user.save");
channel.queueBind("demo_queue_2","my_exchange","user.show");
channel.queueBind("demo_queue_3","my_exchange","user.show.age");
//创建消息
String msg1 = "Hello RabbitMQ!_one";
String msg2 = "Hello RabbitMQ!_two";
String msg3 = "Hello RabbitMQ!_three";
String msg4 = "Hello RabbitMQ!_four";
String msg5 = "Hello RabbitMQ!_five";
//消息的发送,其中“#”代表后续任意层级(可以是0层),而“*”只代表后续一个层级
channel.basicPublish("my_exchange","user.save",MessageProperties.PERSISTENT_TEXT_PLAIN,msg1.getBytes());
channel.basicPublish("my_exchange","user.show",MessageProperties.PERSISTENT_TEXT_PLAIN,msg2.getBytes());
channel.basicPublish("my_exchange","user.#",MessageProperties.PERSISTENT_TEXT_PLAIN,msg3.getBytes());
channel.basicPublish("my_exchange","user.*",MessageProperties.PERSISTENT_TEXT_PLAIN,msg4.getBytes());
channel.basicPublish("my_exchange","user.*.*",MessageProperties.PERSISTENT_TEXT_PLAIN,msg5.getBytes());
/**
* 上述语句结果为:
* 消息1 -> demo_queue_1
* 消息2 -> demo_queue_2
* 消息3 -> demo_queue_1,demo_queue_2,demo_queue_3
* 消息4 -> demo_queue_1,demo_queue_2
* 消息5 -> demo_queue_3
*/
//关闭资源
channel.close();
connection.close();
}
}
RabbitMQ集成SpringBoot
1.在项目中导入相关的依赖
org.springframework.boot
spring-boot-starter-amqp
2.1.7.RELEASE
2.在SpringBoot项目中基础使用RabbitMQ一般需要五个部分
- ①在项目的配置文件
application.yml
中添加
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /my_host
- ②接着项目的启动类不需要修改,默认即可
@SpringBootApplication
public class RabbitSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(RabbitSpringBootApplication.class,args);
}
}
- ③随后我们创建一个配置类,这次使用Topics模式【记得“创建队列”、“创建交换机”、“执行绑定”这三个步骤方法都需要加上“@Bean”注解】
@Configuration
public class RabbitConfiguration {
public static final String QUEUE_A = "demo_queue_a";
public static final String QUEUE_B = "demo_queue_b";
public static final String QUEUE_C = "demo_queue_c";
public static final String EXCHANGE_A = "demo_exchange_a";
public static final String EXCHANGE_B = "demo_exchange_b";
public static final String EXCHANGE_C = "demo_exchange_c";
public static final String ROUTINGKEY_A = "demo_routingkey_a";
public static final String ROUTINGKEY_B = "demo_routingkey_b";
public static final String ROUTINGKEY_C = "demo_routingkey_c";
//创建队列对象
@Bean
public Queue demoQueue() {
//需要注意的是,此处的Queue简写方式的完整形式为"Queue(QUEUE_A,true,false,false,null)"
//第二个参数表示是否持久化
//第三个参数表示是当前消息是否为当前队列独有
//第四个参数表示是否自动删除
return new Queue(QUEUE_A,true);
}
//创建交换机
@Bean
public Exchange demoExchange() {
//需要注意的是,这里的完整形式为"XxxExchange(EXCHANGE_A,true,false)"
//第二个参数表示是否持久化
//第三个参数表示是否自动删除
return new TopicExchange(EXCHANGE_A);
//return new FanoutExchange(EXCHANGE_A);
//return new DirectExchange(EXCHANGE_A);
}
//进行队列和交换机的绑定
@Bean
public Binding itemBinding() {
return BindingBuilder.bind(demoQueue()).to(demoExchange()).with(ROUTINGKEY_A).noargs();
}
}
- ④接下来我们开始创建生产者(Producer)
@Component
public class RabbitProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMsg() {
String msg = "Hello RabbitMQ!";
//发送消息
rabbitTemplate.convertAndSend(RabbitConfiguration.EXCHANGE_A,RabbitConfiguration.ROUTINGKEY_A,msg);
}
}
- ⑤最后是创建消费者(Consumer)
@Component
@RabbitListener(queues = RabbitConfiguration.QUEUE_A)
public class RabbitConsumer {
@RabbitHandler
public void getMsg(String msg) {
System.out.println("收到的信息为:" + msg);
}
}
整合微服务项目
- ①导入相关依赖
org.springframework.boot
spring-boot-starter-amqp
2.1.7.RELEASE
- ②编写
application.yml
配置文件
spring:
rabbitmq:
host: 192.168.xx.xx # RabbitMQ服务端IP地址
port: 5672 # RabbitMQ服务端端口号
username: your_username # RabbitMQ的账号
password: your_password # RabbitMQ的密码
virtual-host: /your_vh_addr # RabbitMQ的虚拟主机名称
publisher-confirms: true # 启用“发布确定”
publisher-returns: true # 启用“返回确定”
template: # 模板信息,接收方可以不写
exchange: your_exchange_temp # 配置默认的交换机,格式一般为“xx.xx.xx”
接下来在需要发送消息的模块方法中先注入AmqpTemplate
对象,接着定义一个发送消息的方法,需要的时候可以直接调用sendMsg
这个方法发送消息。
@Autowired
private AmqpTemplate amqpTemplate;
public void sendMsg(String type,Long id) {
try {
// 发送消息,其中参数1代表RoutingKey的值,参数2代表发送的信息内容
// 该方式发送消息默认消息就是持久化的,即“deliveryMode=2”
this.amqpTemplate.convertAndSend("xx.xx."+type, id);
} catch (AmqpException e) {
e.printStackTrace();
}
}
此时作为接收方则可以创建一个监听类来处理业务
@Component
public class MsgListener {
@Autowired
private DemoService demoService;
//此时在“topic”类型下,RabbitMQ会根据发送方提供的交换机名称和key值进行匹配,符合要求的就会发送到对应的队列中
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "自定义队列名称",durable = "true"),
exchange = @Exchange(value = "交换机名称",ignoreDeclarationExceptions = "true",type = "topic"),
key = {"自定义key1","自定义key2"}
))
public void save(Long id) throws IOException{
if (id ==null) {
return;
}
this.demoService.save(id);
}
}