RabbitMq消息队列

参考:https://blog.csdn.net/hellozpc/article/details/81436980

什么是消息队列

MQ :message Queue ,实际上是一个队列,先进先出,队列中存放的是message

主要用途:不同进程process/线程Thread之间的通信

产生消息队列的原因

1.不同进程(process)之间传递消息时,两个进程之间耦合程度过高,改动一个进程,引发必须修改另一个进程

2.不同进程(process)之间传递消息时,为了实现标准化,将消息的格式规范化

3.某一个进程接受的消息太多,一下子无法处理完,并且也有先后顺序,必须对收到的消息进行排队

RabbitMq的特点

1.实现应用程序和应用程序之间的通信

2.使用Erlang编写,Erlang是一种并发的编程语言

什么叫AMQP

AMQP: 是消息队列的一个协议

如何使用RabbitMQ

1.安装Erlang

2.安装RabbitMQ

搭建RabbitMQ环境 ------》 登录RabbitMQ -------- > 设置用户,密码,vhost ----- >获取端口号 -----》此时就拥 有了一个RabbirMQ

两个应用之间的通信先经过RabbitMQ

RabbitMQ的5种队列

一个生产者 一个队列 一个消费者:

实现:

1.建立客户端

导入客户端依赖包:amqp-client.jar

建立工厂,配置工厂信息(RabbitMQ的用户账号,密码,端口号,vhost)与MQ连接

 /**
ConnectionUtils类
*/
//ConnectionFactory来自com.rabbitmq.client.ConnectionFactory
ConnectionFactory factory = new ConnectionFactory()
//配置工厂信息
factory.setHost("localhost") //设置服务地址
factory.setPort("5672")
factory.setVirtualhost("testhost") //在RabbitMQ上创建的虚拟的主机名称
factory.setUserName("admin") //在RabbitMq上设置的账号
factory.setPassword("admin") //根据工厂建立连接
Connection conn = factory.nwe Connection() // com.rabbitmq.client.Connection
return conn;

生产者发送消息到队列

private final static String QUEUE_NAME = "q_test_01"
//获取连接
Connection conn = ConnectionUtil.getConnection()
//通过连接建立通道
Channel channel = conn.createChannel();
//通过通道创建一个队列
channel.QueueDeclare(QUEUE_NAME,false,false,false,null);
//通过通道向对列传递信息
String message = "hhh";
channel.basicPublish("",QUEUE_NAME,false,message.getByte())

生产者做的事情:

1.与RabbitMQ建立连接(conn = getConnection()),

2.之后建立通道(channel = conn.createChannel()),

3.在通道上建立队列(channel.queueDclare("name")),

4.通过通道往队列发送信息(channel.basicPublish())

消费者消费队列的信息

private final static String QUEUE_NAME = "q_test_01"
//获取连接
Connection conn = ConnectionUtil.getConnection()
//通过连接建立通道
Channel channel = conn.createChannel();
//通过通道声明一个队列
channel.QueueDeclare(QUEUE_NAME,false,false,false,null);
//定义队列的消费者
QueueingConsummer consummer = new QueueingConsumer(channel);
//监听队列
channel.basicCosume(QUEUE_NAME,true,consummer);
//获取队列的信息
while(true){
QueueingConsummer.Delively delivery = comsummer.nextDelivery();
String message = new String(delivery.getBody())
}

生产者做的事情:

1.与RabbitMQ建立连接conn = getConnection()

2.建立通道channel = conn.createChannel()

3.声明队列channel.queueDeclare("name")

4.建立通道里的消费者QueueingConsumer consumer = new Consummer(channel)

5.监听队列信息 basicConsume("队列名称",true,consumer)

6.获取数据 comsummer.nextDelivery()

work模式,一个生产者,一个队列,多个消费者

消费者1接受收信息

