到目前为止,我一直专注于如何让消息进出消息代理,也就是RabbitMQ。

实际上,我们可以继续使用 RabbitMQ 和它的 Exchanges 来连接这个应用程序的其他部分,但是我想探索一个稍微不同的模型:我想使用协调器来跟踪哪些类型的消费者得到消息通知。

这样的话,我断开了传感器数据生成器和数据使用者之间的连接。

同时为了处理这些数据通信,我决定使用事件(event)来通知用户系统中正在发生的事情,并让他们决定是否要处理数据。

其原理大致如下:

  1. 在协调器内部,我们有构建好的 QueueListener。

  2. 我还需要构建另外一个类型,我叫它 EventAggregator。

  3. 来自RabbitMQ 的消息,它将通过一个异步的goroutine 进入QueueListener

  4. goroutine 将把消息传输到一个事件对象(event object)中,并通过事件聚合对象(event aggregation object)进行广播。

  5. 该对象将维护任何对事件感兴趣的使用者的注册表,并向其发送事件对象的副本。

  6. 这使我们能够通过将数据转储到下游的 Queue 来为这些事件注册其他应用程序,但它也可以让使用者能够在协调器内部进行设置,例如日志系统。

  7. 最后,如果使用者最终要通过 Queue 将数据发送到另一个应用程序,则可以对其进行预处理,以添加有用的附加数据,而最终使用者不必知道这些附加信息是如何到达那里的。

编写代码

创建 EventAggregator

在 coordinator 目录下添加 eventaggregator.go,代码如下:

  1. 第 28 行,建立 EventData struct,目前它的字段碰巧和 SensorMessage 是一样的,但是两个 struct 的职责不同,所以我们不复用 SensorMessage,而是单独建立 EventData,以便它们以后可以独立的进化;

  2. 第 5 行,建立了 EventAggregator struct,也就是事件聚合,它只有一个 listeners 字段,是一个 map,它的 key 是事件的名称,它的值是回调函数的集合。当事件发生的时候,EventAggregator 就轮流调用为该事件注册的回调函数;

  3. 第 9 行,就是 EventAggregator 的构造函数;

  4. 第 16 行,AddListener 方法,使用者通过该方法可以向 EventAggregator 注册回调函数;

  5. 第 20 行,PublishEvent 方法用来发布事件。它接收事件名称和事件的数据作为参数。这里需要判断 EventAggregator 里是否已经注册了该事件,如果注册了,那么遍历其对应的回调函数,并使用事件数据进行调用。

    1. 调用回调函数时,使用的不是 EventData 的指针,而是 EventData 的副本,这可以保证使用者不会把事件数据搞乱,影响其它使用者

  6. 取消订阅的功能我就不做了。

把 EventAggregator 连接到 QueueListener

打开 queuelistener.go,添加代码:

  1. 第19 行,在QueueListener struct 里面添加字段ea,类型是 *EventAggregator;

  2. 第 25 行,在 QueueListener 的构造函数里为 ea 自读赋初始值。

在 AddListener 方法里,原来只是把原始数据打印到控制台。现在添加如下代码:

  1. 创建一个 EventData,其字段内容目前和传感器的消息内容一样;

  2. 使用     QueueListener 上的 EventAggregator 发布事件:

    1. 事件的名称是 MessageReceived_传感器名称

    2. 第二个参数就是事件数据

发现早已运行的传感器

最后我们要做的就是如何让协调器发现在协调器上线前就已经在运行的传感器。

目前我们的做法是这样的:首先协调器先运行,然后传感器在上线的时候立即把它们的数据Queue 发送过去,使用的是 Fanout Exchange,这样多个协调器都可以被通知到。

但是,如果传感器先运行,协调器后运行,那么协调器就无法知道传感器的存在,为了解决这个问题,我这样做:

  1. 我在消息代理中也就是 RabbitMQ 里,建立一个新的 Exchange,它是一个 Fanout Exchange,它和其它信息流的方向正好相反。

  2. 在这里,协调器将会向这个 Fanout Exchange 发出一个“发现”请求,这个信息将会发送给所有的传感器。

  3. 传感器接收到这个“发现”请求信息后,将会响应,将它们的数据 Queue 的名称发送给我们以前建立的那个 Fanout Exchange(中间黄色的)。

    1. 这里会出现一些冗余的信息,但协调器里有过滤机制,所以就这样吧。

