Rate limiter + Datadog
感觉这个可能有用:
例如我们有这样一个表capture event的发生时间:
12:05:00 | 10 |
12:05:01 | 15 |
12:05:02 | 14 |
12:05:03 | 16 |
因为一小时有60分钟,所以存分钟为精度,然后一个list存每分钟发生的次数。
12:05 | 10,15,14,16... |
12:06 |
Senario:
根据请求的特征进行限制(feature选取, 例如:ip,user_id, etc)
无需太小的粒度,例如,半分钟内允许2次=》每分钟内4次。
Service:就一个service
Storage:这个是重点
数据不用持久化,例如你要limit5分钟内的,那么5分钟前的数据可以扔掉。
这个系统写和读都要搞笑,所以选in memory storage就ok啦
感觉这里有两种可考虑的存法:用memcache
以event+feature+timestamp作为key,然后呢,我们按精度分bucket来存。例如我要限制每分钟,每小时,每天的访问次数。就存3份,每份用比这个单位小时间来存,例如,我们精度选1分钟,那么就按照秒来存。每个格子代表1秒钟,总共60个格子;每小时60分钟,小时的bucket我们存60个格子,每个表示1分钟;每天24个小时,所以有24个格子,每格是1小时。
我们还需要把格子的ttl设成对应的长度,然后让cache处理过时的数据。
每次我们查询是否over limit的时候,就把规定的时间精度里的格子加一次,看是否超过,超过了,就alert。因为只有60个格子,所以linear加一次也不慢。另外一个需要注意的地方是读和写要atomic,别一边读一遍改了,这样就会出现race condition。但这也得看我们的limit是不是很hard的limit,如果可以容错,那么这个也不是特别大的问题。
听说cache可以bulk提取一堆key的value,因为memcache啥的始终要经过网络,虽然是内网访问速度快,但也是有延迟的。所以可以把key都生成好,然后bulk request一次把60个值都拿过来。
另外一种是用redis来存:
感觉可以用event+feature来做key,然后把发生的次数按秒钟append到list后面。读取的时候可以一条list,一次拿过来,那样的话就一次网络访问好了。看有没有over limit还是一条拿过来,逐个加和来看。这样存的话,感觉比较像算法题里的做法359 Logger Rate Limiter的rockset面试变种,得purge链表里的旧数据,可以在读或写的时候执行一下来purge。
下面是datadog/google analytic/scuba这类可以查看某段时间什么发生了多少次,有time serial的系统
Senario:
用户对某个链接访问次数
可以按最近X小时/X天/X月 etc
假设被请求约2k的QPS
Service:就一个service
Storage:这个是重点
这个系统是一个写很多,读很少的系统。因为每次用户点一下就要写一条记录
而且这个需要持久化,因为要按不同的时间来查数目,(L家的系统设计是查top k)
假设用noSql来存
key就是你要记录次数的东西,可以是网址,exception,某metric
value就访问/发生次数
我们还是想rate limiter那样分级存储。例如今天的数据每钟写一条,昨天的我们就存少一点,每5分钟写一条,上个月的,每天存一条以此类推。
我们这里tradeoff是,减低精度,省去存储空间
我们要定期跑脚本把旧数据瘦身,例如把昨天的集中一下,写成一条
具体存成这样:(越是靠近现在的存得越仔细)
时间 | 间隔 | 次数 |
2016/02/10 15: | 1h | 300 |
... | ||
2017/03/10 15:23 | 1m | 30 |
2017/03/10 15:24 | 1m | 39 |
... | ||
2017/04/01 14:39:50 | 1s | 3 |
2017/04/01 14:39:51 | 1a | 5 |
这个时候,当我们需要查询这个event在最近一个月里发生了多少次,我们就到数据库里按时间range查出最近一个月的row,然后加起来。因为我们有做数据瘦身,所以旧数据已经一定程度上加好了。
另外一个考虑是,怎样减少写操作。因为每次用户操作都会产生一堆metric,如果每次都往数据库写,会造成很多写。所以通常是在server上有一个小程序跑着,收集一段时间然后再打包发给这个server。这样就可以大大减少数据库的写了。
感觉L家的top k可以用Cassandra,row_key是那个要记录的event/exception,然后column key呢就是那些时间,这样就可以按照时间来range query啦。
Last updated