V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
cxytz01
V2EX  ›  程序员

需要设计一个 api 调用计量计费模块,没有什么成熟的思路。

  •  
  •   cxytz01 · 2022-06-16 12:09:26 +08:00 · 3722 次点击
    这是一个创建于 892 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,产品需要对 api 进行按次数收费,比如 1w 次 /1 元钱,或者每个月 40 元钱 4w 次这样,只是打个比方,价格不重要。

    目前就两个思路:

    一.每次 api 调用都同步的到数据库里面进行加 1 ,然后判断是否超过阈值,没超过放行,超过了限制。 这里面流程就长了:取得用户 ID ,判断用户 token 权限,对表字段进行加一,然后各种逻辑判断。 如果是网关级别的,会大大降低 api 性能。 优势是精确,劣势是性能低。

    二.每次 api 调用直接推到 mq 里面,后方进行 api 统计,异步将结果写入 cache 。 再次 api 调用查 cache ,超出阈值,限流。 优势是高性能,劣势是不精确。

    下面是两个业界的 api 收费方式: https://coinmarketcap.com/api/pricing/ https://cloud.tencent.com/document/product/628/39300

    请教大家,有没有什么更好的解决方案?

    15 条回复    2022-06-17 08:02:11 +08:00
    sadfQED2
        1
    sadfQED2  
       2022-06-16 12:15:51 +08:00 via Android
    用户剩余次数同步到 redis 里面,配合 nginx-redis 插件,直接在网关层处理
    theohateonion
        2
    theohateonion  
       2022-06-16 12:17:44 +08:00
    一般都是 2 ,还要考虑丢数据,重算,对账等场景。
    blackboom
        3
    blackboom  
       2022-06-16 12:30:19 +08:00
    提供一个思路,可以看下开源网页统计实现方式。
    y830CAa5nink4rUQ
        4
    y830CAa5nink4rUQ  
       2022-06-16 12:47:26 +08:00
    1w 次 /1 元钱,或者每个月 40 元钱 4w 次

    这种对精确到要求不高,多出来的就当做是赠送了。
    guisheng
        5
    guisheng  
       2022-06-16 12:48:33 +08:00 via iPhone
    网关过滤器呢 每个用户颁发一个密钥通过 API 携带。redis 控制
    helone
        6
    helone  
       2022-06-16 12:55:15 +08:00   ❤️ 2
    其实大部分类似 api 调用的做法都是记录一个调用数,然后定时任务每分钟或者五分钟统计一下全量用户的使用情况,做一些关闭权限之类的操作,不过也可以更精细一些,检查的时候如果用户使用超过 80%或 90%,对这种用户检查频率更高一些
    Saxton
        7
    Saxton  
       2022-06-16 13:40:21 +08:00   ❤️ 1
    一般都是用第二种吧, 异步统计,就跟话费一样,你剩一块钱,假设 1G 一块,你在这几十秒中瞬间就用了 1G 以上,那是不是要做欠费处理呢,不用太纠结。
    gam2046
        8
    gam2046  
       2022-06-16 14:27:19 +08:00
    第二种可以满足绝大多数情况,如用用超了,可以挂账,即可调用数为负数,除非以后再也不用,否则都要先冲正。
    8rmEHZ8WhVHVOb0E
        9
    8rmEHZ8WhVHVOb0E  
       2022-06-16 14:49:35 +08:00
    双模式 设定阈值
    例如用户每分钟消耗 100 元,余额 10000 元,那余额大于 1000 元时走模式 2 ,余额次数小于 1000 元时走模式 1
    8rmEHZ8WhVHVOb0E
        10
    8rmEHZ8WhVHVOb0E  
       2022-06-16 14:58:04 +08:00   ❤️ 1
    阈值模式
    例如用户每分钟消耗 100 元,余额 10000 元,那余额大于 1000 元时走模式 2 ,余额次数小于 1000 元时走模式 1
    业务繁忙系统压力大时下调阈值 减少实时判断压力。系统服务能力充足时提高阈值,提高精准度。
    该方法可以做到系统压力和精准度的最大平衡 减少负余额 /次数超支的情况

    业务流程
    1 读取 redis ,判断是否有 到达阈值标识,有进入 2 ,无进入 3
    2 读取余额(剩余服务次数),判断是否继续提供服务,其他同 4
    3 提供服务
    4 队列异步扣取余额(剩余服务次数),扣取后入口余额到达阈值,redis 标识该用户到达阈值标识
    zeusho871
        11
    zeusho871  
       2022-06-16 22:07:19 +08:00 via Android
    用 redis 可能逻辑上容易实现
    night98
        12
    night98  
       2022-06-17 01:09:28 +08:00
    给你个最简单的方案
    meta 服务控制用户使用量数据,增减扔到 mysql ,秒级推送到 redis ,其他服务端启动拉取元数据,像你这种场景一般都是 to b ,初期直接全量拉 redis 的数据就行,然后根据你服务的机器数去除以用户可用的总数,拿到每个用户单独的计数量,扔到类似并发 map 里,这样就从 redis 扣减改成堆内扣减,性能提升一大层,扣减用 cas 类去扣,基本上随随便便百万吞吐,然后扣减不足情况下可以写 rpc 从其他服务借次数,设置限制最多调用三次,三次拿不到去 redis 看还有没有剩余,然后各个服务定期同步已用次数到 redis ,或者 meta 服务直接数据库统计次数推送到 redis ,服务监听并替换现有计数,做的好的话基本上 0 延迟
    night98
        13
    night98  
       2022-06-17 01:10:33 +08:00
    还有一种就最简单的,redis 集群,单个调用方调用次数顶天 qps2-3 千,直接 redis 扣减,服务重启重新同步库存到 redis ,redis 并发量随随便便几万抗住这个还是很轻松的
    i3x
        14
    i3x  
       2022-06-17 04:31:07 +08:00 via Android   ❤️ 4
    saas 从业者。。。给出我的解答:杜绝客户突发导致超额的方法。

    每个实例一组令牌桶。
    根据客户套餐不同,会有不同的补充速度,以及令牌桶上限。。

    例如客户购买了 1000 万次查询,配额限制 100qps ,突发允许 1000 。则令牌桶最大 1000 ,每秒补充 100 。。。这样就不会有超额问题。
    如果同步量较大,精度要求没那么高,也可以延长时间。
    同时如果地域广泛,服务器提供多入口多地区。可以设置多组。。。。直到总计数取完。。。各地区可能消耗完毕时间不一致。但是也差不多的。

    计费无非是按最终数据库记录的所有的已正确响应应该计费请求的总数。

    数据库肯定是记录每一次到达 api 后端的所有查询以及状态、处理结果的吧。。。
    至少保存了唯一 id ,时间,相应状态吧。这样对计费足够了。
    令牌桶防滥用。计费可以账单制,一分钟或者一个小时汇总一次查询记录计算费用就行了。


    处理量级:这套结构分散在不同的机房,总共也没多少算力,半个小时能处理 210M 次简单的 api 查询。。。测试的 api 数据细碎繁多但是逻辑简单。。。。。
    Abbeyok
        15
    Abbeyok  
       2022-06-17 08:02:11 +08:00
    用 redis.incrby
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2645 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 10:58 · PVG 18:58 · LAX 02:58 · JFK 05:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.