需求背景
蓝牙体征检测设备的初始上报频率非常高,单台每秒 370 个数据包。 需要通过通过服务器向蓝牙网关下发禁止波形数据的指令,禁用掉无用数据的上报。
有两个禁用策略:
- 定时下发禁用指令。例如每十秒
- 收到波形数据时,就立即下发禁用指令
显然第二种方式更合理,而且在有多台设备接入的情况下,也方便批量下发(根据 mac 地址)。
但是,这里就出现了一个频率控制问题,就是从下发指令,到禁止成功,是有一个时间间隔的。这个时间间隔内,要规避重复下发指令。
同时,还有另外一个需求,就是对写入数据进行控制,设备方的体温上报频率过高,增加了存储成本,所以同样需要限制。
找到一个 golang 的官方库,可以方便的实现这个频率控制功能:
go get golang.org/x/time/rate
类似场景: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 聊聊, 查看更多联系方式