我们有一个流程,用户可以通过我们的来源请求我们需要的文件。这个来源不是最可靠的,所以我们使用Amazon SQS实现了一个队列。我们将下载URL放入队列,然后使用我们在Go中编写的小应用程序对其进行轮询。这个应用程序只是检索消息,下载文件,然后将其推送到我们存储它的S3。完成所有这一切后,它会回拨一个服务,该服务将通过电子邮件发送给用户,让他们知道该文件已准备就绪。
最初我写这个来创建 n 频道,然后为每个频道附加1个常规例程,并在无限循环中使用go-routine。这样我就可以确保我一次只处理固定数量的下载。
我意识到这不是应该使用频道的方式,如果我现在正确理解,实际上应该有一个频道 n go-在该频道上接收的例程。每个go-routine都处于一个无限循环中,等待一条消息,当它接收到它时,它将处理数据,做它应该做的所有事情,当它完成时它将等待下一条消息。这使我能够确保我一次只处理 n 文件。我认为这是正确的做法。我相信这是粉丝,对吧?
我 需要做的是将这些流程合并在一起。下载完成后,它将回调远程服务,以便处理剩余的进程。该应用程序无需做任何其他事情。
好的,所以有些代码:
func main() {
queue, err := ConnectToQueue() // This works fine...
if err != nil {
log.Fatalf("Could not connect to queue: %s\n", err)
}
msgChannel := make(chan sqs.Message, 10)
for i := 0; i < MAX_CONCURRENT_ROUTINES; i++ {
go processMessage(msgChannel, queue)
}
for {
response, _ := queue.ReceiveMessage(MAX_SQS_MESSAGES)
for _, m := range response.Messages {
msgChannel <- m
}
}
}
func processMessage(ch <-chan sqs.Message, queue *sqs.Queue) {
for {
m := <-ch
// Do something with message m
// Delete message from queue when we're done
queue.DeleteMessage(&m)
}
}
我在这附近吗?我有 n 运行go-routines(其中MAX_CONCURRENT_ROUTINES
= n )并且在循环中我们将继续将消息传递到单个通道。这是正确的方法吗?我是否需要关闭任何东西,或者我可以无限期地停止运行?
我注意到的一件事是SQS正在返回消息,但是一旦我将10条消息传递到processMessage()
(10是通道缓冲区的大小),就没有其他消息了实际上是处理过的。
全部谢谢
答案 0 :(得分:3)
看起来很好。几点说明:
您可以通过限制生成的工作程序的数量来限制工作并行度。例如,您可以为收到的每条消息创建一个goroutine,然后让生成的goroutine等待限制并行性的信号量。当然有权衡,但你并不仅限于你所描述的方式。
sem := make(chan struct{}, n)
work := func(m sqs.Message) {
sem <- struct{}{} // When there's room we can proceed
// do the work
<-sem // Free room in the channel
}()
for _, m := range queue.ReceiveMessage(MAX_SQS_MESSAGES) {
for _, m0 := range m {
go work(m0)
}
}
正在处理的只有10条消息的限制正在堆栈的其他位置引起。可能你会看到一场比赛,前10名赛道填满了赛道,然后工作没有完成,或者你可能不小心从工人的惯例中回来了。如果你的工人对你所描述的模型持久,你就会确定他们没有回来。
目前尚不清楚是否希望在处理完一定数量的邮件后进程返回。如果您确实希望退出此过程,则需要等待所有工作人员完成当前任务,并可能要求他们在之后返回。看一下sync.WaitGroup
来同步他们的完成情况,并让另一个频道表示没有更多的工作,或关闭msgChannel
,并在你的工人中处理它。 (看看2元组返回通道接收表达式。)