需求
问题
- 直播系统会每隔 5 分钟将每条流的带宽等信息汇报到 CDN 的边缘节点上,如果我们将每条流的上报都直接转发给后端系统,那么请求数就会成倍的增加,后端系统会承受不住。所以我们需要做的就是将数据聚合后再进行汇报,或者达到一定的量,比如 1M,或者是 5 分钟时间达到。
- 目前边缘节点的业务是用 ngx_lua 实现的,最先想到的方案就是通过
ngx.shared.dict
来实现。
方案
- 我最早想到的方案是设置 1m 的共享内存,每次直播系统 post 请求过来的时候尝试往同一个 key 的共享内存里追加数据,采用
safe_set
的方式,如果返回 “no memory” 的话就说明共享内存已经满了,就可以取出数据给后端系统发数据了。但是这里有个问题是每次我更新这个 key 的数据时都需要先进行 decode 再 encode,而 decode 和 encode 的操作是比较耗 cpu 的,直播系统在 5 分钟到点的时候就会让 nginx 的 cpu 走高,影响正常的业务请求。所以第一个方案被我否定了。
- 第二个方案自然想着要避开瞬间的 decode 和 encode,于是我想到针对每次的请求设置不同的 key,这样每次直播系统 post 过来的时候只需要 safe_set 就行了,但是当请求的流特别多的时候,那 key 就相应的增加了,当需要向后端系统发送请求的时候,此时需要遍历所有的 key,然后取出值组装后再发给后端系统。如果 key 特别多的话,遍历的时候就会阻塞该共享内存一定时间导致请求失败。
- 第三个方案就是采用
lpush
和 lpop
来进行存取,每次的 lpush
和 lpop
都是针对同一个 key 进行的,而且也不需要频繁的进行 decode 和 encode,正好可以用在这个方案中。不过 lpush
和 lpop
是 ngx_lua 0.10.6 后才支持的,唯一要做的就是适配好 nginx 和 ngx_lua 的版本兼容问题。
实现
- 上述方案对比后发现第三个方案比较合适,至于实现就比较简单了。5 分钟的定时任务就通过
ngx.timer.at
来做;buffer 的方案就通过 lpush
和 lpop
来实现了。