private final static String QUEUE_NAME = "q_test_01"
//获取连接
Connection conn = ConnectionUtil.getConnection()
//通过连接建立通道
Channel channel = conn.createChannel();
//通过通道声明一个队列
channel.QueueDeclare(QUEUE_NAME,false,false,false,null);
//同一时刻服务器只会发一条信息给消费者
channel.basicQos)(1)
//定义消费者
QueueingConsummer consummer = new QueueingConsummer(channel);
//监听队列
channel.basicComsume("QUEUE_NAME",true,consumer)
//获取队列的信息
while(true){
QueueingConsummer.Delively delivery = comsummer.nextDelivery();
String message = new String(delivery.getBody())
//休眠
Thread.sleep(10)
// 返回确认状态,注释掉表示使用自动确认模式
//channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}

消费者2接收信息

private final static String QUEUE_NAME = "q_test_01"
//获取连接
Connection conn = ConnectionUtil.getConnection()
//通过连接建立通道
Channel channel = conn.createChannel();
//通过通道声明一个队列
channel.QueueDeclare(QUEUE_NAME,false,false,false,null);
//同一时刻服务器只会发一条信息给消费者
channel.basicQos)(1)
//定义消费者
QueueingConsummer consummer = new QueueingConsummer(channel);
//监听队列,false表示手动返回完成状态,true表示自动
channel.basicComsume("QUEUE_NAME",true,consumer)
//获取队列的信息
while(true){
QueueingConsummer.Delively delivery = comsummer.nextDelivery();
String message = new String(delivery.getBody())
//休眠1秒
Thread.sleep(1000)
// 返回确认状态,注释掉表示使用自动确认模式
//channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}

测试结果:

生产者生产100条信息,消费1个消费2一起去消费信息,消费1消费一条信息睡眠0.01秒,消费2消费一条信息睡眠1秒,最后两个消费者消费的信息个数相等,且不重复(轮询分发:消息按顺序的发送给消费者)

按常理来说应该睡眠时间断的消费者得到的信息条数更多

如何解决这一问题呢?

通过Qos,和Acknowledge(告知已收到)来解决

basicQos 方法设置了当前信道最大预获取(prefetch)消息数量为1

basicQos(1):

消息从队列异步推送给消费者,消费者的 ack 也是异步发送给队列 ,队列只有在收到消费者发回的上一条消息 ack 确认后,才会向该消费者发送下一条消息

basicQos(0):

没有限制,队列会将所有消息尽快发给消费者

公平分发:

使用basicQos( prefetchCount = 1)方法 ,限制RabbitMQ只发不超过1条的消息给同一个消费者。当消息处理完毕后,有了反馈,才会进行第二次发送

使用公平分发,必须关闭自动应答,改为手动应答

//使用手动确认模式
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); // 监听队列,false表示手动返回完成状态,true表示自动
channel.basicConsume(QUEUE_NAME, false, consumer);

订阅模式(exchange)

fanout exchange (广播模式)

特点:多个队列,一个队列对应一个用户,生产者将信息发送到exchange中,通过exchange发送给绑定的队列,消费者从对应的队列中消费信息,这样,所有的消费者就可以消费相同的信息

生产者生成

package com.zpc.rabbitmq.subscribe;

import com.zpc.rabbitmq.util.ConnectionUtil;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection; public class Send { private final static String EXCHANGE_NAME = "test_exchange_fanout"; public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明exchange
channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); // 消息内容
String message = "Hello World!";
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'"); channel.close();
connection.close();
}
}

消费者消费

public class Recv {

    private final static String QUEUE_NAME = "test_queue_work1";

    private final static String EXCHANGE_NAME = "test_exchange_fanout";

    public static void main(String[] argv) throws Exception {

        // 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel(); // 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null); // 绑定队列到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ""); // 同一时刻服务器只会发一条消息给消费者
channel.basicQos(1); // 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QUEUE_NAME, false, consumer); // 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [Recv] Received '" + message + "'");
Thread.sleep(10); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}

声明exchange: channel.exchangeDeclare("交换机的名字","交换机的类型")

                        channel.exchangeDeclare("交换机名","fanout")

                        fanout:广播式交换机,所有队列都能接受信息

向交换机发送数据:channel.basicPublish("交换机名","", null, message.getByte())

队列与交换机绑定:channel.queueBind("队列名","交换机名")

Direct exchange (exchange的key与queue的key一一匹配模式)

生产者生产:

生产者创建交换机时声明交换机的类型

channel.exchangeDeclare("交换机名称","direct") //声明交换机的类型为direct

发送数据时指定接受的队列,key值与队列的key值一一对应

channel.basicPublish("交换机名称","路由到queue的key值",null,message.getBytes())

消费者消费:

channel.queueBind("队列名称",”交换机名称“,"队列与交换机的key值一一对应")

topic模糊匹配模式

"#":匹配所有数据

"*":匹配单个数据

生产者发送数据时声明key值

两个交换机:

channel.exchangeDeclare("交换机1","topic")

channel.basicPublish("交换机1","user.news",null,message.getBytes());

channel.basicPublish("j交换机1","user.weather",null,message.getByutes())

消费者绑定交换机:

消费者一:

channel.queueBind("队列名称","交换机名称","user.*")

RabbitMQ的入门学习的更多相关文章

  1. RabbitMQ从入门到精通

    RabbitMQ从入门到精通 学习了:http://blog.csdn.net/column/details/rabbitmq.html RabbitMQ是AMQP(advanced message ...

  2. 中小研发团队架构实践之RabbitMQ快速入门及应用

    原文:中小研发团队架构实践之RabbitMQ快速入门及应用 使用过分布式中间件的人都知道,程序员使用起来并不复杂,常用的客户端API就那么几个,比我们日常编写程序时用到的API要少得多.但是分布式中间 ...

  3. ASP.NET Core消息队列RabbitMQ基础入门实战演练

