从本节开始,我介绍一下如何将相关数据持久化到数据库,也就是上图中蓝色的部分。

目前的问题

我先运行 6 个传感器和2 个协调器,这里我使用了批处理文件:

运行后,看一下 RabbitMQ 的管理控制台:

注意上面前面几个 Queue,这些 Queue 就是我们让传感器和协调器监听那两个 Fanout Exchange 时创建的,因为这两个 Exchange 不使用路由 Key 来决定接收者,我使用了空字符串“”作为这些 Queue 的名称,而RabbitMQ 就会为它们赋予一个唯一的名字。

因为目前创建的 Queue 都是临时的,如果我重新启动系统,RabbitMQ会创建另一套不同的 Queue 来完成工作,这样的话系统资源就会被慢慢的耗尽,所以这个问题需要解决。

调整 autoDelete 参数

首先修改 tools 包下的 queuetools.go 里面的GetQueue 函数,添加一个 autoDelete 参数:

GetQueue 函数会确保创建一个Queue 从而能接收到消息。刚创建它的时候,我的意图是让它作用于 Direct Exchange 和命名的 Queue。后来我对它进行了扩展使用,也可以应用于匿名的 Queue。

再说一下 autoDelete 参数的作用是:若值为 true,那么如果一个 Queue 它没有被注册任何的使用者,这个 Queue 就会被删除。针对上述问题中的临时 Queue,这就是我想要的效果。但是针对传感器的数据 Queue,我还是希望在系统重启后,这些 Queue 能够保留。

所以,我为该函数添加了一个 autoDelete 参数,在创建 Queue 的时候,可以对 autoDelete 进行设置。

有三个调用该函数的地方需要调整代码,先打开 sensors.go:

  1. 针对传感器传送数据的 Queue,我要让它能够保留下来,所以 autoDelete 就是 false

  2. 而 discoveryQueue 是用来监听协调器的“发现”请求的,我想让每个传感器每次上线都会得到一个新的 Queue,这里 autoDelete 就设置为 true,这样的话 RabbitMQ 就会把旧的 Queue 自动清理掉。

调整 queuelistener.go 里面的调用:

这里得到的临时 Queue 是用来监听传感器上线时或响应协调器发现请求时来发布数据 Queue 名称的。

这里函数调用的 autoDelete 参数也设置为 true,从而让它们可以自动被清除掉。

测试运行

把之前的 Queue 都删掉:

然后再运行 5 个传感器和2 个协调器:

现在又是很多的 Queue。

然后我们再停掉所有的传感器和协调器:

可以看到传感器传送数据的 Queue 被保留了,而其它的临时 Queue 都自动删除掉了,这就是我们想要的效果。

泛化事件数据

到目前为止,系统中只发布了一种类型的事件(接收到传感器数据时的事件),而且目前还没有任何使用者监听这个事件。接下来我们就要完善事件这部分功能了,但首先必须做出一些优化修改,以便能真正满足需求。

目前 eventaggregator.go 里面包含了所有添加监听者以及向监听者发布事件的方法。

但现在的情况是事件的使用者也知道如何自行发布事件,这点不太好,因为它们不需要这样做。代码修改如下:

  1. 为了尽量少的暴露功能,我为事件的使用者创建了 EventRaiser 这个接口,它里面只有一个 AddListener 方法,与已经实现的 AddListener 方法相几乎完全匹配。

  2. 但是我把接口里     AddListener 的第二个参数,也就是回调函数里面的参数类型改为了 interface{},从而可以接收多种类型的数据。

  3. 相应的,后边所有涉及事件数据参数的地方都改为     interface{}

现在 EventAggregator 被泛化了,我也可以发布其它类型的事件了。

来到 queuelistener.go,我想在协调器发现数据源之后,发布一个事件:

这个事件的名称叫做 DataSourceDiscovered,事件数据就是 Queue 的名称,由于这个参数的类型是 interface{},所以它可以正常的传递进去。

创建数据的使用者

