在失败时重试进行队列处理

时间:2017-02-23 05:44:45

标签: asynchronous go redis beanstalkd

我们有一堆文件要在处理后上传到远程blob商店。

目前,前端(PHP)会创建此类文件的redis列表,并为其提供一个名为JobID的唯一ID。然后,它将唯一ID传递给bean进程管,该进程由Go进程接收。它使用名为Go workers的库以net/http的方式处理每个作业ID。它接收作业ID,检索redis列表并开始处理文件。

但是,目前一次只处理一个文件。由于这里的操作是I / O绑定,而不是CPU限制,直觉表明每个文件使用goroutine会有好处。

但是,我们希望在失败时重试上传,并跟踪每个作业处理的项目数。我们无法启动未绑定数量的goroutine,因为单个作业可以包含大约10k个要处理的文件,并且在高峰时间每秒可以发送100个这样的作业。对此有什么正确的解决方法?

注意:如果需要,我们可以稍微更改一下技术堆栈(比如换掉beanstalkd)

1 个答案:

答案 0 :(得分:2)

您可以使用缓冲的chan来限制goroutine的数量,其大小为您想要的最大goroutine数量。如果达到最大容量,您可以阻止此chan。随着你的goroutines结束,他们将释放插槽以允许新的goroutines运行。

示例:

package main

import (
    "fmt"
    "sync"
)

var (
    concurrent    = 5
    semaphoreChan = make(chan struct{}, concurrent)
)

func doWork(wg *sync.WaitGroup, item int) {
    // block while full
    semaphoreChan <- struct{}{}

    go func() {
        defer func() {
            // read to release a slot
            <-semaphoreChan
            wg.Done()
        }()
        // This is where your work actually gets done
        fmt.Println(item)
    }()
}

func main() {
    // we need this for the example so that we can block until all goroutines finish
    var wg sync.WaitGroup
    wg.Add(10)

    // start the work
    for i := 0; i < 10; i++ {
        doWork(&wg, i)
    }

    // block until all work is done
    wg.Wait()
}

Go Playground链接:https://play.golang.org/p/jDMYuCe7HV

受到Golang英国会议演讲的启发:https://youtu.be/yeetIgNeIkc?t=1413