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 ...
随机推荐
- git merge all in one
git merge all in one you can follow below steps 1. merge origin/master branch to feature branch # st ...
- element-ui的树型结构图,带有复选框的,没有子项的,横排展示
// 修改树形图样式,如果不含有下箭头的块,要变成行内样式 treeChildInline(){ let hasCaretRight = $("#permission_panel" ...
- MacOS下PHP7.1升级到PHP7.4.15
最近写SDK的时候需要用到object类型提示符,PHPStorm智能提示说需要PHP7.2以上才能支持这种类型提示. 我一查我本机的PHP是7.1.30版本,于是考虑升级一下PHP版本. 首先要尝试 ...
- idea将文件push之后如何回退
- IO、NIO、BIO的区别
我们首先得明白什么是同步,异步,阻塞,非阻塞,只有这几个单个概念理解清楚了,然后在组合理解起来,就相对比较容易了. IO模型主要分类: 同步(synchronous) IO和异步(asynchrono ...
- spring boot +dubbo 踩坑记录
今天初次搭建spring boot +duboo的demo.记录一下踩坑记录. 首先搭建3个小demo,一个maven项目,两个spring boot (服务提供者和服务消费者)项目. 两 sprin ...
- Kubernetes-5.Pod资源控制器(1)
docker version:20.10.2 kubernetes version:1.20.1 本文概述Kubernetes Pod资源控制器的ReplicaSet.Deployment.Daemo ...
- 剑指 Offer 13. 机器人的运动范围 + 深搜 + 递归
剑指 Offer 13. 机器人的运动范围 题目链接 package com.walegarrett.offer; /** * @Author WaleGarrett * @Date 2020/12/ ...
- POJ-3268(来回最短路+dijkstra算法)
Silver Cow Party POJ-3268 这题也是最短路的模板题,只不过需要进行两次求解最短路,因为涉及到来回的最短路之和. 该题的求解关键是:求解B-A的最短路时,可以看做A是起点,这就和 ...
- Java跨平台原理与Java虚拟机(JVM)
Java跨平台原理(字节码文件.虚拟机) C/C++语言都直接编译成针对特定平台机器码.如果要跨平台,需要使用相应的编译器重新编译. Java源程序(.java)要先编译成与平台无关的字节码文件(.c ...