我们首先测试一下先运行传感器项目,再运行协调器项目的效果:

可以看到,协调器运行起来以后,没有接收到该传感器的数据。

修改 queuetools

我们要解决的就是这个问题,下面看代码,首先看 queuetools.go:

这里改动不多,就是把要新建立的 Fanout Exchange 的名称作为常量存在这里。

注意之前在这里定义的 SensorListQueue 已经不需要了,可以删掉。

修改 queuelistener

然后看 queuelistener.go,在这里为 QueueListener 添加一个DiscoverSensors 方法:

该方法中首先我使用了 ExchangeDeclare 方法来声明这个新的 Exchange,并进行设置。

虽然项目中还没用过这个方法,但是里面大多数参数的作用你应该能够猜得出来:

  1. name:Exchange 的名称

  2. kind:Exchange 的类型,可以是 direct、topic、header 或者 fanout,这里使用 fanout

  3. durable:表示这个 Exchange 是否可持久

  4. autoDelete:表示在没有绑定的情况下是否删除 Exchange

  5. internal:这个参数我们还没见过,如果想拒绝外部的发布请求,就把这个设为 true。这可以在高级场景中使用,在高级场景中,Exchange 绑定在一起,在消息代理中形成更复杂的拓扑。

  6. noWait 和 args 就不介绍了。

现在,协调器可以向这个 Exchange 发布消息了。而我们只需要向它发送一个消息即可,并没有什么具体的内容要发送,所以我发布了一个空的 Publishing,这就可以告诉浏览器我在寻找它们了。

修改传感器

下面我们让传感器(sensor.go)对上面发布的“发现”请求进行响应,不过首先,需要重构一下。

把 main 函数里面当传感器上面时,发布数据 Queue 名称那部分代码提取出来放在单独的一个函数里面:

然后在 main 函数相应的位置进行调用:

  1. 第 39 行,对重构的函数进行调用。

  2. 第 41 行,创建一个 Queue

  3. 第 42 行,使用 QueueBind 方法将这个 Queue 和 SensorDiscovery Exchange

  4. 第 48 行,创建goroutine 运行一个将要新建的函数 listenForDiscoveryRequests。通过使用 goroutine,无论当请求什么时候进来,这部分逻辑都将可用,而且不会阻塞系统的其余部分。这里需要传入 Queue 的名称和 Channel。

然后看一下 listenForDiscoveryRequests 函数:

这里使用 Channel 的 Consume 方法对 Channel进行设置以便能接收“发现”请求。

然后用 for range 来接收“发现”请求。这里忽略消息本身即可,因为该消息就是一个触发而已。当消息进来时,调用刚刚重构出来的 publishQueueName 函数即可。

在 queuelistener 里调用发现方法

在 queuelistener.go 的 ListenForNewSource 方法里,在如下位置调用 DiscoverSensors 方法:

为什么在这里调用?因为这是可以保证协调器正在监听传感器路由的消息的第一个地方。

运行测试

先运行一个传感器,然后在运行协调器:

传感器这里我使用了 freq 参数,让其每两秒钟生成一个数据。

可以看到,在这种情况下协调器也可以发现已经运行的传感器并接收数据了。

你可以运行多个传感器和多个协调器,应该也会好用的。

这也是一种非常简单的分布式应用吧。

 
 

