RabbitMQ 入门 (Go) - 4. 使用 Fanout Exchange 做服务发现(上)
到目前为止,我们项目的结果大致如下:
传感器生成的模拟数据(包含传感器名称、数据、时间戳)是通过传感器在运行时动态创建的 Queue 来发送的。这些 Queue 很难直接被发现。
为了解决这个问题,我创建了另一个消息,它包含各传感器的 Queue 的路由 key,这个消息是在一个“众所周知”的 Queue 上发布的,所以协调器就可以得到传感器的路由信息。
传感器的数据是发布在默认的 Direct Exchange 上,也就是说只有一个消费者可以收到这个消息,这就是我们想要的效果。具体的,无论有多少个协调器,RabbitMQ 会保证只有一个协调器会收到信息,并且只会收到一次。
然后,用于发现传感器的路径确有不同的需求,如果存在多个协调器,那么当传感器上线的时候,所有的协调器都必须得知,所以就不能使用 Direct Exchange 了。这时使用 Fanout Exchange 就比较合理了,Fanout Exchange 将会同时通知所有附加在 Exchange 上面的 Queue,也就是把传感器的路由信息发送给所有在线的协调器。
但是这也有其他问题:如果没有接收者监听,那么这些路由信息不会保留,这个问题稍后再解决,我们先把发布路由信息的 Exchange 从 Direct 改为 Fanout。
使用 Fanout Exchange 发布传感器路由信息
目前,在传感器项目中,我们使用默认的 Direct Exchange 来发布传感器路由消息:
看一下管理控制台,可以看到 RabbitMQ 还提供了一个 Fanout Exchange 也就是 amq.fanout:
修改代码,暂时改用 amq.fanout 来发布传感器路由信息:
首先,删除第 38 行的代码,它原是用来创建一个 Queue 以便协调程序可以接收到传感器的路由信息。现在,这个工作将由 Exchange 的消费者们来完成,它们会创建自己的 Queue 来监听这个 Exchange。
第 43 行,把路由 Key 改为 “”,因为 Fanout Exchange 不需要使用该 Key 来决定消息发往哪里,它会把消息进行复制并发送到每个绑定到它的 Queue 上面。
最后,第 42 行,把 exchange 这个参数改为 amq.fanout。
运行 sensors 项目查看效果
打开控制台:
可以看到 amq.fanout 确实有数据了,尽管现在的消息传递速率为 0。
点进去:
可以看到一个路由信息,但是因为没有任何 Queue 绑定到这个 Exchange,这个消息就丢失了,因为消息无处可发。
重建协调器
在最早几节内容中,我做了一个非常简单的协调器程序,它可以简单的发布和接收消息。为了配合我们的应用场景,我们需要建立一个更健壮一些的协调器。它的主要职责是:通过消息代理(RabbitMQ)与传感器进行交互。
不过首先,为了代码复用,我对现有的项目结构进行调整:
我把项目的外层目录名从 sensors 改为 demo,然后在里面建立sensors 文件夹,把 main.go 移动到 sensors 里面,并改名为 sensor.go。
然后建立 coordinator 文件夹,在里面建立 queuelistener.go 文件,内容较多,我分为三个图展示:
第 15 行,建立 QueueListener struct,它里面包含发现传感器数值 Queue 的逻辑,接收它们的消息,并把它们在一个事件聚合器里面翻译成事件。不过目前它主要聚焦获取消息这项工作,所以它有三个字段:
到 RabbitMQ 的连接
在该连接上的 Channel
一个 Map,当作注册表,里面存放着这个协调器所监听的源,使用 Map 可以防止将同一个传感器注册两次,而当传感器下线的时候可以通过这个 Map 来关闭监听(这个我就不实现了)
第 21 行,建立一个构造函数,它可以返回一个 *QueueListener
第 31 行创建一个方法 ListenForNewSource:
它可以让 QueueListener 发现新的传感器,在这里创建 Queue 的时候,我们不关心 Queue 的名称,所以 name 参数为“”,这样的话 RabbitMQ 会为它创建一个唯一的名称。
但是当 Queue 被创建时,它会默认绑定到 Direct Exchange。而在之前,我刚把代码修改为让传感器通过 amq.fanout Exchange 来发布它们的信息,所以我们需要把这个 Queue 重新绑定到那个上面。这里就使用 Channel 上的 QueueBind 方法来实现(第 33 行)。
QueueBind 方法参数:
第一个参数是刚刚创建的 Queue 的名称,这就是要绑定的 Queue
第二个参数是路由 Key,由于 Fanout Exchange 会忽略这个参数,所以这里写“”
第三个参数是要绑定的 Exchange 的名称,也就是 amq.fanout
第四个参数,如果把 noWait 设置为 true,那么万一绑定不成功,就会把 Channel 关闭。这里我把它设为 false,因为我知道 Exchange 和 Queue 都会存在,如果失败,那么会关闭 Channel 并发生错误。
第五个参数不需要,设为 nil
第 40 行,设置消息的接收,返回 Go Channel,这里的参数需要用到 Queue 的名称
第 49 行,通过 for range 来处理通过 Go Channel 发过来的消息。如果接收到消息,表示有新的传感器上线了。
第 50 行,在有传感器上线后,通过 Consume 方法和 msg.Body(也就是传感器的名称),来读取传感器的模拟数据。记得我们把传感器的模拟数据发布到了默认的 Direct Exchange 上面,所以每次只会把消息传递给一个接收者,这意味着,当我注册了多个协调器的时候,它们将共享到这些 Queue 的访问,当这些发生的时候,RabbitMQ 将会轮流传送给每一个注册的接收者。这也就允许我们对协调器进行横向扩展,而且不影响整个系统其余的部分。
第 59 行,判断传感器是否在该协调器中注册,如果没有,那就进行注册。
第 62 行,使用 goroutine 来调用 AddListener 方法,该方法代码如下:
这个方法将会监听 Go Channel 中的消息
在里面使用 for range 来等待 Go Channel 传送消息
在这里,我们把二进制数据转化为我们可以在程序里使用的数据,也就是 SensorMessage 类型
然后暂时先打印即可
建立协调器的 main
在 coordinator 目录下建立 exec 文件夹,目的是创建 main package,在里面创建 main.go 代码如下:
第 9 行,我们创建一个 QueueListener
第 10 行,使用 goroutine 让他进行监听,防止阻塞主线程
第 12-13 行的目的就是让程序一直存活,防止 goroutine 停止运行。
最后 sensor.go 里面有一处代码需要修改,在 main 函数的 for 循环里面,每次使用 encoder 的时候都需要 重新创建一个,所以我添加了 63 行的代码:
运行
我们运行一下试试,注意:一定要先运行 coordinator 项目,然后再运行 sensors 项目,否则会有问题。 下面左侧是 coordinator,右侧是 sensors:
可以看到 coordinator(协调器)可以读取到传感器的数据了。
这里我们使用了一个最简单最基本的机制来做传感器 Queue 的发现。
RabbitMQ 入门 (Go) - 4. 使用 Fanout Exchange 做服务发现(上)的更多相关文章
- RabbitMQ 入门 (Go) - 5. 使用 Fanout Exchange 做服务发现(下)
到目前为止,我一直专注于如何让消息进出消息代理,也就是RabbitMQ. 实际上,我们可以继续使用 RabbitMQ 和它的 Exchanges 来连接这个应用程序的其他部分,但是我想探索一个稍微不同 ...
- 阿里巴巴为什么不用 ZooKeeper 做服务发现?
阿里巴巴为什么不用 ZooKeeper 做服务发现? http://jm.taobao.org/2018/06/13/%E5%81%9A%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8 ...
- 使用Consul做服务发现的若干姿势
从2016年起就开始接触Consul,使用的主要目的就是做服务发现,后来逐步应用于生产环境,并总结了少许使用经验.最开始使用Consul的人不多,为了方便交流创建了一个QQ群,这两年微服务越来越火,使 ...
- Consul做服务发现
使用Consul做服务发现的若干姿势 https://www.cnblogs.com/bossma/p/9756809.html 从2016年起就开始接触Consul,使用的主要目的就是做服务发现,后 ...
- Go | Go 使用 consul 做服务发现
Go 使用 consul 做服务发现 目录 Go 使用 consul 做服务发现 前言 一.目标 二.使用步骤 1. 安装 consul 2. 服务注册 定义接口 具体实现 测试用例 3. 服务发现 ...
- Api网关Kong集成Consul做服务发现及在Asp.Net Core中的使用
写在前面 Api网关我们之前是用 .netcore写的 Ocelot的,使用后并没有完全达到我们的预期,花了些时间了解后觉得kong可能是个更合适的选择. 简单说下kong对比ocelot打动我的 ...
- etcd学习(3)-grpc使用etcd做服务发现
grpc通过etcd实现服务发现 前言 服务注册 服务发现 负载均衡 集中式LB(Proxy Model) 进程内LB(Balancing-aware Client) 独立 LB 进程(Externa ...
- go-micro使用Consul做服务发现的方法和原理
go-micro v4默认使用mdns做服务发现.不过也支持采用其它的服务发现中间件,因为多年来一直使用Consul做服务发现,为了方便和其它服务集成,所以还是选择了Consul.这篇文章将介绍go- ...
- RabbitMQ入门:主题路由器(Topic Exchange)
上一篇博文中,我们使用direct exchange 代替了fanout exchange,这次我们来看下topic exchange. 一.Topic Exchange介绍 topic exchan ...
随机推荐
- SVG Tutorials
SVG Tutorials W3C https://www.w3.org/TR/SVG11/ https://www.w3.org/TR/SVG2/ https://www.w3.org/TR/svg ...
- 2. Vue语法--插值操作&动态绑定属性 详解
目录 1. 设置vue模板 2. vue语法--插值操作 3. 动态绑定属性--v-bind 一. 设置vue模板 我们经常新建一个vue项目的时候, 会写如下的一段代码 <!DOCTYPE h ...
- C#关于panle重叠
当多个panle重叠不显示不出来的时候 选择视图---------------其它窗口----------------文档大纲 然后拖动进行调整
- oracle中关键字的执行顺序
执行顺序: from where group by having select order by ******当having/select 中出现组函数,那么其他没有被组函数修饰的列就必须出现下gro ...
- 鸿蒙开源第三方组件——进度轮ProgressWheel
目录:1.前言2.背景3.组件功能展示4.Sample解析5.Library解析6.作者系列文章合集 前言 基于安卓平台的进度轮组件ProgressWheel(https://github.com/A ...
- jQuery编写组件的几种方式
原文链接:https://w.cnblogs.com/xiao-xi/p/8572471.html 三种方式: 1.通过$.extend()来扩展jQuery 2.通过$.fn 向jQuery添加新的 ...
- linux系统导出随笔
导出时,不要用root用户忽略某张表的命令(多张表则直接往后加即可) --ignore-table=firewall_8088.operate_history --ignore-table=firew ...
- Using Sqoop to import from db2 to hadoop
参考 : https://stackoverflow.com/questions/23933481/db2-data-import-into-hadoop sqoop import - ...
- MarkDown简单语法回顾
写在前面: 本文是我的第一篇博客文章,希望与大家共同交流.分享我们热爱的技术,努力成为一名优秀的软件工程师! 进入正文 使用MarkDown记笔记已经有些时候了,编辑器是使用的sublime text ...
- vs调试qt代码,无法单步调试
在使用vs调试qt代码时,可以编译但无法单步调试QT源码.报错缺少qmain_win.cpp或者其他q******.cpp文件. 1.因为安装qt时没有安装qt源码库,重新下载一个src源码就可以了. ...