※快速掌握消息队列RabbitMQ
一.RabbitMQ概述
(一)什么是消息队列MQ
- 消息队列(Message Queue),后文称MQ,是一种 跨进程的通信机制,用于上下游传递消息。
- MQ作为消息中间件,最主要的作用系统之间的信息传 递进行“解耦”,MQ是数据可靠性的重要保障。
(二)什么是RabbitMQ
- RabbitMQ是全世界最火的开源消息代理服务器, 在全世界拥有超过35000个项目部署在 RabbitMQ。
- RabbitMQ支持几乎所有的操作系统与编程语言。
- Rabbit提供了高并发、高可用的成熟方案,支持 多种消息协议,易于部署与使用。
(三)和同类产品比较
(四)RabbitMQ应用场景
- 异构系统的数据传递
- 高并发程序的流量控制
- 基于P2P,P2PPP的程序
- 分布式系统的事务一致性TCC
- 高可靠性的交易系统
(五)消息状态
Ready – 消息已被送入队列,等待被消费
Unacked – 消息已经被消费者认领,但还未被确认“已被消费” – Unacked状态下,消费者断开连接则消息回到”Ready” – 没有确认,客户有没有断开连接,则一直处于Unacked。 Finished – 调用basicAck()方法后,表示消息已被消费,从队列中移除。
(六)RabbitMQ六中工作模式
二.RabbitMQ单点安装
(一)安装步骤
(二)常用命令
启动与关闭
rabbitmq-server 前台启动服务
rabbitmq-server -detached 后台启动服务
rabbitmqctl stop 停止服务(相当于关闭进程)
终止与启动应用
rabbitmqctl start_app 启动应用 (不会关闭进程)
rabbitmqctl stop_app 终止应用
用户管理
rabbitmqctl add_user {username} {password} – 创建新用户
rabbitmqctl delete_user {username} – 删除用户
rabbitmqctl change_password {username} {newpassword} – 重置密码
rabbitmqctl set_user_tags {username} {tag} – 授予用户角色(Tag)
rabbitmqctl set_permissions -p / user_admin ‘.’ '.’ ‘.*’ – 设置用户允许访问的vhost
RabbitMQ用户四种Tag
超级管理员(administrator) –
可登陆管理控制台(启用management plugin的情况下),可查看所有 的信息,并且可以对用户,策略(policy)进行操作。
监控者(monitoring) – 登陆管理控制台(启用management plugin的情况下),同时可以查看 rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等) u 策略制定者(policymaker) – 可登陆管理控制台(启用management plugin的情况下), 同时可以对 policy进行管理。但无法查看节点的相关信息(上图红框标识的部分)。
普通管理者(management) – 仅可登陆管理控制台(启用management plugin的情况下),无法看到 节点信息,也无法对策略进行管理。
三.rabbitMQ集群安装
1.搭建RabbitMQ集群采用主备模式、镜像模式、远程模式、多活模式中的镜像模式,部署在腾讯云辉:106.53.70.31,腾讯云芳:129.204.181.161,腾讯云传:129.204.152.2,搭建图如下:
2.搭建步骤
(1)https://www.erlang-solutions.com/resources/download.html下载RabbitMQ必备
组件
Erlang插件esl-erlang_22.0.7-1_centos_7_amd64.rpm;https://www.rabbitmq.com/download.html下载RabbitMQ安装包rabbitmq-server-3.7.17-1.el7.noarch.rpm
把这两个文件上传到腾讯云服务器芳Ip地址:129.204.181.161的/usr/local/temp
腾讯云服务器传Ip地址:129.204.152.2
(2)在两台服务器终端执行以下命令来搭建RabbitMQ
[root@VM_0_12_centos ~]# cd /usr/local/temp
[root@VM_0_12_centos temp]# rpm -ivh --nodeps esl-erlang_22.0.7-1_centos_7_amd64.rpm
[root@VM_0_12_centos temp]# rpm -ivh --nodeps rabbitmq-server-3.7.17-1.el7.noarch.rpm
[root@VM_0_12_centos temp]# rabbitmq-plugins enable rabbitmq_management
[root@VM_0_12_centos temp]# chown rabbitmq:rabbitmq /var/lib/rabbitmq/.erlang.cookie
[root@VM_0_12_centos temp]# rabbitmq-server -detached
[root@VM_0_12_centos temp]# rabbitmqctl add_user root root
[root@VM_0_12_centos temp]# rabbitmqctl set_user_tags root administrator
[root@VM_0_12_centos temp]# rabbitmqctl set_permissions -p / root ‘.’ '.’ ‘.*’
最后开启15672控制台端口, 5672客户端访问端口
(3)在两台服务器终端执行以下命令以搭建Mirror镜像集群
129.204.181.161服务器
[root@VM_0_12_centos rabbitmq]# vim /etc/hostname 修改为m1
[root@VM_0_12_centos rabbitmq]# vim /etc/hosts
添加
129.204.181.161 m1
129.204.152.2 m2
开放4369 25672端口
[root@VM_0_12_centos rabbitmq]# scp /var/lib/rabbitmq/.erlang.cookie 129.204.152.2:/var/lib/rabbitmq/
[root@VM_0_12_centos rabbitmq]# chmod 400 /var/lib/rabbitmq/.erlang.cookie
129.204.152.2服务器
[root@VM_0_12_centos rabbitmq]# vim /etc/hostname 修改为m2
[root@VM_0_12_centos rabbitmq]# vim /etc/hosts
添加
129.204.181.161 m1
129.204.152.2 m2
开放4369 25672端口
[root@VM_0_12_centos rabbitmq]# chmod 400 /var/lib/rabbitmq/.erlang.cookie
[root@VM_0_12_centos temp]# rabbitmqctl stop_app
[root@VM_0_12_centos temp]# rabbitmqctl join_cluster rabbit@m1
[root@VM_0_12_centos temp]# rabbitmqctl start_app
(4)在106.53.70.31(辉)服务器终端执行以下命令来搭建Haproxy对RabbitMQ负载均衡
[root@VM_0_16_centos ~]# yum install haproxy
vim /etc/haproxy/haproxy.cfg
在末尾添加内容如下
#---------------------------------------------------------------------
my
#---------------------------------------------------------------------
listen rabbitmq
bind 0.0.0.0:5672
mode tcp
option tcplog
balance roundrobin
#option tcpka
server rabbit1 xx.xx.1.1:5672 check inter 5s rise 2 fall 3
server rabbit2 xx.xx.1.2:5672 check inter 5s rise 2 fall 3
listen http_front
bind 0.0.0.0:1080
stats refresh 30s
stats uri /haproxy?stats
stats auth admin:admin
然后运行Haproxy
haproxy -f /etc/haproxy/haproxy.cfg
重启的方式是
service haproxy restart
四.java端使用RabbitMQ
(一)创建Maven工程,导入jar包
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
(二)创建工具类
package com.itlaoqi.rabbitmq.utils;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class RabbitUtils {
private static ConnectionFactory connectionFactory = new ConnectionFactory();
static {
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);//5672是RabbitMQ的默认端口号
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/test");
}
public static Connection getConnection(){
Connection conn = null;
try {
conn = connectionFactory.newConnection();
return conn;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
常量类
package com.itlaoqi.rabbitmq.utils;
public class RabbitConstant {
public static final String QUEUE_HELLOWORLD = "helloworld";
public static final String QUEUE_SMS = "sms";
public static final String EXCHANGE_WEATHER = "weather";
public static final String EXCHANGE_WEATHER_ROUTING = "weather_routing";
public static final String QUEUE_BAIDU = "baidu";
public static final String QUEUE_SINA = "sina";
public static final String EXCHANGE_WEATHER_TOPIC = "weather_topic";
}
(三)简单模式
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, false, false, false, null);
//四个参数
//exchange 交换机,暂时用不到,在后面进行发布订阅时才会用到
//队列名称
//额外的设置属性
//最后一个参数是要传递的消息字节数组
String message = "helloworld!";
channel.basicPublish("" , RabbitConstant.QUEUE_HELLOWORLD,null , message.getBytes());
channel.close();
conn.close();
System.out.println("发送数据成功");
}
}
消费者
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
Connection conn= RabbitUtils.getConnection();
//创建通道
Channel channel = conn.createChannel();
channel.queueDeclare(RabbitConstant.QUEUE_HELLOWORLD, false, false, false, null);
//创建一个消息消费者
//第二个参数代表是否自动确认收到消息,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 {
/*super.handleDelivery(consumerTag,envelope,properties,body);*/
String messageBody = new String(body);
System.out.println("消费者接收到:" + messageBody);
//签收消息,确认消息
//envelope.getDeliveryTag() 获取这个消息的TagId
//false只确认签收当前的消息,设置为true的时候则代表签收该消费者所有未签收的消息
channel.basicAck(envelope.getDeliveryTag() , false);
}
}
(四)工作队列模式
实体类
package com.itlaoqi.rabbitmq.workqueue;
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;
}
}
生产者
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 = 100 ; i <= 200 ; 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 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);
}
});
}
}
(五)发布订阅模式(创建交换机为fanout)
生产者
public class WeatherBureau {
public static void main(String[] args) throws IOException, TimeoutException {
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();
}
}
消费者(可复制多个),然后改下队列名称
百度消费者
public class Baidu {
public static void main(String[] args) throws IOException {
Connection connection = RabbitUtils.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(RabbitConstant.QUEUE_BAIDU, false, false, false, null);
//queueBind用于将队列与交换机绑定
//参数1:队列名 参数2:交互机名 参数三:路由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));
channel.basicAck(envelope.getDeliveryTag() , false);
}
});
}
}
新浪消费者
public class Sina {
public static void main(String[] args) throws IOException {
Connection connection = RabbitUtils.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(RabbitConstant.QUEUE_SINA, false, false, false, null);
//queueBind用于将队列与交换机绑定
//参数1:队列名 参数2:交互机名 参数三:路由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));
channel.basicAck(envelope.getDeliveryTag() , false);
}
});
}
}
(六)路由模式(精确匹配)(创建交换机为direct)
生产者
public class WeatherBureau {
public static void main(String[] args) throws IOException, TimeoutException {
Map area = new LinkedHashMap<String, String>();
area.put("china.hebei.shijiazhuang.20991011", "中国河北石家庄20991011天气数据");
area.put("china.shandong.qingdao.20991011", "中国山东青岛20991011天气数据");
area.put("china.henan.zhengzhou.20991011", "中国河南郑州20991011天气数据");
area.put("us.cal.la.20991011", "美国加州洛杉矶20991011天气数据");
area.put("china.hebei.shijiazhuang.20991012", "中国河北石家庄20991012天气数据");
area.put("china.shandong.qingdao.20991012", "中国山东青岛20991012天气数据");
area.put("china.henan.zhengzhou.20991012", "中国河南郑州20991012天气数据");
area.put("us.cal.la.20991012", "美国加州洛杉矶20991012天气数据");
Connection connection = RabbitUtils.getConnection();
Channel channel = connection.createChannel();
Iterator<Map.Entry<String, String>> itr = area.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, String> me = itr.next();
//Routing key 第二个参数相当于数据筛选的条件
channel.basicPublish(RabbitConstant.EXCHANGE_WEATHER_ROUTING,me.getKey() , null , me.getValue().getBytes());
}
channel.close();
connection.close();
}
}
百度消费者
public class Baidu {
public static void main(String[] args) throws IOException {
Connection connection = RabbitUtils.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(RabbitConstant.QUEUE_BAIDU, false, false, false, null);
//queueBind用于将队列与交换机绑定
//参数1:队列名 参数2:交互机名 参数三:路由key
channel.queueBind(RabbitConstant.QUEUE_BAIDU, RabbitConstant.EXCHANGE_WEATHER_ROUTING, "china.shandong.qingdao.20991011");
channel.queueBind(RabbitConstant.QUEUE_BAIDU, RabbitConstant.EXCHANGE_WEATHER_ROUTING, "china.shandong.qingdao.20991012");
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));
channel.basicAck(envelope.getDeliveryTag() , false);
}
});
}
}
新浪消费者
public class Sina {
public static void main(String[] args) throws IOException {
Connection connection = RabbitUtils.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(RabbitConstant.QUEUE_SINA, false, false, false, null);
channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER_ROUTING, "us.cal.la.20991011");
channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER_ROUTING, "china.henan.zhengzhou.20991011");
channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER_ROUTING, "us.cal.la.20991012");
channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER_ROUTING, "china.henan.zhengzhou.20991012");
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));
channel.basicAck(envelope.getDeliveryTag() , false);
}
});
}
}
(七)主体模式(模糊匹配)(交换机为topic,*匹配单个字符,#匹配所有)
生产者
public class WeatherBureau {
public static void main(String[] args) throws IOException, TimeoutException {
Map area = new LinkedHashMap<String, String>();
area.put("china.hebei.shijiazhuang.20991011", "中国河北石家庄20991011天气数据");
area.put("china.shandong.qingdao.20991011", "中国山东青岛20991011天气数据");
area.put("china.henan.zhengzhou.20991011", "中国河南郑州20991011天气数据");
area.put("us.cal.la.20991011", "美国加州洛杉矶20991011天气数据");
area.put("china.hebei.shijiazhuang.20991012", "中国河北石家庄20991012天气数据");
area.put("china.shandong.qingdao.20991012", "中国山东青岛20991012天气数据");
area.put("china.henan.zhengzhou.20991012", "中国河南郑州20991012天气数据");
area.put("us.cal.la.20991012", "美国加州洛杉矶20991012天气数据");
Connection connection = RabbitUtils.getConnection();
Channel channel = connection.createChannel();
Iterator<Map.Entry<String, String>> itr = area.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, String> me = itr.next();
//Routing key 第二个参数相当于数据筛选的条件
channel.basicPublish(RabbitConstant.EXCHANGE_WEATHER_TOPIC,me.getKey() , null , me.getValue().getBytes());
}
channel.close();
connection.close();
}
}
百度消费者
public class Baidu {
public static void main(String[] args) throws IOException {
Connection connection = RabbitUtils.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(RabbitConstant.QUEUE_BAIDU, false, false, false, null);
//queueBind用于将队列与交换机绑定
//参数1:队列名 参数2:交互机名 参数三:路由key
channel.queueBind(RabbitConstant.QUEUE_BAIDU, RabbitConstant.EXCHANGE_WEATHER_TOPIC, "*.*.*.20991011");
//channel.queueUnbind(RabbitConstant.QUEUE_BAIDU, RabbitConstant.EXCHANGE_WEATHER_TOPIC, "*.*.*.20991011");
//*.hebei.*.*
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));
channel.basicAck(envelope.getDeliveryTag() , false);
}
});
}
}
新浪消费者
public class Sina {
public static void main(String[] args) throws IOException {
Connection connection = RabbitUtils.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(RabbitConstant.QUEUE_SINA, false, false, false, null);
channel.queueBind(RabbitConstant.QUEUE_SINA, RabbitConstant.EXCHANGE_WEATHER_TOPIC, "us.#");
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));
channel.basicAck(envelope.getDeliveryTag() , false);
}
});
}
}
(八)生产者与MQ的交互
生产者
public class WeatherBureau {
public static void main(String[] args) throws IOException, TimeoutException {
Map area = new LinkedHashMap<String, String>();
area.put("china.hebei.shijiazhuang.20991011", "中国河北石家庄20991011天气数据");
area.put("china.shandong.qingdao.20991011", "中国山东青岛20991011天气数据");
area.put("china.henan.zhengzhou.20991011", "中国河南郑州20991011天气数据");
area.put("us.cal.la.20991011", "美国加州洛杉矶20991011天气数据");
area.put("china.hebei.shijiazhuang.20991012", "中国河北石家庄20991012天气数据");
area.put("china.shandong.qingdao.20991012", "中国山东青岛20991012天气数据");
area.put("china.henan.zhengzhou.20991012", "中国河南郑州20991012天气数据");
area.put("us.cal.la.20991012", "美国加州洛杉矶20991012天气数据");
Connection connection = RabbitUtils.getConnection();
Channel channel = connection.createChannel();
//开启confirm监听模式
channel.confirmSelect();
channel.addConfirmListener(new ConfirmListener() {
public void handleAck(long l, boolean b) throws IOException {
//第二个参数代表接收的数据是否为批量接收,一般我们用不到。
System.out.println("消息已被Broker接收,Tag:" + l);
}
public void handleNack(long l, boolean b) throws IOException {
System.out.println("消息已被Broker拒收,Tag:" + l);
}
});
channel.addReturnListener(new ReturnCallback() {
public void handle(Return r) {
System.err.println("===========================");
System.err.println("Return编码:" + r.getReplyCode() + "-Return描述:" + r.getReplyText());
System.err.println("交换机:" + r.getExchange() + "-路由key:" + r.getRoutingKey() );
System.err.println("Return主题:" + new String(r.getBody()));
System.err.println("===========================");
}
});
Iterator<Map.Entry<String, String>> itr = area.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, String> me = itr.next();
//Routing key 第二个参数相当于数据筛选的条件
//第三个参数为:mandatory true代表如果消息无法正常投递则return回生产者,如果false,则直接将消息放弃。
channel.basicPublish(RabbitConstant.EXCHANGE_WEATHER_TOPIC,me.getKey() ,true, null , me.getValue().getBytes());
}
/*channel.close();
connection.close();*/
}
}
五.Spring与RabbitMQ整合
(一)生产者
1.新建Maven工程,导入jar包
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.8.Release</version>
</dependency>
2.增加配置文件
<?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: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/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!-- 设置连接工厂,配置基本参数 -->
<rabbit:connection-factory id="connectionFactory" host="127.0.0.1" port="5672" username="guest" password="guest"
virtual-host="/test"></rabbit:connection-factory>
<!--
fanout-exchange | direct-exchange | topic-exchange
声明一个名为topicExchange的topic交换机,如果这个交换机不存在,则自动创建
-->
<rabbit:topic-exchange name="topicExchange" auto-declare="true">
</rabbit:topic-exchange>
<!-- Spring为我们封装了RabbitTemplate对象来简化生产者发送数据的过程,对常用的方法进行了封装。 -->
<rabbit:template id="template" connection-factory="connectionFactory" exchange="topicExchange"></rabbit:template>
<!--在生产者中配置template对象,用于发送数据-->
<bean id="newsProducer" class="com.itlaoqi.rabbit.exchange.NewsProducer">
<property name="rabbitTemplate" ref="template"/>
</bean>
<!-- RabbitAdmin对象用于创建、绑定、管理队列与交换机 -->
<rabbit:admin id="rabbitAdmin" connection-factory="connectionFactory"/>
</beans>
3.创建实体类,并序列化
public class News implements Serializable{
private String source;
private String title;
private Date createTime;
private String content;
public News(String source, String title, Date createTime, String content) {
this.source = source;
this.title = title;
this.createTime = createTime;
this.content = content;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
4.编写生产者
public class NewsProducer {
private RabbitTemplate rabbitTemplate = null;
public RabbitTemplate getRabbitTemplate() {
return rabbitTemplate;
}
public void setRabbitTemplate(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
public void sendNews(String routingKey , News news){
//convertAndSend 用于向exchange发送数据
//第一个参数是routingkey
//第二个参数是要传递的对象,可以是字符串、byte【】或者任何实现了【序列化接口】的对象
rabbitTemplate.convertAndSend(routingKey , news);
System.out.println("新闻发送成功");
}
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
NewsProducer np = (NewsProducer)ctx.getBean("newsProducer");
np.sendNews("us.20190101" , new News("新华社" , "特朗普又又又退群啦" , new Date() , "国际新闻内容"));
np.sendNews("china.20190101" , new News("凤凰TV" , "XXX企业荣登世界500强" , new Date() , "国内新闻内容"));
}
}
5.利用rabbitAdmin直接操作队列和交换机
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class RabbitAdminTestor {
@Resource(name="rabbitAdmin")
private RabbitAdmin rabbitAdmin;
@Resource
private RabbitTemplate rabbitTemplate;
@Test
public void testCreateExchange(){
rabbitAdmin.declareExchange(new FanoutExchange("test.exchange.fanout" , true ,false));
rabbitAdmin.declareExchange(new DirectExchange("test.exchange.direct" , true ,false));
rabbitAdmin.declareExchange(new TopicExchange("test.exchange.topic" , true ,false));
}
@Test
public void testQueueAndBind(){
rabbitAdmin.declareQueue(new Queue("test.queue"));
rabbitAdmin.declareBinding(new Binding(
"test.queue", Binding.DestinationType.QUEUE,
"test.exchange.topic", "#", new HashMap<String, Object>()
));
rabbitTemplate.convertAndSend("test.exchange.topic" , "abc" , "abc123");
}
@Test
public void testDelete(){
rabbitAdmin.deleteQueue("test.queue");
rabbitAdmin.deleteExchange("test.exchange.fanout");
rabbitAdmin.deleteExchange("test.exchange.direct");
rabbitAdmin.deleteExchange("test.exchange.topic");
}
}
(二)消费者
1.创建Maven工程,导入jar包
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
2.增加配置文件
<?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: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/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<rabbit:connection-factory id="connectionFactory" host="127.0.0.1" port="5672" username="guest" password="guest"
virtual-host="/test"></rabbit:connection-factory>
<rabbit:admin connection-factory="connectionFactory"></rabbit:admin>
<!--创建队列-->
<rabbit:queue name="topicQueue" auto-declare="true" auto-delete="false" durable="false" exclusive="false"/>
<!--交换机与队列绑定,并指明筛选条件-->
<rabbit:topic-exchange name="topicExchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding queue="topicQueue" pattern="us.*"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange>
<!--启动消费者后,Spring底层自动监听对应的topicQueue数据,一旦有新的消息进来,自动传入到consumer Bean的recv的News参数中,
之后再程序对News进一步处理-->
<rabbit:listener-container connection-factory="connectionFactory">
<rabbit:listener ref="consumer" method="recv" queue-names="topicQueue"/>
</rabbit:listener-container>
<bean id="consumer" class="com.itlaoqi.rabbitmq.NewsConsumer"></bean>
</beans>
3.编写实体类
public class News implements Serializable{
private String source;
private String title;
private Date createTime;
private String content;
public News(String source, String title, Date createTime, String content) {
this.source = source;
this.title = title;
this.createTime = createTime;
this.content = content;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
4.编写消费者方法类
public class NewsConsumer {
public void recv(News news){
System.out.println("接收到最新新闻:" + news.getTitle() + ":" + news.getSource());
}
public static void main(String[] args) {
//初始化IOC容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
}
}
六.Spring Boot与RabbitMQ整合
(一)生产者
1.创建spring boot工程,导入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.修改配置文件
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/test
spring.rabbitmq.connection-timeout=1000ms
#producer
#confirmlistener
spring.rabbitmq.publisher-confirms=true
#returnlistener
spring.rabbitmq.publisher-returns=true
spring.rabbitmq.template.mandatory=true
3.创建实体类,并序列化
public class Employee implements Serializable{
private String empno;
private String name;
private Integer age;
public Employee(String empno, String name, Integer age) {
this.empno = empno;
this.name = name;
this.age = age;
}
public String getEmpno() {
return empno;
}
public void setEmpno(String empno) {
this.empno = empno;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
4.编写生产者方法(需要自己创建好虚拟机,交换机,队列和绑定)
@Component
public class MessageProducer {
@Resource
private RabbitTemplate rabbitTemplate ;
RabbitTemplate.ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
@Override
/**
* CorrelationData 消息的附加信息,即自定义id
* isack 代表消息是否被broker(MQ)接收 true 代表接收 false代表拒收。
* cause 如果拒收cause则说明拒收的原因,帮助我们进行后续处理
*/
public void confirm(CorrelationData correlationData, boolean isack, String cause) {
System.out.println(correlationData);
System.out.println("ack:" + isack);
if(isack == false){
System.err.println(cause);
}
}
};
RabbitTemplate.ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingkey) {
System.err.println("Code:" + replyCode + ",Text:" + replyText );
System.err.println("Exchange:" + exchange + ",RoutingKey:" + routingkey );
}
};
public void sendMsg(Employee emp){
//CorrelationData对象的作用是作为消息的附加信息传递,通常我们用它来保存消息的自定义id
CorrelationData cd = new CorrelationData(emp.getEmpno() + "-" + new Date().getTime());
rabbitTemplate.setConfirmCallback(confirmCallback);
rabbitTemplate.setReturnCallback(returnCallback);
rabbitTemplate.convertAndSend("springboot-exchange" , "hr.employee" , emp , cd);
}
}
(二)消费者
1.创建spring boot工程,导入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.编写配置文件
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/test
spring.rabbitmq.connection-timeout=1000ms
#手动确认
spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.simple.concurrency=1
spring.rabbitmq.listener.simple.max-concurrency=5
3.编写实体类
public class Employee implements Serializable{
private String empno;
private String name;
private Integer age;
public Employee(String empno, String name, Integer age) {
this.empno = empno;
this.name = name;
this.age = age;
}
public String getEmpno() {
return empno;
}
public void setEmpno(String empno) {
this.empno = empno;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
4.编写消费者类
@Component
public class MessageConsumer {
//@RabbitListener注解用于声明式定义消息接受的队列与exhcange绑定的信息
//在SpringBoot中,消费者这端使用注解获取消息
@RabbitListener(
bindings = @QueueBinding(
value = @Queue(value="springboot-queue" , durable="true"),
exchange = @Exchange(value = "springboot-exchange" , durable = "true" , type = "topic") ,
key = "#"
)
)
//用于接收消息的方法
@RabbitHandler //通知SpringBoot下面的方法用于接收消息。
// 这个方法运行后将处于等待的状态,有新的消息进来就会自动触发下面的方法处理消息
//@Payload 代表运行时将消息反序列化后注入到后面的参数中
public void handleMessage(@Payload Employee employee , Channel channel ,
@Headers Map<String,Object> headers) {
System.out.println("=========================================");
System.out.println("接收到" + employee.getEmpno() + ":" + employee.getName());
//所有消息处理后必须进行消息的ack,channel.basicAck()
Long tag = (Long)headers.get(AmqpHeaders.DELIVERY_TAG);
try {
channel.basicAck(tag , false);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("=========================================");
}
}
七.Spring Boot与RabbitMQ集群整合
只需要ip地址改成harproxy的ip,端口5672