RabbitMQ 入门 (Go) - 5. 使用 Fanout Exchange 做服务发现(下)的更多相关文章

  1. RabbitMQ 入门 (Go) - 4. 使用 Fanout Exchange 做服务发现(上)

    到目前为止,我们项目的结果大致如下: 传感器生成的模拟数据(包含传感器名称.数据.时间戳)是通过传感器在运行时动态创建的 Queue 来发送的.这些 Queue 很难直接被发现. 为了解决这个问题,我 ...

  2. 阿里巴巴为什么不用 ZooKeeper 做服务发现?

    阿里巴巴为什么不用 ZooKeeper 做服务发现? http://jm.taobao.org/2018/06/13/%E5%81%9A%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8 ...

  3. 使用Consul做服务发现的若干姿势

    从2016年起就开始接触Consul,使用的主要目的就是做服务发现,后来逐步应用于生产环境,并总结了少许使用经验.最开始使用Consul的人不多,为了方便交流创建了一个QQ群,这两年微服务越来越火,使 ...

  4. Consul做服务发现

    使用Consul做服务发现的若干姿势 https://www.cnblogs.com/bossma/p/9756809.html 从2016年起就开始接触Consul,使用的主要目的就是做服务发现,后 ...

  5. Go | Go 使用 consul 做服务发现

    Go 使用 consul 做服务发现 目录 Go 使用 consul 做服务发现 前言 一.目标 二.使用步骤 1. 安装 consul 2. 服务注册 定义接口 具体实现 测试用例 3. 服务发现 ...

  6. Api网关Kong集成Consul做服务发现及在Asp.Net Core中的使用

    写在前面   Api网关我们之前是用 .netcore写的 Ocelot的,使用后并没有完全达到我们的预期,花了些时间了解后觉得kong可能是个更合适的选择. 简单说下kong对比ocelot打动我的 ...

  7. etcd学习(3)-grpc使用etcd做服务发现

    grpc通过etcd实现服务发现 前言 服务注册 服务发现 负载均衡 集中式LB(Proxy Model) 进程内LB(Balancing-aware Client) 独立 LB 进程(Externa ...

  8. go-micro使用Consul做服务发现的方法和原理

    go-micro v4默认使用mdns做服务发现.不过也支持采用其它的服务发现中间件,因为多年来一直使用Consul做服务发现,为了方便和其它服务集成,所以还是选择了Consul.这篇文章将介绍go- ...

  9. RabbitMQ入门:主题路由器(Topic Exchange)

    上一篇博文中,我们使用direct exchange 代替了fanout exchange,这次我们来看下topic exchange. 一.Topic Exchange介绍 topic exchan ...

随机推荐

  1. eyes protect app

    eyes protect app https://awaremac.com/

  2. macOS open url from terminal

    macOS open url from terminal open URL && start terminal bash open url in chrome open chrome ...

  3. PWA & TWA

    PWA & TWA https://www.bilibili.com/video/av68082979/ Service Worker workbox.js https://developer ...

  4. css & clip-path

    css & clip-path https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path https://tongqu.me/ tw ...

  5. 详解支付体系颠覆者NGK公链:如何通过呼叫河马智能合约加速转账?

    纵观全球加密货币市场,至今为止,全球已经发行的加密货币以及数字代币的数量已经超过了7000种,且未来还将会有更多的加密货币或数字代币出现.在众多加密货币项目中,投资者很难在众多的项目里甄别项目的好坏以 ...

  6. 冷饭新炒:理解JWT的实现原理和基本使用

    前提 这是<冷饭新炒>系列的第五篇文章. 本文会翻炒一个用以产生访问令牌的开源标准JWT,介绍JWT的规范.底层实现原理.基本使用和应用场景. JWT规范 很可惜维基百科上没有搜索到JWT ...

  7. Python 与 excel的简单应用

    1.pip openpyxl库: pip install openpyxl -i http://pypi.douban.com/simple --trust-host pypi.douban.com ...

  8. webpack4.X源码解析之懒加载

    本文针对Webpack懒加载构建和加载的原理,对构建后的源码进行分析. 一.准备工作 首先,init之后创建一个简单的webpack基本的配置,在src目录下创建两个js文件(一个主入口文件和一个非主 ...

  9. Maven遇到的各种问题

    1.遇到报错-Dmaven.multiModuleProjectDirectory system propery is not set.Check $M2_HOME environment varia ...

  10. 将VMware虚拟机最小化到托盘栏

    版权:本文采用「署名-非商业性使用-相同方式共享 4.0 国际」知识共享许可协议进行许可.   目录 前言 将VMware最小化到托盘栏的方法 1.下载 Trayconizer 2.解压 trayco ...