【问题标题】:Limit the number of processed messages from channel限制来自通道的已处理消息的数量
【发布时间】:2020-09-26 01:49:00
【问题描述】:

我每秒通过通道收到大约 200 000 条消息到我的工作人员,我需要将发送给客户端的消息数量限制为每秒 20 条。 这使它每 50 毫秒 1 条消息

在 LOOP 的帮助下,worker 将在整个程序生命周期内仍然活着(而不是为每条消息打开一个通道)。

我的目标: - 由于消息的顺序很重要,我想跳过在阻塞的 50 毫秒内收到的所有消息,只保存最新的消息 - 如果最新的消息在阻塞的 50 毫秒内出现,我希望在循环内的阻塞时间结束并且没有新消息到来时处理保存的消息!

我的策略 - 不断向同一频道发送尚未处理的最新消息

但它的问题是,如果该消息是在(来自应用程序的)新消息之后发送的呢?

下面的代码更像是一个作为工作代码的算法,只是想要一个关于如何做的提示/方法。

func example (new_message_from_channel <-chan *message) {
    default = message
    time = now_milliseconds
    diff_accepted = 50milli
    for this_message := range new_message_from_channel {
        if now_millisecond -  time >= diff_accepted {
            send_it_to_the_client
            time = now_milliseconds
        } else {
            //save the latest message
            default = this_message

            //My problem is how to process this latest message when the blocked 50ms is over and no new message coming ?!

            //My strategy - keep sending it to the same channel
            theChannel <- default
        }

    }
}

如果你有优雅的方法,欢迎与我分享:)

【问题讨论】:

  • 使用速率限制器,例如godoc.org/golang.org/x/time/rate
  • @colm.anseo 我看不出如何整合它,你能给我举个例子吗?谢谢!
  • 刚刚发布了golang.org/x/time/rate的用法示例

标签: go channel goroutine


【解决方案1】:

使用速率限制器,您可以创建一个throttle 函数,该函数将采用:速率和通道作为输入;并返回两个频道 - 一个包含所有原始频道项目,另一个仅以固定速率中继项目:

func throttle(r time.Duration, in <-chan event) (C, tC <-chan event) {

    // "writeable" channels
    var (
        wC  = make(chan event)
        wtC = make(chan event)
    )

    // read-only channels - returned to caller
    C = wC
    tC = wtC

    go func() {
        defer close(wC)
        defer close(wtC)

        rl := rate.NewLimiter(
            rate.Every(r),
            1,
        )

        // relays input channel's items to two channels:
        // (1) gets all writes from original channel
        // (2) only writes at a fixed frequency
        for ev := range in {
            wC <- ev
            if rl.Allow() {
                wtC <- ev
            }
        }
    }()
    return
}

工作示例:https://play.golang.org/p/upei0TiyzNr


编辑:

为了避免使用速率限制器,而是使用简单的time.Ticker

tick := time.NewTicker(r)

for ev := range in {
    select {
    case wC <- ev: // write to main
    case <-tick.C:
        wC <- ev  // write to main ...
        wtC <- ev // ... plus throttle channel
    }
}

工作示例:https://play.golang.org/p/UTRXh72BvRl

【讨论】:

  • 但这并不能解决我的问题,这只会限制阅读率并且不会丢弃所有以前收到的消息!我需要收到所有 200000 条消息,但例如在 50 毫秒时只选择 20 条最新消息……这只会减慢进程,并且发送给用户的消息不会更新(所以我们可以说它不是实时完成的)。
  • @cassandra 重新阅读您的问答。听起来您想要一个双通道解决方案:通道 #1 流式传输原始未过滤的消息;通道 #2 提供过滤的消息 - 以 20/s 的速率,即每 50 毫秒一个。这是正确的解释吗?
  • 我的意见可能有偏见 :-) 如果代码符合您的需求,那就去吧 - 因为它不需要外部包(尽管我的解决方案可以很容易地使用代码)。除此之外,一般来说,我尽量不在非绝对需要的地方添加隐藏机制。通过分层添加功能(例如中间件处理程序,在这种情况下为通道过滤器) - 允许以最小的努力添加或删除此逻辑,甚至在运行时动态打开。
  • 你能用自动收报机调整它吗?
  • 有空我会好好复查代码,再次感谢大家的努力!
猜你喜欢
  • 1970-01-01
  • 2016-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多