    一.课程介绍 人生苦短,我用.NET Core!消息队列RabbitMQ大家相比都不陌生,本次分享课程阿笨将给大家分享一下在一般项目中99%都会用到的消息队列MQ的一个实战业务运用场景.本次分享课程不 ...

  4. ASP.NET Core on K8S 入门学习系列文章目录

    一.关于这个系列 自从2018年底离开工作了3年的M公司加入X公司之后,开始了ASP.NET Core的实践,包括微服务架构与容器化等等.我们的实践是渐进的,当我们的微服务数量到了一定值时,发现运维工 ...

  5. RabbitMQ由浅入深入门全总结(一)

    写在最前面 距离上一次发文章已经很久了,其实这段时间一直也没有停笔,只不过在忙着找工作还有学校结课的事情,重新弄了一下博客,后面也会陆陆续续会把文章最近更新出来~ 这篇文章有点长,就分了两篇Q PS: ...

  6. vue入门学习(基础篇)

    vue入门学习总结: vue的一个组件包括三部分:template.style.script. vue的数据在data中定义使用. 数据渲染指令:v-text.v-html.{{}}. 隐藏未编译的标 ...

  7. Hadoop入门学习笔记---part4

    紧接着<Hadoop入门学习笔记---part3>中的继续了解如何用java在程序中操作HDFS. 众所周知,对文件的操作无非是创建,查看,下载,删除.下面我们就开始应用java程序进行操 ...

  8. Hadoop入门学习笔记---part3

    2015年元旦,好好学习,天天向上.良好的开端是成功的一半,任何学习都不能中断,只有坚持才会出结果.继续学习Hadoop.冰冻三尺,非一日之寒! 经过Hadoop的伪分布集群环境的搭建,基本对Hado ...

  9. PyQt4入门学习笔记(三)

    # PyQt4入门学习笔记(三) PyQt4内的布局 布局方式是我们控制我们的GUI页面内各个控件的排放位置的.我们可以通过两种基本方式来控制: 1.绝对位置 2.layout类 绝对位置 这种方式要 ...

随机推荐

  1. netty源码解析(4.0)-26 ByteBuf内存池:PoolArena-PoolSubpage

    PoolChunk用来分配大于或等于一个page的内存,如果需要小于一个page的内存,需要先从PoolChunk中分配一个page,然后再把一个page切割成多个子页-subpage,最后把内存以s ...

  2. 百万年薪python之路 -- 面向对象之三大特性

    1.面向对象之三大特性 1.1封装 封装:就是把一堆代码和数据,放在一个空间,并且可以使用 对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封 ...

  3. zepto源码分析·core模块

    准备说明 该模块定义了库的原型链结构,生成了Zepto变量,并将其以'Zepto'和'$'的名字注册到了window,然后开始了其它模块的拓展实现. 模块内部除了对选择器和zepto对象的实现,就是一 ...

  4. 两行代码玩转SUMO!

    两行代码玩转SUMO! 这篇博客很简单,但是内容很丰富 如何生成如下所示的研究型路网结构? 只需要打开ubuntu终端输入如下代码即可,grid.number代表路口数量,grid.length代表路 ...

  5. InitializingBean,spring 初始化bean

    springframework的提供接口,InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的 ...

  6. 持久层框架JPA与Mybatis该如何选型

    一.现状描述 目前java 持久层ORM框架应用最广泛的就是JPA和Mybatis.JPA只是一个ORM框架的规范, 对该规范的实现比较完整就是Spring Data JPA(底层基于Hibernat ...

  7. C语言中为什么float型数据的范围是3.4E-38~3.4E+38

    因为float所占的位数决定了他的大小位数就是计算机的存储所需要的bit多少32位浮点,64位双精度浮点范围不同//////////////////////以前学计算系统基础的时候有这么个说法计算机存 ...

  8. 【阿里云IoT+YF3300】8.物联网设备用户脚本开发

    除了我们必须熟悉的网页脚本,比如JavaScript.其实在工业自动化中,组态软件是必备脚本的,只是有的脚本语言风格类似C或类似Basic而已.比如昆仑通泰的组态屏中的组态软件.通过安装组态软件可以简 ...

  9. Vue---mock.js 使用

    mockjs 概述 在我们的生产实际中,后端的接口往往是较晚才会出来,并且还要写接口文档,于是我们的前端的许多开发都要等到接口给我们才能进行,这样对于我们前端来说显得十分的被动,于是有没有可以制造假数 ...

  10. 医生智能提醒小程序数据库设计心得——Legends Never Die

    数据库设计心得 根据我们小组数据库设计的整个流程,我们将整个数据库设计划分为两个具体的阶段,在每个阶段需要进行不同的准备,有不同的注意事项,接下来我们将结合在数据库设计过程中遇到的一些问题和困难,提出 ...