MQ基本概念
Message Queue(消息队列)。是指在消息的传输过程中保存消息的容器,,多用于分布式系统之间进行通信。
分布式系统两种通信方式
远程RPC调用
引入消息中间件
小结
- MQ,消息队列,存储消息的中间件
- 分布式系统同行两种方式:直接远程调用和借助第三方间接通信。
- 发布方成为生产者,接收方成为消费者。
MQ的优劣
优势
应用解耦:提高系统容错性和可维护性
远程调用带来的下面的耦合
可以看出这个远程调用带来的问题就是,系统的耦合性高,容错性低。可维护性也低
我们来看看使用中间件的解耦
使用MQ是的应用间解耦,提升容错性和可维护性
异步提速:提升用户体验与吞吐量
我们来看看这个同步带来的问题
下单耗时是:20+300+300+300=920ms
用户点击下单按钮以后,需要等待920ms,这样系统的响应时间太长。
我们再来看这个MQ的异步提速
这里的下单响应时间是:20+5=25ms
不精提高了用户体验和系统的吞吐量
削峰填谷:使用MQ就提高了,系统的稳定性
使用了这个MQ之后呢,就限制了消费的速度是1000,这样高峰期产生的数据势必会积压在MQ中,因此削去了高峰,因为消息的积压,为高峰期过后的一段时间内,还是维持在1000,直到消除积压,就叫填谷
劣势
-
系统可用性降低
系统引入的外部依赖多系统稳定性就越差,MQ宕机,就会对业务造成影响,因此保证MQ的高可用是问题的关键 -
系统复杂度提高
MQ的加入大大提高了系统的复杂度,以前系统间是同步的远程调用,现在是通过MQ进行异步调用,问题的关键就在于:- 如何保证消息没有被重复消费?
- 如何处理消息的丢失情况?
- 如何保证消息传递的顺序性?
-
一致性问题
A处理了业务,通过MQ给BCD业三个系统都发送消息数据,那么如何保证消息数据处理的一致性?
常见MQ
RabbitMQ的简介与入门
AMQP协议
AMQP:Advanced Message Queuing(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。
JMS
JMS即Java消息服务(JavaMessage Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
RabbitMQ简介
由ErLang语言开发
基础架构图
RabbitMQ的入门
安装(这里展示在线安装,屁事少)
# 第一步
rpm --import https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc
# 第二步
#=========================================
# centos7 用这个
cat <<EOF > /etc/yum.repos.d/rabbitmq.repo
[bintray-rabbitmq-server]
name=bintray-rabbitmq-rpm
baseurl=https://dl.bintray.com/rabbitmq/rpm/rabbitmq-server/v3.8.x/el/7/
gpgcheck=0
repo_gpgcheck=0
enabled=1
EOF
# centos6 用这个
cat <<EOF > /etc/yum.repos.d/rabbitmq.repo
[bintray-rabbitmq-server]
name=bintray-rabbitmq-rpm
baseurl=https://dl.bintray.com/rabbitmq/rpm/rabbitmq-server/v3.8.x/el/6/
gpgcheck=0
repo_gpgcheck=0
enabled=1
EOF
# ==============================================
# 第三步
yum makecache
# 第四步
yum install socat
#第五步
wget https://github.com/rabbitmq/erlang-rpm/releases/download/v21.3.8.12/erlang-21.3.8.12-1.el7.x86_64.rpm
rpm -ivh erlang-21.3.8.12-1.el7.x86_64.rpm --force --nodeps
# 第六部
yum install rabbitmq-server
启动RabbitMQ服务器
# 设置服务自启动
systemctl enable rabbitmq-server
# 启动服务
systemctl start rabbitmq-server
启用图形化管理界面
# 开启管理界面插件
rabbitmq-plugins enable rabbitmq_management
# 防火墙打开 15672 管理端口(这是一个好习惯)
firewall-cmd --zone=public --add-port=15672/tcp --permanent
firewall-cmd --reload
# 打开客户端连接端口
firewall-cmd --zone=public --add-port=5672/tcp --permanent
firewall-cmd --reload
# 或者关掉防火墙
systemctl stop firewalld.service
主要端口介绍
- 4369 – erlang发现口
- 5672 – client端通信口
- 15672 – 管理界面ui端口
- 25672 – server间内部通信口
添加用户与重启
# 添加用户(用户名是Admin 密码也是Admin)
rabbitmqctl add_user admin admin
# 新用户设置用户为超级管理员
rabbitmqctl set_user_tags admin administrator
# 重启
systemctl restart rabbitmq-server
有如下界面,完事!
RabbitMQ的入门案例(使用工作模式中的简单模式)
项目依赖
<dependencies>
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.6.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
生产者(Producer)
package org.shu;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 这个类是消息中间的一个生产者
*/
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接(连接工厂)
ConnectionFactory cf = new ConnectionFactory();
// 2.设置参数 (虚拟机,用户名 密码 端口 ip...)
cf.setHost("192.168.126.131");//设置主机
cf.setPort(5672);//默认5672
cf.setVirtualHost("/shuyoujing");//默认就是/
cf.setUsername("shu");
cf.setPassword("shu");
// 3. 创建连接(Connection)
Connection connection = cf.newConnection();
// 4. 创建管道channel
Channel channel = connection.createChannel();
// 5. 创建队列(简单模式不需要交换机)
/**
* 参数解析
* String queue //队列名称
* boolean durable //是否持久化
* boolean exclusive //是否独占
* boolean autoDelete //是否自动删除
* Map<String, Object> arguments //参数
*/
//如果没有队列就创建队列
channel.queueDeclare("hello_world",true,false,false,null);
// 6. 发送消息
/**
* 参数信息
* (String exchange //交换机
* String routingKey // 路由名称 (只要跟队列名一样就可以发送消息)
* BasicProperties props// 配置信息
* byte[] body // 发送消息数据
*/
String body="舒有敬";
channel.basicPublish("","hello_world",null,body.getBytes());
// 7.释放资源
channel.close();
connection.close();
}
}
消费者(Consumer)
package org.shu;
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 {
// 1.建立连接(连接工厂)
ConnectionFactory cf = new ConnectionFactory();
// 2.设置参数 (虚拟机,用户名 密码 端口 ip...)
cf.setHost("192.168.126.131");//设置主机
cf.setPort(5672);//默认5672
cf.setVirtualHost("/shuyoujing");//默认就是/
cf.setUsername("shu");
cf.setPassword("shu");
// 3. 创建连接(Connection)
Connection connection = cf.newConnection();
// 4. 创建管道channel
Channel channel = connection.createChannel();
// 5. 创建队列(简单模式不需要交换机)
/**
* 参数解析
* String queue //队列名称
* boolean durable //是否持久化
* boolean exclusive //是否独占
* boolean autoDelete //是否自动删除
* Map<String, Object> arguments //参数
*/
//如果没有队列就创建队列
channel.queueDeclare("hello_world",true,false,false,null);
// 6 消费数据
/**
*String queue 1.队列名称
* autoAck 2.是否自动确认
* callback 3. 回调对象
*/
DefaultConsumer consumer=new DefaultConsumer(channel){
@Override //
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("标识"+consumerTag);
System.out.println("交换机"+envelope.getExchange());
System.out.println("properies"+properties);
System.out.println("收到的消息:"+new String(body));
}
};
channel.basicConsume("hello_world",true,consumer);
}
//不需要释放资源
}
RabbitMQ的工作模式
RabbitMQ 提供了 6 种工作模式
简单模式
上述案例就是
work queues (工作队列)
- 多了一个或者多个消费端
- 多个消费端共同消费一个队列中的小心
- 对于任务过重的情况使用这个工作地雷可以提高任务处理的速度
Publish/Subscribe (发布与订阅模式)
交换机(Exchange)
一方面接收生产者发送的消息,另一方面知道如何处理,如何操作取决于交换机的类型:
- Fanout:广播 将消息给所有绑定到交换机的队列
- Direct:定向 把消息交给符合指定的Routing key 的队列
- Topic: 通配符,把消息交给符合Routing pattern
交换机只负责转发消息,不具备消息的存储能力,因此如果没有任何队列与交换机绑定,或者没有符合路由规则的队列,则消息会丢失.
生产者
package org.shu;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 这个类是消息中间的一个生产者
*/
public class Producer_PubSub {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接(连接工厂)
ConnectionFactory cf = new ConnectionFactory();
// 2.设置参数 (虚拟机,用户名 密码 端口 ip...)
cf.setHost("192.168.126.131");//设置主机
cf.setPort(5672);//默认5672
cf.setVirtualHost("/shuyoujing");//默认就是/
cf.setUsername("shu");
cf.setPassword("shu");
// 3. 创建连接(Connection)
Connection connection = cf.newConnection();
// 4. 创建管道channel
Channel channel = connection.createChannel();
// 5 创建交换机
String exchangeName="Test_Fanout";
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT,true,false,false,null);
// 6创建队列
String queue1Name="test_fanout_queue1";
String queue2Name="test_fanout_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
//7 绑定队列给交换机
channel.queueBind(queue1Name,exchangeName,"");
channel.queueBind(queue2Name,exchangeName,"");
// 发送消息
String message="日志信息:---------";
channel.basicPublish(exchangeName,"",null,message.getBytes());
// 释放资源
channel.close();
connection.close();
}
}
消费者
package org.shu;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_PubSub1 {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接(连接工厂)
ConnectionFactory cf = new ConnectionFactory();
// 2.设置参数 (虚拟机,用户名 密码 端口 ip...)
cf.setHost("192.168.126.131");//设置主机
cf.setPort(5672);//默认5672
cf.setVirtualHost("/shuyoujing");//默认就是/
cf.setUsername("shu");
cf.setPassword("shu");
// 3. 创建连接(Connection)
Connection connection = cf.newConnection();
// 4. 创建管道channel
Channel channel = connection.createChannel();
// 5. 创建队列(简单模式不需要交换机)
/**
* 参数解析
* String queue //队列名称
* boolean durable //是否持久化
* boolean exclusive //是否独占
* boolean autoDelete //是否自动删除
* Map<String, Object> arguments //参数
*/
//如果没有队列就创建队列
String queue1Name="test_fanout_queue1";
String queue2Name="test_fanout_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
// 6 消费数据
/**
*String queue 1.队列名称
* autoAck 2.是否自动确认
* callback 3. 回调对象
*/
DefaultConsumer consumer=new DefaultConsumer(channel){
@Override //
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("标识"+consumerTag);
System.out.println("交换机"+envelope.getExchange());
System.out.println("properies"+properties);
System.out.println("收到的消息在控制台:"+new String(body));
}
};
channel.basicConsume(queue1Name,true,consumer);
}
//不需要释放资源
}
Routing 路由模式
- 队列与交换机的绑定,不能是任意绑定,而是要指定一个RoutingKey(路由Key)
- 消息的发送方在想Exchange发送消息是,也必然指定消息的RoutingKey
- Exchange不再把消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的Routing Key与消息的RoutingKey完全一致,才会接受到消息
生产者
package org.shu;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 这个类是消息中间的一个生产者
*/
public class Producer_Routing {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接(连接工厂)
ConnectionFactory cf = new ConnectionFactory();
// 2.设置参数 (虚拟机,用户名 密码 端口 ip...)
cf.setHost("192.168.126.131");//设置主机
cf.setPort(5672);//默认5672
cf.setVirtualHost("/shuyoujing");//默认就是/
cf.setUsername("shu");
cf.setPassword("shu");
// 3. 创建连接(Connection)
Connection connection = cf.newConnection();
// 4. 创建管道channel
Channel channel = connection.createChannel();
// 5 创建交换机
String exchangeName="Test_Direct";
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT,true,false,false,null);
// 6创建队列
String queue1Name="test_direct_queue1";
String queue2Name="test_direct_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
//7 绑定队列给交换机
channel.queueBind(queue1Name,exchangeName,"error");
channel.queueBind(queue2Name,exchangeName,"info");
channel.queueBind(queue2Name,exchangeName,"error");
channel.queueBind(queue2Name,exchangeName,"warning");
// 发送消息
String message="日志信息:---------日志级别:error";
channel.basicPublish(exchangeName,"error",null,message.getBytes());
// 释放资源
channel.close();
connection.close();
}
}
消费者1(只接受Error路由Key)
package org.shu;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_Rounting1 {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接(连接工厂)
ConnectionFactory cf = new ConnectionFactory();
// 2.设置参数 (虚拟机,用户名 密码 端口 ip...)
cf.setHost("192.168.126.131");//设置主机
cf.setPort(5672);//默认5672
cf.setVirtualHost("/shuyoujing");//默认就是/
cf.setUsername("shu");
cf.setPassword("shu");
// 3. 创建连接(Connection)
Connection connection = cf.newConnection();
// 4. 创建管道channel
Channel channel = connection.createChannel();
// 5. 创建队列(简单模式不需要交换机)
/**
* 参数解析
* String queue //队列名称
* boolean durable //是否持久化
* boolean exclusive //是否独占
* boolean autoDelete //是否自动删除
* Map<String, Object> arguments //参数
*/
//如果没有队列就创建队列
String queue1Name="test_direct_queue1";
String queue2Name="test_direct_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
// 6 消费数据
/**
*String queue 1.队列名称
* autoAck 2.是否自动确认
* callback 3. 回调对象
*/
DefaultConsumer consumer=new DefaultConsumer(channel){
@Override //
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("标识"+consumerTag);
System.out.println("交换机"+envelope.getExchange());
System.out.println("properies"+properties);
System.out.println("收到的消息在数据库中:"+new String(body));
}
};
channel.basicConsume(queue1Name,true,consumer);
}
//不需要释放资源
}
消费者2(可以接受3种路由Key)打印在控制台
package org.shu;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_Rounting2 {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接(连接工厂)
ConnectionFactory cf = new ConnectionFactory();
// 2.设置参数 (虚拟机,用户名 密码 端口 ip...)
cf.setHost("192.168.126.131");//设置主机
cf.setPort(5672);//默认5672
cf.setVirtualHost("/shuyoujing");//默认就是/
cf.setUsername("shu");
cf.setPassword("shu");
// 3. 创建连接(Connection)
Connection connection = cf.newConnection();
// 4. 创建管道channel
Channel channel = connection.createChannel();
// 5. 创建队列(简单模式不需要交换机)
/**
* 参数解析
* String queue //队列名称
* boolean durable //是否持久化
* boolean exclusive //是否独占
* boolean autoDelete //是否自动删除
* Map<String, Object> arguments //参数
*/
//如果没有队列就创建队列
String queue1Name="test_direct_queue1";
String queue2Name="test_direct_queue2";
channel.queueDeclare(queue2Name,true,false,false,null);
// 6 消费数据
/**
*String queue 1.队列名称
* autoAck 2.是否自动确认
* callback 3. 回调对象
*/
DefaultConsumer consumer=new DefaultConsumer(channel){
@Override //
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("标识"+consumerTag);
System.out.println("交换机"+envelope.getExchange());
System.out.println("properies"+properties);
System.out.println("收到的消息在控制台中:"+new String(body));
}
};
channel.basicConsume(queue2Name,true,consumer);
}
//不需要释放资源
}
Topics 主题模式(通配符模式)
生产者
package org.shu;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 这个类是消息中间的一个生产者
*/
public class Producer_Topic {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接(连接工厂)
ConnectionFactory cf = new ConnectionFactory();
// 2.设置参数 (虚拟机,用户名 密码 端口 ip...)
cf.setHost("192.168.126.131");//设置主机
cf.setPort(5672);//默认5672
cf.setVirtualHost("/shuyoujing");//默认就是/
cf.setUsername("shu");
cf.setPassword("shu");
// 3. 创建连接(Connection)
Connection connection = cf.newConnection();
// 4. 创建管道channel
Channel channel = connection.createChannel();
// 5 创建交换机
String exchangeName="Test_Topic";
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC,true,false,false,null);
// 6创建队列
String queue1Name="test_topic_queue1";
String queue2Name="test_topic_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
//7 绑定队列给交换机
channel.queueBind(queue1Name,exchangeName,"#.error");
channel.queueBind(queue1Name,exchangeName,"order.*");
channel.queueBind(queue2Name,exchangeName,"*.*");
// 发送消息
String message="日志信息:---------日志级别:NaN";
channel.basicPublish(exchangeName,"x.x",null,message.getBytes());
// 释放资源
channel.close();
connection.close();
}
}
消费者1
package org.shu;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_Topic1 {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接(连接工厂)
ConnectionFactory cf = new ConnectionFactory();
// 2.设置参数 (虚拟机,用户名 密码 端口 ip...)
cf.setHost("192.168.126.131");//设置主机
cf.setPort(5672);//默认5672
cf.setVirtualHost("/shuyoujing");//默认就是/
cf.setUsername("shu");
cf.setPassword("shu");
// 3. 创建连接(Connection)
Connection connection = cf.newConnection();
// 4. 创建管道channel
Channel channel = connection.createChannel();
// 5. 创建队列(简单模式不需要交换机)
/**
* 参数解析
* String queue //队列名称
* boolean durable //是否持久化
* boolean exclusive //是否独占
* boolean autoDelete //是否自动删除
* Map<String, Object> arguments //参数
*/
//如果没有队列就创建队列
String queue1Name="test_topic_queue1";
String queue2Name="test_topic_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
// 6 消费数据
/**
*String queue 1.队列名称
* autoAck 2.是否自动确认
* callback 3. 回调对象
*/
DefaultConsumer consumer=new DefaultConsumer(channel){
@Override //
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("标识"+consumerTag);
System.out.println("交换机"+envelope.getExchange());
System.out.println("properies"+properties);
System.out.println("收到的消息在数据库中:"+new String(body));
}
};
channel.basicConsume(queue1Name,true,consumer);
}
//不需要释放资源
}
消费者2
package org.shu;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer_Topic2 {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接(连接工厂)
ConnectionFactory cf = new ConnectionFactory();
// 2.设置参数 (虚拟机,用户名 密码 端口 ip...)
cf.setHost("192.168.126.131");//设置主机
cf.setPort(5672);//默认5672
cf.setVirtualHost("/shuyoujing");//默认就是/
cf.setUsername("shu");
cf.setPassword("shu");
// 3. 创建连接(Connection)
Connection connection = cf.newConnection();
// 4. 创建管道channel
Channel channel = connection.createChannel();
// 5. 创建队列(简单模式不需要交换机)
/**
* 参数解析
* String queue //队列名称
* boolean durable //是否持久化
* boolean exclusive //是否独占
* boolean autoDelete //是否自动删除
* Map<String, Object> arguments //参数
*/
//如果没有队列就创建队列
String queue1Name="test_topic_queue1";
String queue2Name="test_topic_queue2";
channel.queueDeclare(queue2Name,true,false,false,null);
// 6 消费数据
/**
*String queue 1.队列名称
* autoAck 2.是否自动确认
* callback 3. 回调对象
*/
DefaultConsumer consumer=new DefaultConsumer(channel){
@Override //
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("标识"+consumerTag);
System.out.println("交换机"+envelope.getExchange());
System.out.println("properies"+properties);
System.out.println("收到的消息在控制台中:"+new String(body));
}
};
channel.basicConsume(queue2Name,true,consumer);
}
//不需要释放资源
}
RPC 远程调用模式(远程调用)
Spring整合RabbitMQ
创建两个Maven项目
依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
properties文件(与依赖一样,生产者消费者都要有这个)
rabbitmq.host=192.168.126.131
rabbitmq.port=5672
rabbitmq.username=shu
rabbitmq.password=shu
rabbitmq.virtual-host=/shuyoujing
生产者的XML配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd
http://www.springframework.org/schema/rabbit ">
<!--加载配置文件-->
<context:property-placeholder location="classpath:properties/rabbitmq.properties"/>
<!-- 定义rabbitmq connectionFactory -->
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"/>
<!--定义管理交换机、队列-->
<rabbit:admin connection-factory="connectionFactory"/>
<!--定义持久化队列,不存在则自动创建;不绑定到交换机则绑定到默认交换机
默认交换机类型为direct,名字为:"",路由键为队列的名称
-->
<rabbit:queue id="spring_queue" name="spring_queue" auto-declare="true"/>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~广播;所有队列都能收到消息~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!--定义广播交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_fanout_queue_1" name="spring_fanout_queue_1" auto-declare="true"/>
<!--定义广播交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_fanout_queue_2" name="spring_fanout_queue_2" auto-declare="true"/>
<!--定义广播类型交换机;并绑定上述两个队列-->
<rabbit:fanout-exchange id="spring_fanout_exchange" name="spring_fanout_exchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding queue="spring_fanout_queue_1"/>
<rabbit:binding queue="spring_fanout_queue_2"/>
</rabbit:bindings>
</rabbit:fanout-exchange>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~通配符;*匹配一个单词,#匹配多个单词 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!--定义广播交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_topic_queue_star" name="spring_topic_queue_star" auto-declare="true"/>
<!--定义广播交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_topic_queue_well" name="spring_topic_queue_well" auto-declare="true"/>
<!--定义广播交换机中的持久化队列,不存在则自动创建-->
<rabbit:queue id="spring_topic_queue_well2" name="spring_topic_queue_well2" auto-declare="true"/>
<rabbit:topic-exchange id="spring_topic_exchange" name="spring_topic_exchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding pattern="heima.*" queue="spring_topic_queue_star"/>
<rabbit:binding pattern="heima.#" queue="spring_topic_queue_well"/>
<rabbit:binding pattern="itcast.#" queue="spring_topic_queue_well2"/>
</rabbit:bindings>
</rabbit:topic-exchange>
<!--定义rabbitTemplate对象操作可以在代码中方便发送消息-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
</beans>
消费者的XML文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!--加载配置文件-->
<context:property-placeholder location="classpath:properties/rabbitmq.properties"/>
<!-- 定义rabbitmq connectionFactory -->
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"/>
<bean id="springQueueListener" class="com.itheima.rabbitmq.listener.SpringQueueListener"/>
<!-- <bean id="fanoutListener1" class="com.itheima.rabbitmq.listener.FanoutListener1"/>-->
<!-- <bean id="fanoutListener2" class="com.itheima.rabbitmq.listener.FanoutListener2"/>-->
<!-- <bean id="topicListenerStar" class="com.itheima.rabbitmq.listener.TopicListenerStar"/>-->
<!-- <bean id="topicListenerWell" class="com.itheima.rabbitmq.listener.TopicListenerWell"/>-->
<!-- <bean id="topicListenerWell2" class="com.itheima.rabbitmq.listener.TopicListenerWell2"/>-->
<rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
<rabbit:listener ref="springQueueListener" queue-names="spring_queue"/>
<!-- <rabbit:listener ref="fanoutListener1" queue-names="spring_fanout_queue_1"/>-->
<!-- <rabbit:listener ref="fanoutListener2" queue-names="spring_fanout_queue_2"/>-->
<!-- <rabbit:listener ref="topicListenerStar" queue-names="spring_topic_queue_star"/>-->
<!-- <rabbit:listener ref="topicListenerWell" queue-names="spring_topic_queue_well"/>-->
<!-- <rabbit:listener ref="topicListenerWell2" queue-names="spring_topic_queue_well2"/>-->
</rabbit:listener-container>
</beans>
生产者的代码
package org.shu;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.nio.channels.Pipe;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class ProducerTest {
@Autowired
private RabbitTemplate template;
@Test
public void testHelloWorld(){
template.convertAndSend("spring_queue","Hello World By Spring ");
}
@Test
public void testFanout(){
template.convertAndSend("spring_fanout_exchange","","Hello FanOut");
}
@Test
public void testTopic( ){
template.convertAndSend("spring_topic_exchange","heima.shu.shu","Spring Topci");
}
}
消费者的代码
提供一个类,实现MessageListener接口
package com.itheima.rabbitmq.listener;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
public class SpringQueueListener implements MessageListener {
@Override
public void onMessage(Message message) {
System.out.println(new String(message.getBody()));
}
}
测试代码
package org.shu;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-consumer.xml")
public class ConsumerTest {
@Test
public void test1(){
boolean flag=true;
while (true){
}
}
}
SpringBoot整合RabbitMQ
创建SpringBoott项目
引入依赖坐标
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
编写yml文件的配置,基本信息配置
spring:
rabbitmq:
host: 192.168.126.131
port: 5672
username: admin
password: admin
virtual-host: /shushu
生产者:定义交换机,队列以及绑定关机的配置类
package org.shu.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfig {
public static final String EXCHNAGE_NAME="bootExchange";
public static final String QUEUE_NAME="bootQueue";
//1. 定义交换机 Exchange
@Bean("exchange")
public Exchange getExchange(){
return ExchangeBuilder.topicExchange(EXCHNAGE_NAME).durable(true).build();
}
//2. 定义队列 Queue
@Bean("queue")
public Queue getQueue(){
return QueueBuilder.durable(QUEUE_NAME).build();
}
//3. 绑定交换机 Bingding
/**
* 知道队列
* 知道交换机
* routingkey
* @return
*/
@Bean
public Binding binding(@Qualifier("queue") Queue queue,@Qualifier("exchange") Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("boot.#").noargs();
}
}
注入RabbitTemplate调用方法完成消息队列
测试类
package org.shu.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.shu.config.RabbitConfig;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest
@RunWith(SpringRunner.class)
public class ProducerTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSend(){
rabbitTemplate.convertAndSend(RabbitConfig.EXCHNAGE_NAME,"boot.hh","这是SpringBoot整合给出的消息");
}
}
消费者获取数据
package org.shu.comsumerspringboot.listener;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class RabbitListeners {
@RabbitListener(queues = "bootQueue")
public void GetMessage(Message message){
System.out.println(new String(message.getBody()));
}
}