目前,我们整个系统的设计一共有三层,而数据源和数据的使用者是通过协调器分开的。这样做的好处是,关于如何处理消息的业务逻辑都集中在协调器这一层上面了,而数据源和数据使用者层仅关注它们自身的任务即可。

为了达到这个目的,需要在 coordinator 目录下创建一个 databaseconsumer.go 文件:

这个文件的作用是监听整个系统发出的事件,并决定哪些事件可以转发到数据管理包(我一会要建立的)。

dataconsumer.go

首先看一下 dataconsumer.go 文件的内容:

  1. 第 15 行建立 DatabaseConsumer struct,它有5个字段:

    1. 第一个字段类型是 EventRaiser 接口,该接口只能用于监听,而不能发布事件,这就是该接口的目的。

    2. 接下来三个字段都是与 RabbitMQ 相关的。

    3. 最后一个字段是注册的监听器的 Queue 名称的集合。

  2. 第 23 行,为 DatabaseConsumer 创建一个构造函数。它接收 EventRaiser 作为参数,并创建 RabbitMQ 相关的连接、Channel、Queue 为 DatabaseConsumer 各字段赋值。

  3. 第 29 行创建 Queue 时用到了一个 Queue 的名称,这个 Queue 是用来做持久化的,它是众所周知的,它的名称存放在 queuetools.go 文件里:

  1. 第 31 行就是监听数据源被发现的事件,回调函数的参数类型是空接口(其实就是事件的名称)。在回调函数内,调用我随后要建立的 SubscribeToDataEvent 方法,把 eventData 转化为字符串传递进去。

下面看一看 SubscribeToDataEvent 方法:

  1. 该方法的参数是事件的名称。

  2. 第 39 行,对已注册的监听器进行遍历,如果传进来的事件名称已注册,return 即可。

  3. 否则的话,需要注册这个数据源,这个事件的名称是     MessageReceived_+Queue 的名称。

  4. 第 45 行的回调函数,我将传入一个立即执行的匿名函数,它会返回我们真正需要使用的回调函数,也就是闭包。这种做法的好处就是返回的函数可以捕获其被定义的作用域内的变量,这样的话真正的回调函数就可以拥有一些可持续的“状态”(也就是     prevTime 和 buf)。这里我的需求是至少要间隔 5 秒钟以上,才记录一次(到数据库)。

  5. 回调函数内其它的逻辑都很简单,就不逐行介绍了。

  6. 第 67 行,发布消息使用的是 Default Exchange,并路由到持久化的那个 Queue。

修改 queuelistener.go 里面的构造函数

让其传入 EventAggregator 作为参数并赋值给 QueueListener 的 ea 字段。

修改协调器的 main 函数

  1. 创建包级共享的 DatabaseConsumer 变量,在 main 里用构造函数进行创建并赋值。

  2. 创建     EventAggregator,并传递给 DatabaseConsumer 和     QueueListener,让他们共享同一个 EventAggregator。

