涉县移动网站建设报价,网站建设开发设计营销公司厦门,公司做个网页要多少钱,360免费wifi频繁掉线RabbitMQ实战学习 文章目录 RabbitMQ实战学习RabbitMQ常用资料1、安装教程2、使用安装包3、常用命令4、验证访问5、代码示例 一、RabbitMQ基本概念1.1. MQ概述1.2 MQ 的优势和劣势1.3 MQ 的优势1. 应用解耦2. 异步提速3. 削峰填谷 1.4 MQ 的劣势1.5 RabbitMQ 基础架构1.6 JMS 二…RabbitMQ实战学习 文章目录 RabbitMQ实战学习RabbitMQ常用资料1、安装教程2、使用安装包3、常用命令4、验证访问5、代码示例 一、RabbitMQ基本概念1.1. MQ概述1.2 MQ 的优势和劣势1.3 MQ 的优势1. 应用解耦2. 异步提速3. 削峰填谷 1.4 MQ 的劣势1.5 RabbitMQ 基础架构1.6 JMS 二、RabbitMQ学习1、五种工作模式1.1 简单模式1.2 work queues模式1.3 Pub/Sub 订阅模式1.4 Routing 路由模式1.5 Topics 通配符模式 2、工作模式总结3、SpringBoot 整合RabbitMQ3.1 生产者3.2 消费者3.3 小结 三、常见问题1、Erlang和RabbitMQ版本不匹配2、找不到ERLANG_HOME3、RabbitMQ和Erlang的版本对应关系 RabbitMQ常用资料
1、安装教程
https://blog.csdn.net/l1090739767/article/details/116302051
2、使用安装包
rabbitmq-server
https://repo.huaweicloud.com/rabbitmq-server/v3.12.1/
erlang
https://erlang.org/download/
3、常用命令
rabbitmq-service.bat start # 启动服务
service rabbitmq-server start
service rabbitmq-server stop # 停止服务
service rabbitmq-server restart # 重启服务如果环境变量不生效可以进行强制设置
#设置ERLANG_HOME的路径地址
set ERLANG_HOMEE:\app\MQ\Erlang OTP4、验证访问
http://localhost:15672/
5、代码示例
码云仓库
https://gitee.com/zhang_w_b/rabbit-mq
1RabbitMQ五种工作模式 rabbitmq-producer rabbitmq-consumer 2SpringBoot 整合RabbitMQ rabbitmq-producer rabbitmq-consumer 一、RabbitMQ基本概念
1.1. MQ概述
MQ全称 Message Queue消息队列是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。 小结
MQ消息队列存储消息的中间件分布式系统通信两种方式直接远程调用 和 借助第三方 完成间接通信发送方称为生产者接收方称为消费者
1.2 MQ 的优势和劣势
优势
应用解耦异步提速削峰填古
劣势
系统可用性降低系统复杂性提高一致性问题
1.3 MQ 的优势
1. 应用解耦
没有使用MQ - 系统的耦合性越高容错性就越低可维护性就越低。
使用MQ
使用 MQ 使得应用间解耦提升容错性和可维护性。
2. 异步提速
没有使用MQ
一个下单操作耗时20 300 300 300 920ms用户点击完下单按钮后需要等待920ms才能得到下单响应太慢
使用MQ
用户点击完下单按钮后只需等待25ms就能得到下单响应 (20 5 25ms)。提升用户体验和系统吞吐量单位时间内处理请求的数目。
3. 削峰填谷
没有使用MQ 使用MQ 使用了 MQ 之后限制消费消息的速度为1000这样一来高峰期产生的数据势必会被积压在 MQ 中高峰就被“削”掉了但是因为消息积压在高峰期过后的一段时间内消费消息的速度还是会维持在1000直到消费完积压的消息这就叫做填谷。使用MQ后可以提高系统稳定性。
小结
应用解耦提高系统容错性和可维护性异步提速提升用户体验和系统吞吐量削峰填谷提高系统稳定性
1.4 MQ 的劣势 系统可用性降低
系统引入的外部依赖越多系统稳定性越差。一旦 MQ 宕机就会对业务造成影响。如何保证MQ的高可用
系统复杂度提高
MQ 的加入大大增加了系统的复杂度以前系统间是同步的远程调用现在是通过 MQ 进行异步调用。如何保证消息没有被重复消费怎么处理消息丢失情况那么保证消息传递的顺序性
一致性问题
A 系统处理完业务通过 MQ 给B、C、D三个系统发消息数据如果 B 系统、C 系统处理成功D 系统处理失败。如何保证消息数据处理的一致性
小结
既然 MQ 有优势也有劣势那么使用 MQ 需要满足什么条件呢
生产者不需要从消费者处获得反馈。引入消息队列之前的直接调用其接口的返回值应该为空这才让明明下层的动作还没做上层却当成动作做完了继续往后走即所谓异步成为了可能。容许短暂的不一致性。确实是用了有效果。即解耦、提速、削峰这些方面的收益超过加入MQ管理MQ这些成本。
1.5 RabbitMQ 基础架构 RabbitMQ 中的相关概念
Broker接收和分发消息的应用RabbitMQ Server就是 Message BrokerVirtual host出于多租户和安全因素设计的把 AMQP 的基本组件划分到一个虚拟的分组中类似于网络中的 namespace概念。当多个不同的用户使用同一个 RabbitMQ server 提供的服务时可以划分出多个vhost每个用户在自己的 vhost创建 exchangequeue 等Connectionpublisherconsumer 和 broker 之间的 TCP 连接Channel如果每一次访问 RabbitMQ 都建立一个 Connection在消息量大的时候建立 TCP Connection的开销将是巨大的效率也较低。Channel 是在 connection内部建立的逻辑连接如果应用程序支持多线程通常每个thread创建单独的 channel 进行通讯AMQP method包含了channel id 帮助客户端和message broker 识别 channel所以 channel之间是完全隔离的。Channel 作为轻量级的 Connection 极大减少了操作系统建立 TCP connection 的开销Exchangemessage 到达 broker 的第一站**根据分发规则匹配查询表中的 routing key分发消息到queue中去。**常用的类型有direct (point-to-point), topic (publish-subscribe) and fanout (multicast)Queue消息最终被送到这里等待 consumer 取走Bindingexchange 和 queue 之间的虚拟连接binding 中可以包含 routing key。Binding 信息被保存到 exchange 中的查询表中用于 message 的分发依据
RabbitMQ 提供了 6 种工作模式简单模式、work queues、Publish/Subscribe 发布与订阅模式、Routing 路由模式、Topics 主题模式、RPC 远程调用模式远程调用不太算 MQ暂不作介绍。
1.6 JMS
JMS 即 Java 消息服务JavaMessage Service应用程序接口是一个 Java 平台中关于面向消息中间件的APIJMS 是 JavaEE 规范中的一种类比JDBC很多消息中间件都实现了JMS规范例如ActiveMQ。RabbitMQ 官方没有提供 JMS 的实现包但是开源社区有
小结
RabbitMQ 是基于 AMQP 协议使用 Erlang 语言开发的一款消息队列产品。RabbitMQ提供了6种工作模式我们学习5种。这是今天的重点。AMQP 是协议类比HTTP。JMS 是 API 规范接口类比 JDBC。
二、RabbitMQ学习
1、五种工作模式
简单模式、work queues、Publish/Subscribe 发布与订阅模式、Routing 路由模式、Topics 主题模式、RPC 远程调用模式
1.1 简单模式
上述的入门案例中其实使用的是如下的简单模式 在上图的模型中有以下概念
P生产者也就是要发送消息的程序C消费者消息的接收者会一直等待消息到来queue消息队列图中红色部分。类似一个邮箱可以缓存消息生产者向其中投递消息消费者从其中取出消息
代码示例
依赖 dependencygroupIdcom.rabbitmq/groupIdartifactIdamqp-client/artifactIdversion5.6.0/version/dependency生产者
package com.zwb.producer;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_HelloWorld {public static void main(String[] args) throws IOException, TimeoutException {//1、创建连接工厂ConnectionFactory factory new ConnectionFactory();//2、设置参数//factory.setHost(192.168.1.100); //默认localhostfactory.setPort(5672); //默认5672factory.setVirtualHost(/itcast); //设置RabbitMQ服务器中的虚拟机类似mysql数据库中的某一个库factory.setUsername(heima);factory.setPassword(heima);//3、创建连接ConnectionConnection connection factory.newConnection();//4、创建ChannelChannel channel connection.createChannel();//5.创建队列Queue 暂时不用交换机/*String queue, boolean durable, boolean exclusive, boolean autoDelete,MapString, Object arguments)1.queue 队列名称2.durable 是否持久化当mq重启之后还在3.exclusive*是否独占只能有一个消费者监听队列*当connection关闭时是否删除队列4.autoDelete: 是否自动删除当没有consumer时自动删除掉5.arguments: 参数*///如果没有一个名字为hello_world的队列则会创建队列如果有则不会创建channel.queueDeclare(hello_world,true,false,false,null);//6.发送消息/*String exchange, String routingKey, boolean mandatory, byte[] body参数1. exchange交换机名称简单模式下交换机会使用默认的 2. routingKey: 路由名称3. props: 配置信息4. body: 发送消息数据*/String bodyhello rabbitmq----测试内容222;channel.basicPublish(,hello_world,null,body.getBytes());//7、释放资源/*channel.close();connection.close();*/}
}消费者
package com.zwb.consumer;import com.rabbitmq.client.*;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Consumer_HelloWorld {public static void main(String[] args) throws IOException, TimeoutException {//1、创建工厂ConnectionFactory factory new ConnectionFactory();//2、设置参数//factory.setHost(192.168.1.100); //默认localhostfactory.setPort(5672);//默认5672factory.setVirtualHost(/itcast);// 默认/factory.setUsername(heima);// heimafactory.setPassword(heima);// heima//3、创建连接ConnectionConnection connection factory.newConnection();//4、创建ChannelChannel channel connection.createChannel();//5、创建队列Queue 暂时不用交换机/*String queue, boolean durable, boolean exclusive, boolean autoDelete,MapString, Object arguments)1.queue 队列名称2.durable 是否持久化当mq重启之后还在3.exclusive*是否独占只能有一个消费者监听队列*当connection关闭时是否删除队列4.autoDelete: 是否自动删除当没有consumer时自动删除掉5.arguments: 参数*///如果没有一个名字为hello_world的队列则会创建队列如果有则不会创建channel.queueDeclare(hello_world,true,false,false,null);//6、接收消息/*basicConsume(String queue, boolean autoAck, Consumer callback)参数1. queue队列名称2. autoAck: 是否自动确认3. callback: 回调对象*/Consumer consumer new DefaultConsumer(channel) {/*回调方法当收到消息后会自动执行该方法1.consumerTag标识2.envelope获取一些信息。交换机路由key...3.properties: 配置信息4.body: 数据*/public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {System.out.println(consumerTag:consumerTag);System.out.println(exchange:envelope.getExchange());System.out.println(RoutingKey:envelope.getRoutingKey());System.out.println(properties:properties);System.out.println(body:new String(body));//byte数组转string字符串}};channel.basicConsume(hello_world,true,consumer);//关闭资源不要因为消费者要监听}
}1.2 work queues模式
1模式说明 Work Queues与入门程序的简单模式相比多了一个或一些消费端多个消费端共同消费同一个队列中的消息。队列存在竞争关系可以理解为抢红包应用场景对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
2代码编写
Work Queues 与入门程序的简单模式的代码几乎是一样的。可以完全复制并多复制一个消费者进行多个消费者同时对消费消息的测试。
Producer_WorkQueues
package com.itheima.producer;
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_WorkQueues {public static void main(String[] args) throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factorynew ConnectionFactory();//2.设置参数factory.setHost(192.168.101.22);//ip地址 默认localhostfactory.setPort(5672);//端口 默认值5672factory.setVirtualHost(/itcast);//虚拟机默认值 /虚拟机factory.setUsername(heima);//用户名 默认guestfactory.setPassword(heima);//密码 默认guest//3.创建连接 connectionConnection connection factory.newConnection();//4.创建channelChannel channel connection.createChannel();//5.创建队列Queue 暂时不用交换机/*String queue, boolean durable, boolean exclusive, boolean autoDelete,MapString, Object arguments)1.queue 队列名称2.durable 是否持久化当mq重启之后还在3.exclusive*是否独占只能有一个消费者监听队列*当connection关闭时是否删除队列4.autoDelete: 是否自动删除当没有consumer时自动删除掉5.arguments: 参数*///如果没有一个名字为hello_world的队列则会创建队列如果有则不会创建channel.queueDeclare(work_queues,true,false,false,null);//6.发送消息/*String exchange, String routingKey, boolean mandatory, byte[] body参数1. exchange交换机名称简单模式下交换机会使用默认的 2. routingKey: 路由名称3. props: 配置信息4. body: 发送消息数据*/for (int i 1; i 10; i) {String bodyihello rabbitmq----;channel.basicPublish(,work_queues,null,body.getBytes());}//7.释放资源/*channel.close();connection.close();*/}
}Consumer_WorkQueues1
package com.itheima.consumer;import com.rabbitmq.client.*;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Consumer_WorkQueues1 {public static void main(String[] args) throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factorynew ConnectionFactory();//2.设置参数factory.setHost(192.168.101.22);//ip地址 默认localhostfactory.setPort(5672);//端口 默认值5672factory.setVirtualHost(/itcast);//虚拟机默认值 /虚拟机factory.setUsername(heima);//用户名 默认guestfactory.setPassword(heima);//密码 默认guest//3.创建连接 connectionConnection connection factory.newConnection();//4.创建channelChannel channel connection.createChannel();//5.创建队列Queue 暂时不用交换机/*String queue, boolean durable, boolean exclusive, boolean autoDelete,MapString, Object arguments)1.queue 队列名称2.durable 是否持久化当mq重启之后还在3.exclusive*是否独占只能有一个消费者监听队列*当connection关闭时是否删除队列4.autoDelete: 是否自动删除当没有consumer时自动删除掉5.arguments: 参数*///如果没有一个名字为hello_world的队列则会创建队列如果有则不会创建channel.queueDeclare(work_queues,true,false,false,null);//6.接收消息/*basicConsume(String queue, boolean autoAck, Consumer callback)参数1. queue队列名称2. autoAck: 是否自动确认3. callback: 回调对象*/Consumer consumernew DefaultConsumer(channel){/*回调方法当收到消息后会自动执行该方法1.consumerTag标识2.envelope获取一些信息。交换机路由key...3.properties: 配置信息4.body: 数据*/public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {/* System.out.println(consumerTag:consumerTag);System.out.println(exchange:envelope.getExchange());System.out.println(RoutingKey:envelope.getRoutingKey());System.out.println(properties:properties);*/System.out.println(body:new String(body));//byte数组转string字符串}};channel.basicConsume(work_queues,true,consumer);//关闭资源不要因为消费者要监听}
}Consumer_WorkQueues2
package com.itheima.consumer;import com.rabbitmq.client.*;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Consumer_WorkQueues2 {public static void main(String[] args) throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factorynew ConnectionFactory();//2.设置参数factory.setHost(192.168.101.22);//ip地址 默认localhostfactory.setPort(5672);//端口 默认值5672factory.setVirtualHost(/itcast);//虚拟机默认值 /虚拟机factory.setUsername(heima);//用户名 默认guestfactory.setPassword(heima);//密码 默认guest//3.创建连接 connectionConnection connection factory.newConnection();//4.创建channelChannel channel connection.createChannel();//5.创建队列Queue 暂时不用交换机/*String queue, boolean durable, boolean exclusive, boolean autoDelete,MapString, Object arguments)1.queue 队列名称2.durable 是否持久化当mq重启之后还在3.exclusive*是否独占只能有一个消费者监听队列*当connection关闭时是否删除队列4.autoDelete: 是否自动删除当没有consumer时自动删除掉5.arguments: 参数*///如果没有一个名字为hello_world的队列则会创建队列如果有则不会创建channel.queueDeclare(work_queues,true,false,false,null);//6.接收消息/*basicConsume(String queue, boolean autoAck, Consumer callback)参数1. queue队列名称2. autoAck: 是否自动确认3. callback: 回调对象*/Consumer consumernew DefaultConsumer(channel){/*回调方法当收到消息后会自动执行该方法1.consumerTag标识2.envelope获取一些信息。交换机路由key...3.properties: 配置信息4.body: 数据*/public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {/* System.out.println(consumerTag:consumerTag);System.out.println(exchange:envelope.getExchange());System.out.println(RoutingKey:envelope.getRoutingKey());System.out.println(properties:properties);*/System.out.println(body:new String(body));//byte数组转string字符串}};channel.basicConsume(work_queues,true,consumer);//关闭资源不要因为消费者要监听}
}3测试
注意先运行消费者1和消费者2再运行生产者
结果如下消费者1消费消息序号 1,3,5,7,9 消费者2消费消息序号 2,4,6,8,10 4 小结
在一个队列中如果有多个消费者那么消费者之间对于同一个消息的关系是竞争的关系。Work Queues 对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。例如短信服务部署多个只需要有一个节点成功发送即可。
1.3 Pub/Sub 订阅模式
1模式说明 在订阅模型中多了一个 Exchange 角色而且过程略有变化 P生产者也就是要发送消息的程序但是不再发送到队列中而是发给X交换机 C消费者消息的接收者会一直等待消息到来 Queue消息队列接收消息、缓存消息 Exchange交换机X。一方面 接收生产者发送的消息 。另一方面 知道如何处理消息 例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作取决于Exchange的类型。Exchange有常见以下3种类型 Fanout广播将消息交给所有绑定到交换机的队列Direct定向把消息交给符合指定routing key 的队列Topic通配符把消息交给符合routing pattern路由模式 的队列
Exchange交换机只负责转发消息不具备存储消息的能力因此如果没有任何队列与 Exchange 绑定或者没有符合路由规则的队列那么消息会丢失
2代码编写
生产者Producer_PubSub
package com.itheima.producer;
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 factorynew ConnectionFactory();//2.设置参数factory.setHost(192.168.101.22);//ip地址 默认localhostfactory.setPort(5672);//端口 默认值5672factory.setVirtualHost(/itcast);//虚拟机默认值 /虚拟机factory.setUsername(heima);//用户名 默认guestfactory.setPassword(heima);//密码 默认guest//3.创建连接 connectionConnection connection factory.newConnection();//4.创建channelChannel channel connection.createChannel();//5.创建交换机/*exchangeDeclare(String exchange, 交换机名称BuiltinExchangeType type, 交换机类型 枚举类型DIRECT(direct), 定向FANOUT(fanout), 扇形广播发送消息到每一个与之绑定的队列TOPIC(topic),通配符方式HEADERS(headers);参数匹配boolean durable, 是否持久化boolean autoDelete, 是否自动删除boolean internal, 内部使用一般falseMapString, Object arguments) 参数列表*/String exchangeNametest_fanout;channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT,true,false,false,null);//6.创建两个队列String queue1Nametest_fanout_queue1;String queue2Nametest_fanout_queue2;/*String queue, boolean durable, boolean exclusive, boolean autoDelete,MapString, Object arguments)1.queue 队列名称2.durable 是否持久化当mq重启之后还在3.exclusive*是否独占只能有一个消费者监听队列*当connection关闭时是否删除队列4.autoDelete: 是否自动删除当没有consumer时自动删除掉5.arguments: 参数*/channel.queueDeclare(queue1Name,true,false,false,null);channel.queueDeclare(queue2Name,true,false,false,null);//7绑定队列和交换机/*queueBind(String queue, String exchange, String routingKey)参数1.queue: 队列名称2.exchange交换机3.routingKey路由键绑定规则如果交换机的类型为fanoutroutingKey设置为*/channel.queueBind(queue1Name,exchangeName,);channel.queueBind(queue2Name,exchangeName,);//8.发送消息String body日志信息张三调用了findAll方法...日志级别info;channel.basicPublish(exchangeName,,null,body.getBytes());//9.释放资源channel.close();connection.close();}
}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576Consumer_PubSub1
package com.itheima.consumer;
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 factorynew ConnectionFactory();//2.设置参数factory.setHost(192.168.101.22);//ip地址 默认localhostfactory.setPort(5672);//端口 默认值5672factory.setVirtualHost(/itcast);//虚拟机默认值 /虚拟机factory.setUsername(heima);//用户名 默认guestfactory.setPassword(heima);//密码 默认guest//3.创建连接 connectionConnection connection factory.newConnection();//4.创建channelChannel channel connection.createChannel();//5.队列已经声明一次了这里不用再声明队列String queue1Nametest_fanout_queue1;String queue2Nametest_fanout_queue2;//6.接收消息/*basicConsume(String queue, boolean autoAck, Consumer callback)参数1. queue队列名称2. autoAck: 是否自动确认3. callback: 回调对象*/Consumer consumernew DefaultConsumer(channel){/*回调方法当收到消息后会自动执行该方法1.consumerTag标识2.envelope获取一些信息。交换机路由key...3.properties: 配置信息4.body: 数据*/public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {/* System.out.println(consumerTag:consumerTag);System.out.println(exchange:envelope.getExchange());System.out.println(RoutingKey:envelope.getRoutingKey());System.out.println(properties:properties);*/System.out.println(body:new String(body));//byte数组转string字符串System.out.println(将日志信息打印到控制台);}};channel.basicConsume(queue1Name,true,consumer);//关闭资源不要因为消费者要监听}
}
Consumer_PubSub2
package com.itheima.consumer;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Consumer_PubSub2 {public static void main(String[] args) throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factorynew ConnectionFactory();//2.设置参数factory.setHost(192.168.101.22);//ip地址 默认localhostfactory.setPort(5672);//端口 默认值5672factory.setVirtualHost(/itcast);//虚拟机默认值 /虚拟机factory.setUsername(heima);//用户名 默认guestfactory.setPassword(heima);//密码 默认guest//3.创建连接 connectionConnection connection factory.newConnection();//4.创建channelChannel channel connection.createChannel();//5.队列已经声明一次了这里不用再声明队列String queue1Nametest_fanout_queue1;String queue2Nametest_fanout_queue2;//6.接收消息/*basicConsume(String queue, boolean autoAck, Consumer callback)参数1. queue队列名称2. autoAck: 是否自动确认3. callback: 回调对象*/Consumer consumernew DefaultConsumer(channel){/*回调方法当收到消息后会自动执行该方法1.consumerTag标识2.envelope获取一些信息。交换机路由key...3.properties: 配置信息4.body: 数据*/public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {/* System.out.println(consumerTag:consumerTag);System.out.println(exchange:envelope.getExchange());System.out.println(RoutingKey:envelope.getRoutingKey());System.out.println(properties:properties);*/System.out.println(body:new String(body));//byte数组转string字符串System.out.println(将日志信息保存数据库);}};channel.basicConsume(queue2Name,true,consumer);//关闭资源不要因为消费者要监听}
}运行结果 3小结 交换机需要与队列进行绑定绑定之后一个消息可以被多个消费者都收到。 发布订阅模式与工作队列模式的区别 工作队列模式不用定义交换机而发布/订阅模式需要定义交换机 发布/订阅模式的生产方是面向交换机发送消息工作队列模式的生产方是面向队列发送消息(底层使用默认交换机) 发布/订阅模式需要设置队列和交换机的绑定工作队列模式不需要设置实际上工作队列模式会将队列绑 定到默认的交换机
1.4 Routing 路由模式
1模式说明
队列与交换机的绑定不能是任意绑定了而是要指定一个 RoutingKey路由key消息的发送方在向 Exchange 发送消息时也必须指定消息的 RoutingKeyExchange 不再把消息交给每一个绑定的队列而是根据消息的 Routing Key 进行判断只有队列的Routingkey 与消息的 Routing key 完全一致才会接收到消息
图解 P生产者向 Exchange 发送消息发送消息时会指定一个routing keyXExchange交换机接收生产者的消息然后把消息递交给与 routing key 完全匹配的队列C1消费者其所在队列指定了需要 routing key 为 error 的消息C2消费者其所在队列指定了需要 routing key 为 info、error、warning 的消息
2代码编写
Producer_Routing
package com.zwb.producer.routing;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 factory new ConnectionFactory();//2、设置参数//factory.setHost(192.168.1.100); //默认localhostfactory.setPort(5672); //默认5672factory.setVirtualHost(/itcast); //设置RabbitMQ服务器中的虚拟机类似mysql数据库中的某一个库factory.setUsername(heima);factory.setPassword(heima);//3、创建连接ConnectionConnection connection factory.newConnection();//4、创建ChannelChannel channel connection.createChannel();//5、创建交换机/*exchangeDeclare(String exchange, 交换机名称BuiltinExchangeType type, 交换机类型 枚举类型DIRECT(direct), 定向FANOUT(fanout), 扇形广播发送消息到每一个与之绑定的队列TOPIC(topic),通配符方式HEADERS(headers);参数匹配boolean durable, 是否持久化boolean autoDelete, 是否自动删除boolean internal, 内部使用一般falseMapString, Object arguments) 参数列表*/String exchangeName test_direct;channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT,true,false,null);//6、创建两个队列String queue1Nametest_direct_queue1;String queue2Nametest_direct_queue2;/*String queue, boolean durable, boolean exclusive, boolean autoDelete,MapString, Object arguments)1.queue 队列名称2.durable 是否持久化当mq重启之后还在3.exclusive*是否独占只能有一个消费者监听队列*当connection关闭时是否删除队列4.autoDelete: 是否自动删除当没有consumer时自动删除掉5.arguments: 参数*/channel.queueDeclare(queue1Name,true,false,false,null);channel.queueDeclare(queue2Name,true,false,false,null);//7、绑定队列和交换机/*queueBind(String queue, String exchange, String routingKey)参数1.queue: 队列名称2.exchange交换机3.routingKey路由键绑定规则如果交换机的类型为fanoutroutingKey设置为*///队列1channel.queueBind(queue1Name,exchangeName,error);//队列2channel.queueBind(queue2Name,exchangeName,info);channel.queueBind(queue2Name,exchangeName,warning);channel.queueBind(queue2Name,exchangeName,error);//8.发送消息String body日志信息张三调用了delete方法出错误了...日志级别error;channel.basicPublish(exchangeName,error,null,body.getBytes());//9.释放资源channel.close();connection.close();}
}Consumer_Routing1
package com.zwb.consumer.routing;import com.rabbitmq.client.*;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Consumer_Routing1 {public static void main(String[] args) throws IOException, TimeoutException {//1、创建工厂ConnectionFactory factory new ConnectionFactory();//2、设置参数//factory.setHost(192.168.1.100); //默认localhostfactory.setPort(5672);//默认5672factory.setVirtualHost(/itcast);// 默认/factory.setUsername(heima);// heimafactory.setPassword(heima);// heima//3、创建连接ConnectionConnection connection factory.newConnection();//4、创建ChannelChannel channel connection.createChannel();//5.队列已经声明一次了这里不用再声明队列String queue1Nametest_direct_queue1;String queue2Nametest_direct_queue2;//6.接收消息/*basicConsume(String queue, boolean autoAck, Consumer callback)参数1. queue队列名称2. autoAck: 是否自动确认3. callback: 回调对象*/Consumer consumernew DefaultConsumer(channel){/*回调方法当收到消息后会自动执行该方法1.consumerTag标识2.envelope获取一些信息。交换机路由key...3.properties: 配置信息4.body: 数据*/public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {/* System.out.println(consumerTag:consumerTag);System.out.println(exchange:envelope.getExchange());System.out.println(RoutingKey:envelope.getRoutingKey());System.out.println(properties:properties);*/System.out.println(body:new String(body));//byte数组转string字符串System.out.println(将日志信息打印到控制台);}};channel.basicConsume(queue2Name,true,consumer);//关闭资源不要因为消费者要监听}
}Consumer_Routing2
package com.zwb.consumer.routing;import com.rabbitmq.client.*;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Consumer_Routing2 {public static void main(String[] args) throws IOException, TimeoutException {//1、创建工厂ConnectionFactory factory new ConnectionFactory();//2、设置参数//factory.setHost(192.168.1.100); //默认localhostfactory.setPort(5672);//默认5672factory.setVirtualHost(/itcast);// 默认/factory.setUsername(heima);// heimafactory.setPassword(heima);// heima//3、创建连接ConnectionConnection connection factory.newConnection();//4、创建ChannelChannel channel connection.createChannel();//5.队列已经声明一次了这里不用再声明队列String queue1Nametest_direct_queue1;String queue2Nametest_direct_queue2;//6.接收消息/*basicConsume(String queue, boolean autoAck, Consumer callback)参数1. queue队列名称2. autoAck: 是否自动确认3. callback: 回调对象*/Consumer consumernew DefaultConsumer(channel){/*回调方法当收到消息后会自动执行该方法1.consumerTag标识2.envelope获取一些信息。交换机路由key...3.properties: 配置信息4.body: 数据*/public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {/* System.out.println(consumerTag:consumerTag);System.out.println(exchange:envelope.getExchange());System.out.println(RoutingKey:envelope.getRoutingKey());System.out.println(properties:properties);*/System.out.println(body:new String(body));//byte数组转string字符串System.out.println(将日志信息打印到控制台);}};channel.basicConsume(queue1Name,true,consumer);//关闭资源不要因为消费者要监听}}(3) 运行结果 小结
Routing 模式要求队列在绑定交换机时要指定 routing key消息会转发到符合 routing key 的队列
1.5 Topics 通配符模式
1模式说明
Topic 类型与 Direct 相比都是可以根据 RoutingKey 把消息路由到不同的队列。只不过 Topic 类型Exchange 可以让队列在绑定 Routing key 的时候使用通配符Routingkey 一般都是有一个或多个单词组成多个单词之间以”.”分割例如 item.insert通配符规则# 匹配一个或多个词* 匹配不多不少恰好1个词例如item.# 能够匹配 item.insert.abc 或者item.insertitem.* 只能匹配 item.insert 图解
红色 Queue绑定的是 usa.# 因此凡是以 usa. 开头的 routing key 都会被匹配到黄色 Queue绑定的是 #.news 因此凡是以 .news 结尾的 routing key 都会被匹配*代表一个单词#代表0个或者多个单词
2代码实现
Producer_Topics
package com.zwb.producer.topics;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_Topics {public static void main(String[] args) throws IOException, TimeoutException {//1、创建连接工厂ConnectionFactory factory new ConnectionFactory();//2、设置参数//factory.setHost(192.168.1.100); //默认localhostfactory.setPort(5672); //默认5672factory.setVirtualHost(/itcast); //设置RabbitMQ服务器中的虚拟机类似mysql数据库中的某一个库factory.setUsername(heima);factory.setPassword(heima);//3、创建连接ConnectionConnection connection factory.newConnection();//4、创建ChannelChannel channel connection.createChannel();//5、创建交换机/*exchangeDeclare(String exchange, 交换机名称BuiltinExchangeType type, 交换机类型 枚举类型DIRECT(direct), 定向FANOUT(fanout), 扇形广播发送消息到每一个与之绑定的队列TOPIC(topic),通配符方式HEADERS(headers);参数匹配boolean durable, 是否持久化boolean autoDelete, 是否自动删除boolean internal, 内部使用一般falseMapString, Object arguments) 参数列表*/String exchangeName test_topic;channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC,true,false,null);//6、创建两个队列String queue1Nametest_topic_queue1;String queue2Nametest_topic_queue2;/*String queue, boolean durable, boolean exclusive, boolean autoDelete,MapString, Object arguments)1.queue 队列名称2.durable 是否持久化当mq重启之后还在3.exclusive*是否独占只能有一个消费者监听队列*当connection关闭时是否删除队列4.autoDelete: 是否自动删除当没有consumer时自动删除掉5.arguments: 参数*/channel.queueDeclare(queue1Name,true,false,false,null);channel.queueDeclare(queue2Name,true,false,false,null);//7、绑定队列和交换机/*queueBind(String queue, String exchange, String routingKey)参数1.queue: 队列名称2.exchange交换机3.routingKey路由键绑定规则如果交换机的类型为fanoutroutingKey设置为*///routing key系统的名称日志级别//--需求所有error级别的日志存入数据库所有order系统的日志存入数据库channel.queueBind(queue1Name,exchangeName,#.error);channel.queueBind(queue1Name,exchangeName,order.*);channel.queueBind(queue2Name,exchangeName,*.*);//8.发送消息String body日志信息张三调用了findAll方法...日志级别info;channel.basicPublish(exchangeName,order.info,null,body.getBytes());//9.释放资源channel.close();connection.close();}
}Consumer_Topic1
package com.zwb.consumer.topics;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 factory new ConnectionFactory();//2、设置参数//factory.setHost(192.168.1.100); //默认localhostfactory.setPort(5672);//默认5672factory.setVirtualHost(/itcast);// 默认/factory.setUsername(heima);// heimafactory.setPassword(heima);// heima//3、创建连接ConnectionConnection connection factory.newConnection();//4、创建ChannelChannel channel connection.createChannel();//5.队列已经声明一次了这里不用再声明队列String queue1Nametest_topic_queue1;String queue2Nametest_topic_queue2;//6.接收消息/*basicConsume(String queue, boolean autoAck, Consumer callback)参数1. queue队列名称2. autoAck: 是否自动确认3. callback: 回调对象*/Consumer consumernew DefaultConsumer(channel){/*回调方法当收到消息后会自动执行该方法1.consumerTag标识2.envelope获取一些信息。交换机路由key...3.properties: 配置信息4.body: 数据*/public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {/* System.out.println(consumerTag:consumerTag);System.out.println(exchange:envelope.getExchange());System.out.println(RoutingKey:envelope.getRoutingKey());System.out.println(properties:properties);*/System.out.println(body:new String(body));//byte数组转string字符串System.out.println(将日志信息打印到控制台);}};channel.basicConsume(queue2Name,true,consumer);//关闭资源不要因为消费者要监听}}Consumer_Topic2
package com.zwb.consumer.topics;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 factory new ConnectionFactory();//2、设置参数//factory.setHost(192.168.1.100); //默认localhostfactory.setPort(5672);//默认5672factory.setVirtualHost(/itcast);// 默认/factory.setUsername(heima);// heimafactory.setPassword(heima);// heima//3、创建连接ConnectionConnection connection factory.newConnection();//4、创建ChannelChannel channel connection.createChannel();//5.队列已经声明一次了这里不用再声明队列String queue1Nametest_topic_queue1;String queue2Nametest_topic_queue2;//6.接收消息/*basicConsume(String queue, boolean autoAck, Consumer callback)参数1. queue队列名称2. autoAck: 是否自动确认3. callback: 回调对象*/Consumer consumernew DefaultConsumer(channel){/*回调方法当收到消息后会自动执行该方法1.consumerTag标识2.envelope获取一些信息。交换机路由key...3.properties: 配置信息4.body: 数据*/public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {/* System.out.println(consumerTag:consumerTag);System.out.println(exchange:envelope.getExchange());System.out.println(RoutingKey:envelope.getRoutingKey());System.out.println(properties:properties);*/System.out.println(body:new String(body));//byte数组转string字符串System.out.println(将日志信息打印到控制台);}};channel.basicConsume(queue1Name,true,consumer);//关闭资源不要因为消费者要监听}
}结果 小结
Topic 主题模式可以实现 Pub/Sub 发布与订阅模式和 Routing 路由模式的功能只是 Topic 在配置routing key 的时候可以使用通配符显得更加灵活。
2、工作模式总结 3、SpringBoot 整合RabbitMQ
3.1 生产者 创建生产者SpringBoot工程 引入start依赖坐标 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-amqp/artifactId
/dependency编写yml配置基本信息配置 定义交换机队列以及绑定关系的配置类 注入RabbitTemplate调用方法完成消息发送 pom.xml parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.5.15/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.rabiitmq/groupIdartifactIdproducer-springboot/artifactIdversion0.0.1-SNAPSHOT/versionnameproducer-springboot/namedescriptionproducer-springboot/descriptionpropertiesjava.version8/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-amqp/artifactId/dependencydependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.13.1/versionscopetest/scope/dependency/dependenciesRabbitMQConfig.java 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 RabbitMQConfig {public static final String EXCHANGE_NAMEboot_topic_exchange;public static final String QUEUE_NAMEboot_quque;/** 参数说明* id: bean的名称* namequque名称* auto-declare:自动创建* auto-delete: 自动删除最后一个消费者和该队列断开连接后自动删除队列* durable:是否持久化* exclusive:是否独占*///1.交换机Bean(bootExchange)public Exchange bootExchange(){return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();}//2.队列Bean(bootQueue)public Queue bootQueue(){return QueueBuilder.durable(QUEUE_NAME).build();}//3.队列和交换机的绑定关系Beanpublic Binding binQueueExchange(Qualifier(bootQueue) Queue queue, Qualifier(bootExchange) Exchange exchange){return BindingBuilder.bind(queue).to(exchange).with(boot.#).noargs();}
} ProducerSpringbootApplication
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication
public class ProducerSpringbootApplication {public static void main(String[] args) {SpringApplication.run(ProducerSpringbootApplication.class, args);}}application.yml
# 配置rabbitmq的基本信息 IP端口 username passwordspring:
spring:rabbitmq:host: localhostport: 5672virtual-host: /itcastusername: heimapassword: heimaProducerTest
import com.rabiitmq.config.RabbitMQConfig;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;SpringBootTest
class ProducerSpringbootApplicationTests {//1.注入rabbitTemplateAutowiredprivate RabbitTemplate rabbitTemplate;Testpublic void testSend(){rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME,boot.haha,这是springboot-rabbitmq的测试消息22);}
}测试运行Junit Test 3.2 消费者 创建消费者SpringBoot工程 引入start依赖坐标 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-amqp/artifactId
/dependency编写yml配置基本信息配置 定义监听类使用RabbitListener注解完成队列监听。 ConsumerSpringbootApplication
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication
public class ConsumerSpringbootApplication {public static void main(String[] args) {SpringApplication.run(ConsumerSpringbootApplication.class, args);}}RabbitMQListener.java
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;Component
public class RabbitMQListener {RabbitListener(queues boot_quque)public void ListenerQueue(Message message){System.out.println(new String(message.getBody()));}
}application.yml
# 配置rabbitmq的基本信息 IP端口 username passwordspring:
spring:rabbitmq:host: localhostport: 5672virtual-host: /itcastusername: heimapassword: heimapom.xml
parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.5.15/versionrelativePath/ !-- lookup parent from repository --
/parent
groupIdcom.rabbitmq/groupId
artifactIdconsumer-springboot/artifactId
version0.0.1-SNAPSHOT/version
nameconsumer-springboot/name
descriptionconsumer-springboot/description
propertiesjava.version8/java.version
/properties
dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-amqp/artifactId/dependencydependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.13.1/versionscopetest/scope/dependencydependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.13.1/versionscopetest/scope/dependency
/dependencies运行ConsumerSpringbootApplication,消费者从MQ中拿到消息 3.3 小结
SpringBoot提供了快速整合RabbitMQ的方式基本信息在yml中配置队列交互机以及绑定关系在配置类中使用Bean的方式配置生产端直接注入RabbitTemplate完成消息发送消费端直接使用RabbitListener完成消息接收
三、常见问题
1、Erlang和RabbitMQ版本不匹配
https://blog.csdn.net/weixin_62319133/article/details/127925299 在安装RabbitMQ是会出现无法读取ErlangOTP的情况多数是因为版本不匹配 1.在官方文档中找到RabbitMQ版本对应的Erlang版本重新下载安装包 文档RabbitMQ Erlang Version Requirements — RabbitMQ 2.下载完成后停止Erlang和RabbitMQ服务并进入原本Erlang和RabbitMQ的安装目录运行Uninstall.exe 3.卸载完成后检查Erlang和RabbitMQ的安装目录是否删干净 4.删除RabbitMQ和Erlang的所有安装目录。 5.运行CMD–sc delete RabbitMQ 6.删除目录C:\Windows\System32\config\systemprofile中的.erlang.cookie文件如果有的话。 7.删除目录C:\Users\用户名 中的.erlang.cookie文件如果有的话。 8.删除目录C:\Users\用户名\AppData\Roaming目录下的RabbitMQ文件夹。 9.打开注册表编辑器删除表 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Ericsson\Erlang下的子项 10.以管理员运行分别运行Erlang和RabbitMQ安装包即可 2、找不到ERLANG_HOME
rabbitmq启动的时候报错Please either set ERLANG_HOME to point to your Erlang installation or place
https://blog.csdn.net/weixin_42409107/article/details/100105905
3、RabbitMQ和Erlang的版本对应关系
https://www.rabbitmq.com/which-erlang.html
文章转载自: http://www.morning.ypktc.cn.gov.cn.ypktc.cn http://www.morning.rwtlj.cn.gov.cn.rwtlj.cn http://www.morning.lmzpk.cn.gov.cn.lmzpk.cn http://www.morning.dmxzd.cn.gov.cn.dmxzd.cn http://www.morning.srhqm.cn.gov.cn.srhqm.cn http://www.morning.drfcj.cn.gov.cn.drfcj.cn http://www.morning.zlhzd.cn.gov.cn.zlhzd.cn http://www.morning.dnvhfh.cn.gov.cn.dnvhfh.cn http://www.morning.yqgbw.cn.gov.cn.yqgbw.cn http://www.morning.zqkms.cn.gov.cn.zqkms.cn http://www.morning.c7623.cn.gov.cn.c7623.cn http://www.morning.mhlsx.cn.gov.cn.mhlsx.cn http://www.morning.mxbks.cn.gov.cn.mxbks.cn http://www.morning.zwfgh.cn.gov.cn.zwfgh.cn http://www.morning.fnmtc.cn.gov.cn.fnmtc.cn http://www.morning.pwggd.cn.gov.cn.pwggd.cn http://www.morning.tkchg.cn.gov.cn.tkchg.cn http://www.morning.gxtbn.cn.gov.cn.gxtbn.cn http://www.morning.jnoegg.com.gov.cn.jnoegg.com http://www.morning.routalr.cn.gov.cn.routalr.cn http://www.morning.fnbtn.cn.gov.cn.fnbtn.cn http://www.morning.slqgl.cn.gov.cn.slqgl.cn http://www.morning.yfnhg.cn.gov.cn.yfnhg.cn http://www.morning.mtxrq.cn.gov.cn.mtxrq.cn http://www.morning.rdxnt.cn.gov.cn.rdxnt.cn http://www.morning.tcylt.cn.gov.cn.tcylt.cn http://www.morning.wlnr.cn.gov.cn.wlnr.cn http://www.morning.c7512.cn.gov.cn.c7512.cn http://www.morning.mllmm.cn.gov.cn.mllmm.cn http://www.morning.mtjwp.cn.gov.cn.mtjwp.cn http://www.morning.prxqd.cn.gov.cn.prxqd.cn http://www.morning.lchtb.cn.gov.cn.lchtb.cn http://www.morning.yktr.cn.gov.cn.yktr.cn http://www.morning.nbnpb.cn.gov.cn.nbnpb.cn http://www.morning.tphjl.cn.gov.cn.tphjl.cn http://www.morning.mjbjq.cn.gov.cn.mjbjq.cn http://www.morning.cszbj.cn.gov.cn.cszbj.cn http://www.morning.nhgkm.cn.gov.cn.nhgkm.cn http://www.morning.lfmwt.cn.gov.cn.lfmwt.cn http://www.morning.kmqlf.cn.gov.cn.kmqlf.cn http://www.morning.kjsft.cn.gov.cn.kjsft.cn http://www.morning.kynf.cn.gov.cn.kynf.cn http://www.morning.ffbp.cn.gov.cn.ffbp.cn http://www.morning.snmth.cn.gov.cn.snmth.cn http://www.morning.jrpmf.cn.gov.cn.jrpmf.cn http://www.morning.dwrjj.cn.gov.cn.dwrjj.cn http://www.morning.oioini.com.gov.cn.oioini.com http://www.morning.ytfr.cn.gov.cn.ytfr.cn http://www.morning.yrhpg.cn.gov.cn.yrhpg.cn http://www.morning.kwjyt.cn.gov.cn.kwjyt.cn http://www.morning.spxsm.cn.gov.cn.spxsm.cn http://www.morning.ktmnq.cn.gov.cn.ktmnq.cn http://www.morning.mzzqs.cn.gov.cn.mzzqs.cn http://www.morning.syfty.cn.gov.cn.syfty.cn http://www.morning.ncfky.cn.gov.cn.ncfky.cn http://www.morning.jfsbs.cn.gov.cn.jfsbs.cn http://www.morning.tmlhh.cn.gov.cn.tmlhh.cn http://www.morning.ktcfl.cn.gov.cn.ktcfl.cn http://www.morning.clybn.cn.gov.cn.clybn.cn http://www.morning.wbqk.cn.gov.cn.wbqk.cn http://www.morning.inheatherskitchen.com.gov.cn.inheatherskitchen.com http://www.morning.qcymf.cn.gov.cn.qcymf.cn http://www.morning.xkyqq.cn.gov.cn.xkyqq.cn http://www.morning.bwzzt.cn.gov.cn.bwzzt.cn http://www.morning.lynb.cn.gov.cn.lynb.cn http://www.morning.rykmz.cn.gov.cn.rykmz.cn http://www.morning.tpqrc.cn.gov.cn.tpqrc.cn http://www.morning.srbl.cn.gov.cn.srbl.cn http://www.morning.kgrwh.cn.gov.cn.kgrwh.cn http://www.morning.plqkz.cn.gov.cn.plqkz.cn http://www.morning.wdprz.cn.gov.cn.wdprz.cn http://www.morning.xfmzk.cn.gov.cn.xfmzk.cn http://www.morning.rfldz.cn.gov.cn.rfldz.cn http://www.morning.zwckz.cn.gov.cn.zwckz.cn http://www.morning.kpbgp.cn.gov.cn.kpbgp.cn http://www.morning.nbybb.cn.gov.cn.nbybb.cn http://www.morning.jxdhc.cn.gov.cn.jxdhc.cn http://www.morning.splkk.cn.gov.cn.splkk.cn http://www.morning.grpbt.cn.gov.cn.grpbt.cn http://www.morning.qfgwx.cn.gov.cn.qfgwx.cn