令牌桶算法实现思路小记

一、背景

近期公司部分接口遭到有意的刷接口,对服务本身造成的影响倒还不大,但产生了大量脏数据,主要类似评论、关注、私信等数据对正常用户产生了不好的体验,单纯的借助sentinel流控,甚至设置热点参数都并不能满足我们的需求,产品期望的是能识别恶意的刷子后,直接对账号进行封禁或做其他操作。
除接了第三方的敏感词库外,为了应对问题用户层出不穷的尝试,还是需要一套算法来进行将这样的用户进行识别,能想到的有两个不同的方向:
1.自建敏感词库,通过一系列算法(同义词替换、回译、随机交换、基于上下文信息增强等)来增强/训练样本,通过敏感词识别出来问题用户。
2.通过流控算法来识别出疑似用户,提给运营去决策是否封禁。
方向一成本过高,且至少对我自己而言,有门槛和研究成本,产品期望当天就能上线,就没选用这一套;最终选用了方向二。

二、最终实现思路示意

常见的流控算法有两种:漏桶和令牌桶。
顾名思义,漏桶就是以恒定的速率限流,强行限制了访问速率,而令牌桶则是提前在桶内放置了一些令牌,每次访问取走一个令牌,同时以恒定的速度添加令牌,只有当用户取不到令牌时,则拒绝访问。这块网上相关资料很多,不做赘述。
删去和业务相关的敏感信息后,仅简单示意下最终选用的令牌桶如下:


令牌桶 (1).jpg

这里其实有个关键问题在于,怎么实现【以恒定的速度放令牌】,网上有些实现是单起一个线程,持续的往里放,但因为基于用户维度的话,要维护的桶很多,且不想单起线程,最终实现时是按如下思路实现(仅列举思路,不贴代码):
【1】桶的缓存对象中,也增加一个下次刷新时间的时间戳,一并存入,初始化时就计算【下次刷新桶的时间】(当前时间戳 + 每隔多少毫秒需要放入1个令牌的毫秒数)

【2】请求进来时,如果当【当前时间】还未达到【下次刷新桶的时间】,则仅扣减令牌

【3】请求进来时,如果当前时间已经超过了下次刷新桶的时间,则在扣减令牌前,还需要计算应该往桶里加的令牌数((当前时间-桶上记得刷新时间)/每隔多少毫秒需要放入1个令牌的毫秒数 + 1),同时也更新【下次刷新桶的时间】,并扣减令牌(对桶的查和写需要防并发,这里不赘述)

PS.这里只记录对令牌桶的相关操作,如果取一堆用户随机取用户id来绕过降低单个用户的访问频率绕开限制的,我们这边有其他相关策略,在这里不做讨论,不赘述,只描述一下不涉及业务的技术方案。上了几天,貌似没啥问题,但自己时不时也会考虑遗漏,如果逻辑有疏漏,欢迎路过大佬指正

你可能感兴趣的:(令牌桶算法实现思路小记)