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 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式