定速出队,需要个建议

forgaoqiang

有些服务是限制QPS的,那么如何设计一个系统A,对系统A请求全部进入队列,但是从队列出队保证是一定的QPS进行

这样就不会导致被请求的系统因为QPS超出限制导致的拒绝服务

1551 5 0
5个回答

xcsoft

漏桶?

  • forgaoqiang 2022-05-27

    leak bucket 是不错的算法 问题是如何用PHP具体实现有什么建议和参考么

  • xcsoft 2022-05-27

    可以考虑 模仿redis-queue? 消费时按照nitron的做法 每次消费后sleep指定时间, 但是只能开单进程了, 这样可以确保不超过最大qps, 入队的时候 是不会影响的

  • forgaoqiang 2022-05-29

    @xcsoft 这个主意也不错 简单有效
    也就是尽快出列,一秒内出到10个任务,那么开始等sleep(1秒 - 这秒内最初那个出队任务时间)时长,然后继续下一个出队
    如果每秒不超过10任务则不进行sleep 你是这个意思吧

nitron

简单实现就是consume时先sleep一哈?
不精确但至少不会超越最大QPS

  • forgaoqiang 2022-05-27

    问题在于 sleep的时候有入队的如何处理 理想状态是实现 每秒出出多少个

  • nitron 2022-05-27

    不影响push啊,只是pop出来的时候进行sleep,这样间接达到减慢pop速率的效果

  • 2548a 2022-05-27

    影响了,workerman不能用sleep的,用了之后进程会卡住处理不了任何东西的

  • nitron 2022-05-27

    嗷对,忘了这茬了
    那计算数量发延迟队列

  • forgaoqiang 2022-05-29

    @2548a @nitron 如果这个进程的主要任务就是将任务出列发送出去然后异步处理请求的回调 是不是就不是问题

tanhongbin

我写了一个不知道对不对,用redis有序集合实现的

  • forgaoqiang 2022-05-27

    愿闻其详,也是一个办法,我也想着配合redis的队列和集合实现,流控是关键,如何实现指定的QPS

  • tanhongbin 2022-05-27

    上代码吧
    /**

    • @Describe: 限制速率接口 利用redis有序集合
    • @param string $name 集合名称
    • @param int $time 时间 例如60秒内
    • @param string $count time 时间内限制的次数
    • @return bool
    • @Datetime: 2022-03-31 9:59
    • @Username: thb
      /
      static public function rate($name,$time,$count)
      {
      //获取当前时间 微秒 1648691146.836558
      $new_time = self::msectime();
      //限制开始时间
      $start_time = $new_time - $time
      1000;
      //限制时间内的成员数量
      $rate_count = Redis::zCount('sms:redis_queue:'.$name,$start_time,$new_time);
      //大于等于成员数量 返回fasle
      if($rate_count >= $count){
      //删除限制开始时间之前的请求
      Redis::zRemRangeByScore('sms:redis_queue:' . $name,0,$start_time-1);
      return false;
      }
      //将本次成员添加进入集合
      Redis::zAdd('sms:redis_queue:' . $name, $new_time,randstr(4));
      return true;
      }
      //返回当前13位时间戳
      static public function msectime() {
      list($msec, $sec) = explode(' ', microtime());
      return (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
      }

    也不知道对不对呀,返回fasle直接把数据推回队列,如果想保证数据顺序处理 ,可以放入队列的右边进去,不知道对不对,别瞎用

  • forgaoqiang 2022-05-27

    其实我的想法稍微有点邪恶 因为很多api可以免费使用但是限制qps 所以要是能够分到不同的账户上去调用 qps总量就上来了 redis通信本地还行 如果在远端 自身延迟就有较大的影响了

    我没完全看懂你的算法 ,你只是尝试将请求放到集合,如果重点考虑如何将内容按照QPS取出是不是更好 类似漏桶算法 后面的可以不断入队 但是我取出的时候按照指定速率
    你这个更像是如何入队,而不是出队

chaz6chez

不建议这样设计,原因如下:

  1. 如果限制出队,那么入队速率也应该限制,因为队列buffer是有上限的,消费跟不上生产,直接会把队列拉崩;

  2. 一般限流的服务接受方都会做限流响应,队列根据对应相应抛回队列做重试即可,或者抛入调度服务做延迟请求:如1小时内只消费一次;

当然如果想要实现这样的功能完全可以和限流一样做,消费者使用令牌桶进行消费,没有令牌了就抛回队列,消费者前置一个调度就好了。

  • forgaoqiang 2022-05-29

    亲 队列的功能就是在消费者能力不足的情况下设计的 这是队列存在的根本原因 条件1完全不成立
    你视乎没有理解我的业务需求,我是要实现定速出队,而不是消费者能力有多强则取任务有多快

  • chaz6chez 2022-05-30

    队列是为了异步任务,而不是消费能力不强,你说的业务场景是任务调度服务干的事儿

胡桃

滑动时间窗口算法

  • forgaoqiang 2022-05-29

    嗯 是的 我就是要实现这个效果 有什么好的php参考么 特别是能和workerman配合的

  • 胡桃 2022-05-30

    这种东西网上一大把,知晓原理很容易实现,基于 redis sorted set 可实现限速,追求性能用共享内存,队列就更不用我说了。建议就是这么多,现成的代码我就没有了。

年代过于久远,无法发表回答
×
🔝