RabbitMQ 入门 (Go) - 6. 数据持久化(上)的更多相关文章

  1. RabbitMQ 入门 (Go) - 7. 数据持久化(下)【完】

    数据库 我使用的是 PostgreSQL. 使用的驱动是 github.com/lib/pq 这个网址 https://pkg.go.dev/github.com/lib/pq 是官方文档. 创建数据 ...

  2. Named Volume 在 MySQL 数据持久化上的基本应用

    原文作者:春哥 初识春哥时,春哥是美术设计大咖.后不久,创业并致力于游戏开发,已有3年.从Unity3D到IOS(Swift)开发,从前端开发到后端以及容器技术,从设计开发测试到产品经理以及CEO,已 ...

  3. RabbitMQ 入门系列:3、基础含义:持久化、排它性、自动删除、强制性、路由键。

    系列目录 RabbitMQ 入门系列:1.MQ的应用场景的选择与RabbitMQ安装. RabbitMQ 入门系列:2.基础含义:链接.通道.队列.交换机. RabbitMQ 入门系列:3.基础含义: ...

  4. 【spring boot】9.spring boot+spring-data-jpa的入门使用,实现数据持久化

    spring-data-jpa官方使用说明文档:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/ spring-d ...

  5. Flutter数据持久化入门以及与Web开发的对比

    对于大部分安卓或者IOS开发人员来说,App的数据持久化可能是很平常的一个话题.但是对于Web开发人员来说,可能紧紧意味着localStorage和sessionStorage. Web开发 loca ...

  6. openshift 容器云从入门到崩溃之七《数据持久化》

    数据持久化常用的有两种: hostPath 挂载容器宿主机的本地文件夹,直接修改pod的配置 volumes: - hostPath: path: /data/logging-es type: '' ...

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

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

  8. Sentinel上生产环境只差一步,监控数据持久化

    之前介绍了Sentinel相关的文章,小伙伴在生产实践中不知道有没有这个疑问?我们的Sentinel控制台监控的数据只能看最近5分钟的,如图 那么就导致历史数据是查看不了的,那肯定是不行的,在生产环境 ...

  9. Java EE数据持久化框架 • 【第1章 MyBatis入门】

    全部章节   >>>> 本章目录 1.1 初识MyBatis 1.1.1 持久化技术介绍 1.1.2 MyBatis简介 1.1.2 Mybatis优点 1.1.3 利用Mav ...

随机推荐

  1. VSCode & disable telemetry reporting

    VSCode & disable telemetry reporting https://code.visualstudio.com/docs/supporting/faq#_how-to-d ...

  2. XML & XPath & XQuery

    XML & XPath & XQuery full XPath demo https://www.w3.org/TR/xpath-full-text-30/ https://www.w ...

  3. NGK:价值对标比特币,上线暴涨4558%,下一个财富暴增风口

    近期,美股行情多变,一直饱受争议的比特币也成了其中的"弄潮儿".看多者认为,机构的兴趣有助于支撑比特币作为对冲美元疲软和通胀的工具. 特别是今年1月底的时候,马斯克将推特简介更改为 ...

  4. 谁能成为数据储存领域领头羊?永久数据存储--NGK的终极使命!

    区块链的目的是永远存储交易网络的历史.NGK技术团队能够永久存储其去中心化账本的副本.这是其日后能进行审计关键.一些著名的团队,如Solana和SKALE,现在正在为此与NGK进行最后的集成,我们预计 ...

  5. 1004 Counting Leaves ——PAT甲级真题

    1004 Counting Leaves A family hierarchy is usually presented by a pedigree tree. Your job is to coun ...

  6. hive分区分桶

    目录 1.分区 1.1.静态分区 1.1.1.一个分区 1.1.2.多个分区 1.2.动态分区 2.分桶 1.分区 如果一个表中数据很多,我们查询时就很慢,耗费大量时间,如果要查询其中部分数据该怎么办 ...

  7. 如何系统的了解Kafka

    1.概述 在大数据的浪潮下,时时刻刻都会产生大量的数据.比如社交媒体.博客.电子商务等等,这些数据会以不同的类型存储在不同的平台里面.为了执行ETL(提取.转换.加载)操作,需要一个消息中间件系统,该 ...

  8. PAT-1150(Travelling Salesman Problem)旅行商问题简化+模拟图+简单回路判断

    Travelling Salesman Problem PAT-1150 #include<iostream> #include<cstring> #include<st ...

  9. C++ 中的虚函数表及虚函数执行原理

    为了实现虚函数,C++ 使用了虚函数表来达到延迟绑定的目的.虚函数表在动态/延迟绑定行为中用于查询调用的函数. 尽管要描述清楚虚函数表的机制会多费点口舌,但其实其本身还是比较简单的. 首先,每个包含虚 ...

  10. SpringBoot启动流程分析原理(一)

    我们都知道SpringBoot自问世以来,一直有一个响亮的口号"约定优于配置",其实一种按约定编程的软件设计范式,目的在于减少软件开发人员在工作中的各种繁琐的配置,我们都知道传统的 ...