MQ - RabbitMQ
前言
消息队列Message Queue,是一种跨进程的通信机制,用于在系统之间进行传递消息.是一种跨进程的通信机制,用于在系统之间进行传递消息,
提示:以下是本篇文章正文内容,下面案例可供参考
一、消息队列的使用场景
应用解耦
多应用间通过消息队列对同一消息进行处理,避免调用接口失败导致整个过程失败
系统间消息传递通过MQ进行解耦,“消息队列”是在消息的传输过程中保存消息 的容器
异步处理
应用间并发处理消息,相比串行处理,减少处理时间
比如用户为了使用某个应用,进行注册,系统需要发送注册邮件,验证短信
限流削峰
广泛应用于秒杀或抢购活动中,避免流量过大导致处理请求阻塞的情况
二、RabbitMQ
RabbitMQ是支持多种消息协议,易于部署和使用的开源消息代理服务器,用于在分布式系统中存储转发消息
1.由以高性能、健壮以及可伸缩性出名的Erlang语言编写;提供了成熟的高并发,高可用的解决方案
A.可以根据实际业务情况动态地扩展集群节点
B.在集群中的机器上设置镜像,使得在部分节点出现问题的情况下仍然可用
2.支持多种客户端语言,如:Python、Ruby、.NET、Java等,支持AJAX
RabbitMQ提供了一个易用的用户界面,使得用户可以监控和管理消息、集群中的 节点等。
安装与使用
Linux安装使用
常用命令
启停服务
用户管理
命令行指令
相关概念
Queue
消息队列,提供了FIFO(先入先出)的处理机制,具有缓存消息的能力。rabbitmq中,队列消息可以设置为持久化,临时或者自动删除。
1.设置为持久化的队列,queue中的消息会在server本地硬盘存储一份,防止系统crash.
2.数据丢失设置为临时队列,queue中的数据在系统重启之后就会丢失
3.设置为自动删除的队列,当不存在用户连接到server,队列中的数据会被自动删除
队列状态
Vhost虚拟主机
虚拟主机vhost是AMQP概念的基础,RabbitMQ包含了开箱即用的 默认虚拟主机"/“
1.vhost之间是绝对隔离,保障了队列和交换机的安全性。
2.rabbitmqctl add_vhost[vhost_name]
3.rabbitmqctl delete _vhost[vhost_name]
Exchange
Exchange类似于数据通信网络中的交换机,提供消息路由策略。rabbitmq中,producer不是通过信道直接将消息发送给queue,而是先发送给Exchange。一个Exchange可以和多个Queue进行绑定,producer在传递消息的时候,会传递一个ROUTING_KEY,Exchange会根据这个ROUTING_KEY按照特定的路由算法,将消息路由给指定的queue。和Queue一样,Exchange也可设置为持久化,临时或者自动删除。
Exchange有4种类型:direct(默认),fanout, topic, 和headers,不同类型的Exchange转发消息的策略有所区别:
1.Direct
直接交换器,工作方式类似于单播,Exchange会将消息发送完全匹配ROUTING_KEY的Queue
2.Fanout
广播式交换器,不管消息的ROUTING_KEY设置为什么,Exchange都会将消息转发给所有绑定的Queue。
3.Topic
主题交换器,工作方式类似于组播,Exchange会将消息转发和ROUTING_KEY匹配模式相同的所有队列,比如,ROUTING_KEY为user.stock的Message会转发给绑定匹配模式为 * .stock,user.stock, * . * 和#.user.stock.#的队列。( * 表是匹配一个任意词组,#表示匹配0个或多个词组)
4.Headers
消息体的header匹配(ignore)
Java 客户端访问RabbitMQ
添加依赖
创建生产者
/**
* 生产者
* @throws IOException
* @throws TimeoutException
*/
@Test
public void producer() throws IOException, TimeoutException {
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setVirtualHost("/user");
connectionFactory.setHost("localhost");
connectionFactory.setPassword("guest");
connectionFactory.setUsername("guest");
connectionFactory.setPort(5672);
//创建连接
Connection connection=connectionFactory.newConnection();
//创建管道
Channel channel=connection.createChannel();
//参数一:队列的名称,有则直接用,没有则创建
//参数二:是否持久化,false表示不作持久化处理
//参数三:是否私有化,false表示任何消费者都可以访问该队列,true表示只有第一次访问该队列的消费者才可以访问
//参数四:是否自动删除,表示mq停掉服务后是否删除该队列,false表示不自动删除
//参数五:是其他的一些属性设置
channel.queueDeclare("user_queue",false,false,false,null);
//参数一:交换机的名称
//参数二:队列的名称
//参数三:其他额外的属性
//参数四:发布的消息
channel.basicPublish("","user_queue",null,"2019/8/13".getBytes());
channel.close();
connection.close();
}
创建消费者
@Test
public void Consumer() throws IOException, TimeoutException {
ConnectionFactory connectionFactory=new ConnectionFactory();
connectionFactory.setVirtualHost("/user");
connectionFactory.setHost("localhost");
connectionFactory.setPassword("guest");
connectionFactory.setUsername("guest");
connectionFactory.setPort(5672);
//创建连接
Connection connection=connectionFactory.newConnection();
//创建管道
Channel channel=connection.createChannel();
//参数一:队列的名称,有则直接用,没有则创建
//参数二:是否持久化,false表示不作持久化处理
//参数三:是否私有化,false表示任何消费者都可以访问该队列,true表示只有第一次访问该队列的消费者才可以访问
//参数四:是否自动删除,表示mq停掉服务后是否删除该队列,false表示不自动删除
//参数五:是其他的一些属性设置
channel.queueDeclare("user_queue",false,false,false,null);
//参数一:队列
//参数二:是否代码手动签收,false代表手动签收(推荐)
channel.basicConsume("user_queue", false, new DefaultConsumer(channel) {
@Override
public void handleDelivery
(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
throws IOException {
//参数一:消息的ID
//参数二:true表示签收的所有消息,false表示只签收当前消息
channel.basicAck(envelope.getDeliveryTag(),false);
System.out.println(new String(body));//2019/8/13
}
});
}
RabbitMQ的工作模式
Simp工作模式
一个生产者,一个消费者
生产者
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = RabbitmqUitl.getConnectionFactory();
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work_queue", false, false, false, null);
for (int i = 1; i < 100; i++) {
Message message = new Message();
message.setId(i);
message.setContext("work_通知");
String json = JSONObject.toJSONString(message);
channel.basicPublish("", "work_queue", null, json.getBytes());
}
channel.close();
connection.close();
}
消费者
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory= RabbitmqUitl.getConnectionFactory();
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.basicConsume("work_queue", false, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
channel.basicAck(envelope.getDeliveryTag(),false);
System.out.println("消费消息:"+new String(body));
}
});
}
Work工作模式
1.一个生产者,多个消费者,每个消费者获取到的消息唯一
2.在多个消息的情况下,WorkQueue会将消息分派给不同的消费者,每个消费者会 接收到不同的消息,并且可以根据消息的处理速度来接受不同数量的消息,进而让 消费者程序发挥最大的性能
3.适合在集群的环境中做异步处理,最大发挥每一台服务器的性能
publish/subscribe发布订阅模式
1该模式下交换机的类型为扇形交换中心(Fanout exchange)
2.一个生产者发送的消息会被多个消费者获取
3.发布订阅模式中生产者不在直接与队列进行绑定,而是将数据发送给交换机 (Exchange)
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = RabbitmqUitl.getConnectionFactory();
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//生产者直接与exchange交互,将消息投递投递给exchange
channel.basicPublish("msg_exchange","",null,"一些短信通知".getBytes());
channel.close();
connection.close();
System.out.println("完成生产");
}
4.交换机Exchage负责将数据按照某种规则送入与之绑定的队列,进而供消费者使用
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = RabbitmqUitl.getConnectionFactory();
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("xc_queue",false,false,false,null);
//将队列与交换机绑定
//参数一:队列;参数二:交换机;参数三:路由key
channel.queueBind("xc_queue","msg_exchange","");
channel.basicConsume("xc_queue",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
channel.basicAck(envelope.getDeliveryTag(),false);
System.out.println("msg:"+new String(body));
}
});
}
Routing 路由模式
1.路由模式下交换机的类型是直接交换中心 (Direct exchange)
2.发布订阅模式是交换机无条件将所有消息分发给绑定的队列;而路由模式则是根据 routing key有条件的将数据筛选后分发给绑定的队列。
3.发送消息到交换机时要指定路由key ,消费者将队列绑定到交换机时也需要指定路由key
生产者
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = RabbitmqUitl.getConnectionFactory();
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
Map map=new HashMap();
map.put("chain.ch.2000-10-1","2000-10-1川航票务");
map.put("chain.xh.2000-10-1","2000-10-1夏航票务");
map.put("chain.cd.2000-10-1","2000-10-1成都票务");
map.put("chain.xz.2000-10-1","2000-10-1西藏票务");
map.put("chain.bj.2000-10-2","2000-10-2北京票务");
map.put("chain.xh.2000-10-2","2000-10-2夏航票务");
map.put("chain.cd.2000-10-2","2000-10-2成都票务");
map.put("chain.xz.2000-10-2","2000-10-2西藏票务");
map.put("chain.bj.2000-10-2","2000-10-2北京票务");
for(Iterator iterator=map.entrySet().iterator();iterator.hasNext();){
Map.Entry<String,String> entry= (Map.Entry<String, String>) iterator.next();
//参数二为路由key
channel.basicPublish("route_exchange",entry.getKey(),null,entry.getValue().getBytes());
}
}
消费者
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = RabbitmqUitl.getConnectionFactory();
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("qn_queue",false,false,false,null);
//将队列与交换机绑定
//参数一:队列;参数二:交换机;参数三:路由key
channel.queueBind("qn_queue","route_exchange","chain.xz.2000-10-2");
channel.basicConsume("qn_queue",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
channel.basicAck(envelope.getDeliveryTag(),false);
System.out.println("msg:"+new String(body));
}
});
}
Topic主题模式
1.主题模式是在Routing模式匹配基础上,提供了对RouteKey模糊匹配的功能,可 以简化编程,主题模式下交换机的类型为主题交换中心(Topic exchange)
2.主题模式下,模糊匹配表达式规则为
*匹配单个关键字
#匹配所有关键字
生产者
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = RabbitmqUitl.getConnectionFactory();
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
Map map=new HashMap();
map.put("chain.ch.2000-10-1","2000-10-1川航票务");
map.put("chain.xh.2000-10-1","2000-10-1夏航票务");
map.put("chain.cd.2000-10-1","2000-10-1成都票务");
map.put("chain.xz.2000-10-1","2000-10-1西藏票务");
map.put("chain.bj.2000-10-2","2000-10-2北京票务");
map.put("chain.xh.2000-10-2","2000-10-2夏航票务");
map.put("chain.cd.2000-10-2","2000-10-2成都票务");
map.put("chain.xz.2000-10-2","2000-10-2西藏票务");
map.put("chain.bj.2000-10-2","2000-10-2北京票务");
for(Iterator iterator = map.entrySet().iterator(); iterator.hasNext();){
Map.Entry<String,String> entry= (Map.Entry<String, String>) iterator.next();
//参数二为路由key
channel.basicPublish("topic_exchange",entry.getKey(),null,entry.getValue().getBytes());
}
}
消费者
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = RabbitmqUitl.getConnectionFactory();
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("qn_queue",false,false,false,null);
//将队列与交换机绑定
//参数一:队列;参数二:交换机;参数三:路由key
channel.queueBind("qn_queue","topic_exchange","*.*.2000-10-2");
channel.basicConsume("qn_queue",false,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
channel.basicAck(envelope.getDeliveryTag(),false);
System.out.println("msg:"+new String(body));
}
});
}