golang 实现蓝牙体征监测设备数据上报及存储的频率控制

更新日期: 2022-12-27 阅读次数: 2902 字数: 989 分类: golang

需求背景

蓝牙体征检测设备的初始上报频率非常高,单台每秒 370 个数据包。 需要通过通过服务器向蓝牙网关下发禁止波形数据的指令,禁用掉无用数据的上报。

有两个禁用策略:

  • 定时下发禁用指令。例如每十秒
  • 收到波形数据时,就立即下发禁用指令

显然第二种方式更合理,而且在有多台设备接入的情况下,也方便批量下发(根据 mac 地址)。

但是,这里就出现了一个频率控制问题,就是从下发指令,到禁止成功,是有一个时间间隔的。这个时间间隔内,要规避重复下发指令。

同时,还有另外一个需求,就是对写入数据进行控制,设备方的体温上报频率过高,增加了存储成本,所以同样需要限制。

找到一个 golang 的官方库,可以方便的实现这个频率控制功能:

go get golang.org/x/time/rate

配合 Golang Gin 实现接口限速,可以参考这里

类似场景:IP 限速

https://medium.com/@pliutau/rate-limiting-http-requests-in-go-based-on-ip-address-4e66d1bea4cf

原理就是:

使用 rate 库,每个 ip 对应一个 limiter。对应的这里使用 mac 地址作为 key。

内存控制

如果不断的加 IP,如何控制总内存。

限制 IP 总量,当超过时,清掉长期不用的。虽然目前的应用场景,设备总量可控,而且不太可能超过 1000。

但,这个很容易变成一个漏洞。如果黑客模拟发包,导致 key 总量不可控,内存爆炸。。。是否可以像 nginx 一样,限制内存使用上限。

限速一秒一次

除了直接指定每秒产生的 Token 个数外,还可以用 Every 方法来指定向 Token 桶中放置 Token 的间隔,例如:

limit := rate.Every(1 * time.Second);
limiter := rate.NewLimiter(limit, 1);

https://www.cyhone.com/articles/usage-of-golang-rate/

如何测试

  • 一个高频的定时调用 (调用者)
  • 一个低频限速的函数(被调用者)

加锁是否会影响性能

频繁 RWMutex Lock 是否影响性能。

不必担心。RWMutex 是读写锁,同时,Lock 是加写锁,RLock 是加读锁,性能更加有保证了。

互斥锁 (sync.Mutex) 和读写锁 (sync.RWMutex)

https://geektutu.com/post/hpg-mutex.html

  • 互斥锁:互斥即不可同时运行。即, 使用了互斥锁的两个代码片段互相排斥,只有其中一个代码片段执行完成后,另一个才能执行。
  • 读写锁:保证读操作的安全,那只要保证并发读时没有写操作在进行就行。在这种场景下我们需要一种特殊类型的锁,其允许多个只读操作并行执行,但写操作会完全互斥。

TODO

  • [X] 收到波形数据包时,调用禁用函数,向网关下发禁用指令
  • [X] 禁用函数,使用统一 client id
  • [X] 禁用函数,接受一个结构体参数,里面包含体征蓝牙设备 mac 地址
  • [X] 结构体增加字段:体征设备 mac 地址
  • [X] 网关监听 mqtt topic 的名称规范,用于接受下发指令: 监听 topic: healthdata/gateway/2207027001
  • [X] 提取 mac 地址,并拼接下发指令。指令测试格式在单元测试文件里。
  • [X] 调整三种波形禁用指令的发送间隔, 100ms
  • [X] 在禁用函数内,增加频率控制的逻辑。限制一秒一次
  • [X] 修改测试设备,网关 mqtt topic
  • 数据写入时的频率控制 (优先级低,可以回去台式机上测试,再部署),每个指标一个 rate limiter 感觉太啰嗦了。不知道 influxdb 是否支持

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式