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 ...
随机推荐
- how to check SVG type in js
how to check SVG type in js SVGSVGElement & SVGElement svg = document.querySelector(`svg`); // & ...
- github & markdown & image layout
github & markdown & image layout css & right https://github.com/sindresorhus/log-symbols ...
- DOM & Shadow DOM & Virtual DOM
DOM & Shadow DOM & Virtual DOM What is the difference between Shadow DOM and Virtual DOM? ht ...
- js 上传图片,用户自定义截取图片大小
js 上传图片,用户自定义截取图片大小 js 组件
- 应该如何看待VAST的未来价格与价值?
提起数字货币的价格,很多币圈人士都是滔滔不绝,随口一举例,便是百倍千倍的数字货币.可是提起数字货币的价值,就很少有币圈人士能举出几个有力的例子,常常顾左右而言他,场面十分尴尬.之所以会这样,是因为很多 ...
- 《容器高手实战: Dockerfile最佳实践》
Dockerfile最佳实践一个容器对应一个进程一个Docker容器应该只对应一个进程,也就是一个Docker 镜像一般只包含一个应用的制品包(比如.jar). 在需要组合多个进程的场景,使用容器组( ...
- Python算法_递归:汉诺塔
游戏链接:https://zhangxiaoleiv.github.io/app/TowerOfHanoi/Hanoi.html 汉诺塔游戏算法: 1 def hanoi(n,x,y,z): 2 if ...
- Sqoop 数据迁移工具
Sqoop 数据迁移工具 sqoop : SQL to hadOOP 两个功能: 1. RDB 向HDFS导入 2. HDFS向RDB导入 注:拷贝mysql-connector.jar 和 json ...
- Codeforces 682C Alyona and the Tree
题目链接:http://codeforces.com/problemset/problem/682/C 分析:存图,用dfs跑一遍,详细见注释 1 #include<iostream> 2 ...
- 通达OA 越权访问-2013/2015版本
漏洞参考 http://wiki.0-sec.org/0day/%E9%80%9A%E8%BE%BEoa/9.html 复现 根据⽹上的通达 OA的源码找这些敏感地址,如: /general/syst ...