大话RabbitMQ 基础入门
----------写在前面----------
近些年微服务越来越火,让我也忍不住想去一窥微服务究竟,讲到微服务,就离不开分布式,而分布式,也离不开消息队列,在消息队列中,RabbitMQ可以说是比较具有代表性的一款。
这里是一篇介绍消息队列以及各种消息队列产品对比的文章,讲得很好,有兴趣的可以看一看。
https://cloud.tencent.com/developer/article/1006035
在讲RabbitMQ之前,首先需要在电脑上安装和配置RabbitMQ,网络上已经有很多这类文章,如果懒得去搜索,可以看看这篇介绍如何安装配置RabbitMQ的文章。
https://blog.csdn.net/weixin_39735923/article/details/79288578
其中,在安装RabbitMQ的过程中,遇到了一个坑,在启用RabbltMQ的管理界面执行
rabbitmq-plugins enable rabbitmq_management
命令时,出现了以下这样的报错
可以在该指令前加上 .\ 即
.\rabbitmq-plugins enable rabbitmq_management
祝安装顺利 !!
-------正文------
基本概念
下面是在.Net中使用RabbitMQ要明白的一些名词概念。
综上所诉,他们之间的关系可以用我下面的 丑图 表示。
在图中,没有吧Routing key画出。Producer每一次发送消息,除了发出消息本身,还会随着消息带上一个routingKey,而且每一次将Exchange和Queue绑定,大体需要三个参数,
string queueName, string exchangeName, string routingKey
其中也有一个routingKey,但此RoutingKey非彼Routingkey。
大白话
对这个过程,我们可以理解为国家给灾区发送救灾物资,国家给当地政府划拨物资的时候,会规定,谁才能拿到这批物资,如(房子倒了的.家里有人受伤了的.家庭经济困难的)。
而当地政府在分配这批物资之前,为了方便物资的分配,会给每个家庭贴上一个标签,如
家庭A 经济困难
家庭B 房子倒了.经济困难
家庭C 家庭富有.房子倒了
家庭D 房子倒了的.家里有人受伤了的.家庭经济困难的
所以,发送消息时候的routingKey就是国家规定的那批物质分配规则。
而Exchange和Queue绑定时的RoutingKey可以理解为当地政府给每个家庭贴上的一个标签。
Exchange(交换机)转发消息的规则也有很多种:direct, topic, headers(不常用) 和 fanout,我们称之为交换类型。
我们可以把Exchange理解为分配这批物质的政府,现在国家规定了宏观的分配方向(发送消息时的routingKey),每个家庭也有了家庭情况的标签(绑定Exchange时的routingKey),但是这个物资具体怎么分,还是当地政府说了算。
Direct 严格按照国家规定来,只有房子倒了的,家里有人受伤了的而且家庭经济困难的才能分到救灾物资。 家庭D能分到
Fanout 只要是灾区的居民都能分到, 不管家庭情况如何。 家庭A\B\C\D都能分到
Topic 主题匹配: 只要家庭情况在国家规定分配规则内的,都能分到物资,但是家庭C分不到,因为他家太有钱了,这个条件不在国家的分配规则里。家庭A\B\D能分到
所以,我们在声明一个Exchange(交换机)的同时,还要指定该交换机的类型,即(当地政府怎么来分救灾物资)
其实,用这个例子,我是想说,生产者和消费者之间,就像国家与难民之间一样,国家只知道,我要帮助难民,但是难民有谁,物资能不能分到难民手里,还得当地政府说了算,你就说我这个例子恰不恰当吧!哈哈
好了,懂了概念,我们再来结合具体例子看看。
Fanout
Producer.cs的代码
using System; using System.Text; using RabbitMQ.Client;namespace _2_Publish {class Program{static void Main(string[] args){//创建连接工厂var factory = new ConnectionFactory() { HostName = "localhost" };//创建连接using (var connection = factory.CreateConnection()){//创建会话using (var chancel = connection.CreateModel()){//生命交换机chancel.ExchangeDeclare(exchange: "FanoutDemo", type: ExchangeType.Fanout);string readMsg = "helloWorld";while (readMsg.ToLower() != "exit"){var body = Encoding.UTF8.GetBytes(readMsg);//给交换机发送消息chancel.BasicPublish(exchange: "FanoutDemo", routingKey: "", body: body);Console.WriteLine($"成功发送消息{readMsg}");Console.WriteLine("请输入要发送的内容!");readMsg = Console.ReadLine();}}}}} }
Customer.cs代码
using System; using System.Text; using RabbitMQ.Client; using RabbitMQ.Client.Events;namespace _2_Receiver {class Program{static void Main(string[] args){//创建连接工厂var factory = new ConnectionFactory() { HostName = "localhost" };//创建连接using (var connection = factory.CreateConnection()){//创建会话using (var channel = connection.CreateModel()){//声明一个Fanout类型的交换机channel.ExchangeDeclare(exchange: "FanoutDemo", type: ExchangeType.Fanout);//声明一个消息队列并获取它的名字var queueName = channel.QueueDeclare().QueueName;//把消息队列和交换机绑定channel.QueueBind(exchange: "FanoutDemo", queue: queueName, routingKey: "");//创建消费者var consume = new EventingBasicConsumer(channel);//把消费者和队列绑定channel.BasicConsume(queue: queueName, autoAck: true,consumer: consume);consume.Received += (model, ea) =>{var body = ea.Body;var message = Encoding.UTF8.GetString(body);Console.WriteLine($"收到消息{message}");};Console.ReadLine();}}}} }
在上面的代码中,无论是在生产者的发送消息里
//给交换机发送消息
chancel.BasicPublish(exchange: "FanoutDemo", routingKey: "", body: body);
还是消费者所在的Queue的绑定里
//把消息队列和交换机绑定
channel.QueueBind(exchange: "FanoutDemo", queue: queueName, routingKey: "");
我们都没有制定routingKey,因为没个人都能获取消息,所以此处,声明routingKey就没有意义了。
我们看看运行效果。
运行了三个消费者,当生产者发出消息时,三个消费者都收到了相同的消息。可以理解为广播模式。(Customer单词拼写错了,图片修改不方便,就不改了,大家将就一下)
Direct
Direct时严格匹配的,只有队列绑定的RoutingKey与生产者发送消息时指定的RoutingKey完全相同,才能接收成功。
Producer.cs
using System; using System.Text; using RabbitMQ.Client;namespace _2_Publish {class Program{static void Main(string[] args){//创建连接工厂var factory = new ConnectionFactory() { HostName = "localhost" };//创建连接using (var connection = factory.CreateConnection()){//创建会话using (var chancel = connection.CreateModel()){//生命交换机chancel.ExchangeDeclare(exchange: "DirectDemo", type: ExchangeType.Direct);string readMsg = "helloWorld";while (readMsg.ToLower() != "exit"){var body = Encoding.UTF8.GetBytes(readMsg);//给交换机发送消息chancel.BasicPublish(exchange: "DirectDemo", routingKey: "Direct.Key", body: body);Console.WriteLine($"成功发送消息{readMsg}");Console.WriteLine("请输入要发送的内容!");readMsg = Console.ReadLine();}}}}} }
我把Exchange的类型更改为Direct类型,并且发送消息的routingKey设置为Direct.Key。
然后我们来定义消费者
Customer.CS
using System; using System.Text; using RabbitMQ.Client; using RabbitMQ.Client.Events;namespace _2_Receiver {class Program{static void Main(string[] args){//创建连接工厂var factory = new ConnectionFactory() { HostName = "localhost" };//创建连接using (var connection = factory.CreateConnection()){//创建会话using (var channel = connection.CreateModel()){//声明一个Fanout类型的交换机channel.ExchangeDeclare(exchange: "DirectDemo", type: ExchangeType.Direct);//声明一个消息队列并获取它的名字var queueName = channel.QueueDeclare().QueueName;Console.WriteLine("请输入RoutingKey!");var routingKey = Console.ReadLine();//把消息队列和交换机绑定channel.QueueBind(exchange: "DirectDemo", queue: queueName, routingKey: routingKey);//创建消费者var consume = new EventingBasicConsumer(channel);//把消费者和队列绑定channel.BasicConsume(queue: queueName, autoAck: true,consumer: consume);Console.WriteLine("开始监听消息");consume.Received += (model, ea) =>{var body = ea.Body;var message = Encoding.UTF8.GetString(body);Console.WriteLine($"收到消息{message}");};Console.ReadLine();}}}} }
消费者的RoutingKey再控制台输入,
运行效果如下:
可以看到,只有RoutingKey为Direct.Key的消费者才收到了生产者发出的消息。
Topic
RabbitMQ 中的 RouteKey 支持绑定键表达式写法,有两种主要的绑定键:
*(星号)可以代替一个单词.
# (井号) 可以代替0个或多个单词.
比如在下面这个图中(P为发送者,X为RabbitMQ中的Exchange,C为消费者,Q为队列)
在这个示例中,我们将发送一条关于动物描述的消息,也就是说 Name(routeKey) 字段中的内容包含 3 个单词。第一个单词是描述速度的(celerity),第二个单词是描述颜色的(colour),第三个是描述哪种动物的(species),它们组合起来类似:“..”。
然后在使用 CapSubscribe
绑定的时候,Q1绑定为 CapSubscribe["*.orange.*"]
, Q2 绑定为CapSubscribe["*.*.rabbit"]
和 [CapSubscribe["lazy.#]
。
那么,当发送一个名为 "quick.orange.rabbit" 消息的时候,这两个队列将会同时收到该消息。同样名为 lazy.orange.elephant
的消息也会被同时收到。另外,名为 "quick.orange.fox" 的消息将仅会被发送到Q1队列,名为 "lazy.brown.fox" 的消息仅会被发送到Q2。"lazy.pink.rabbit" 仅会被发送到Q2一次,即使它被绑定了2次。"quick.brown.fox" 没有匹配到任何绑定的队列,所以它将会被丢弃。
另外一种情况,如果你违反约定,比如使用 4个单词进行组合,例如 "quick.orange.male.rabbit",那么它将匹配不到任何的队列,消息将会被丢弃。
但是,假如你的消息名为 "lazy.orange.male.rabbit",那么他们将会被发送到Q2,因为 #(井号)可以匹配 0 或者多个单词。
我们结合代码来看一看。
Producer.cs
using System; using System.Text; using RabbitMQ.Client;namespace _2_Publish {class Program{static void Main(string[] args){//创建连接工厂var factory = new ConnectionFactory() { HostName = "localhost" };//创建连接using (var connection = factory.CreateConnection()){//创建会话using (var chancel = connection.CreateModel()){//生命交换机chancel.ExchangeDeclare(exchange: "TopicDemo", type: ExchangeType.Topic);string readMsg = "helloWorld";while (readMsg.ToLower() != "exit"){var body = Encoding.UTF8.GetBytes(readMsg);//给交换机发送消息chancel.BasicPublish(exchange: "TopicDemo", routingKey: "Topic.Demo.Key", body: body);Console.WriteLine($"成功发送消息{readMsg}");Console.WriteLine("请输入要发送的内容!");readMsg = Console.ReadLine();}}}}} }
我给发送消息的routingKey指定为Topic.Demo.Key
再来看看消费者
Cuustomer.cs
using System; using System.Text; using RabbitMQ.Client; using RabbitMQ.Client.Events;namespace _2_Receiver {class Program{static void Main(string[] args){//创建连接工厂var factory = new ConnectionFactory() { HostName = "localhost" };//创建连接using (var connection = factory.CreateConnection()){//创建会话using (var channel = connection.CreateModel()){//声明一个Fanout类型的交换机channel.ExchangeDeclare(exchange: "TopicDemo", type: ExchangeType.Topic);//声明一个消息队列并获取它的名字var queueName = channel.QueueDeclare().QueueName;Console.WriteLine("请输入RoutingKey!");var routingKey = Console.ReadLine();//把消息队列和交换机绑定channel.QueueBind(exchange: "TopicDemo", queue: queueName, routingKey: routingKey);//创建消费者var consume = new EventingBasicConsumer(channel);//把消费者和队列绑定channel.BasicConsume(queue: queueName, autoAck: true,consumer: consume);Console.WriteLine("开始监听消息");consume.Received += (model, ea) =>{var body = ea.Body;var message = Encoding.UTF8.GetString(body);Console.WriteLine($"收到消息{message}");};Console.ReadLine();}}}} }
其RoutingKey也是在外部输入。
我们看看运行效果
因为Producer发布消息的RoutingKey是Topic.Demo.Key
又因为#可以代表0个或者多个单词 ,*能代表一个单词
所以*.*.Key Topic.#与Topic.Demo.Key匹配,而其他两个*.Key和test.1.2当然是不匹配的,所以没有收到消息。
总结
对于上面的例子,我们可以总结出,编写一个生产者的过程如下:
创建连接工厂-》创建连接-》创建会话(Chanel)-》创建交换机(Exchange)-》发送消息
编写一个生产者的过程如下:
创建连接工厂-》创建连接-》创建会话(Chanel)-》创建交换机(Exchange)-》创建队列-》绑定队列和交换机-》创建消费者-》把消费者和队列绑定-》监听消息
掌握这个大的方向,不管交换机怎么分配,代码应该都会写了。
为什么在生产者中和消费者中都要创建交换机呢? 因为我们不确定是生产者先执行还是消费者先执行,所以提前创建一下,避免连接时发现没有创建交换机,出现错误,如果交换机已经创建了,那么默认不会再次创建的。
另外,交换机创建后,同一名称的交换机使用完不会自动删除,但是第二次如果创建的名称和上次一样,但是交换机类型不一样了,那么便会出现报错。
这里总结的是一些RabbitMQ的基础知识,后面还会继续写一些更深入的使用技巧,如果不想错过精彩信息,点击关注一下吧(๑¯◡¯๑)!
大话RabbitMQ 基础入门相关推荐
- 《Ansible权威指南 》一 第一篇 Part 1 基础入门篇
本节书摘来自华章出版社<Ansible权威指南 >一书中的第1章,第1.1节,李松涛 魏 巍 甘 捷 著更多章节内容可以访问云栖社区"华章计算机"公众号查看. 第一篇 ...
- RabbitMQ从入门到实战(图文并茂)
MQ概述 MQ全称 Message Queue(消息队列),是在消息的传输过程中保存消息的容器.多用于分布式系统 之间进行通信. MQ优势 1.应用解耦 MQ相当于一个中介,生产方通过MQ与消费方交互 ...
- Java基础入门:IDEA软件安装和软件初始化设置
IDEA是一个专门针对Java的集成开发工具(IDE),由Java语言编写.所以,需要有JRE运行环境并配置好环境变量. 它可以极大地提升我们的开发效率.可以自动编译,检查错误.在公司中,使用的就是I ...
- 视频教程-2020新版C语言程序设计零基础入门小白自学编程-C/C++
2020新版C语言程序设计零基础入门小白自学编程 7年的开发架构经验,曾就职于国内一线互联网公司,开发工程师,现在是某创业公司技术负责人, 擅长语言有node/java/python,专注于服务端研发 ...
- Springboot RabbitMQ 基础使用、消息发送确认、签收
概述 rabbitMQ 会做一个系列,包括:安装.基础使用.高级队列.集群. 使用环境: jdk 8 .springboot 2.4.10 常见概念: AMQP:高级消息队列协议,这是一个消息应用的规 ...
- kafka基础入门_CodingPark编程公园
文章介绍 本文是kafka基础入门篇,讲解内容包括: 1. 消息队列对比表 2. Kafka概念及特性 3. kafka总体结构 4. kafka各项配置 5. 生产者 6. kafka Broker ...
- 用python循环语句求素数_Python基础入门_3条件语句和迭代循环
Python 基础入门前两篇: Python 基础入门--简介和环境配置 Python基础入门_2基础语法和变量类型 这是第三篇内容,主要简单介绍条件语句和迭代循环语句,内容也比较简单,目录如下: 条 ...
- MAYA 2022基础入门学习教程
流派:电子学习| MP4 |视频:h264,1280×720 |音频:AAC,48.0 KHz 语言:英语+中英文字幕(根据原英文字幕机译更准确)|大小解压后:3.41 GB |时长:4.5小时 包含 ...
- Blender 3.0基础入门学习教程 Introduction to Blender 3.0
成为Blender通才,通过这个基于项目的循序渐进课程学习所有主题的基础知识. 你会学到什么 教程获取:Blender 3.0基础入门学习教程 Introduction to Blender 3.0- ...
最新文章
- CI流水线配置文件参数详解(一)
- R语言基于自定义函数构建xgboost模型并使用LIME解释器进行模型预测结果解释:基于训练数据以及模型构建LIME解释器解释一个iris数据样本的预测结果、LIME解释器进行模型预测结果解释并可视化
- android 版本更新原理,Android系统Recovery工作原理之使用update.zip升级过程分析(二)...
- Redis5.0:这些场景下使用,高效还降低成本!
- sklearn 决策树例子_决策树DecisionTree(附代码实现)
- 史上最详细的SSM框架整合(Spring、SpringMVC、Mybatis)
- 数据结构排序法之鸡尾酒排序法he快速排序法
- ROS----窃听小乌龟行动计划
- java机器PDF_机器人制作入门(第3版)PDF 下载
- 主成分分析(Principal components analysis)(特征降维)(PCA)-最大方差解释
- Timer运行多个TimeTask
- 【避坑】初次接项目的血与泪,扎坑了老铁(二)
- CRMPM如何帮助企业创造最优销售绩效
- Linux libmodbus库编译,libmodbus编译安装使用
- A4纸在屏幕上的像素尺寸
- Python入门第三章--第三节:列表
- 快速记住《计算机文化基础》海量题法
- 冰汽时代机器人不用热_冰汽时代机器流玩法 寒霜朋克机器人流玩法怎么玩
- 无需安装软件架设NOD32升级服务器指南!
- 雍正《连平州志·序》:揭秘连平起源之迷
热门文章
- Delphi 与 DirectX 之 DelphiX(93): TDIB.DrawDarken();
- Day1 安装虚拟机和centos7系统
- AngularJS第六课(路由)
- [转载] 中华典故故事(孙刚)——31 千里送鹅毛_礼轻情义重
- 黄聪:C#索引器详解、示例
- iOS ipv6审核被拒绝的解决方案(已审核通过)
- rhel6.4部署tomcat
- java类读取properties里内容
- nginx lua获取客户端ip
- 造轮子,常用JS处理HTML工具(HTMLUtils)