golang 中的生产者消费者 - 并发与并行性?

producer consumer in golang - concurrency vs parallelism?

我正在研究纯 Golang 的后端架构。我有一个 API 用于将文件上传到 golang 服务器,然后我将文件传输到云存储(从 golang 服务器本身)。现在,我希望这两个传输是独立的,这样,最终用户就不必在上传文件后等待响应。

End User -> Golang Server ->[Concurrency/Parallelism] -> Cloud Storage

现在,我想到了两个办法:

  1. 用户完成上传后立即创建一个 goroutine 并将文件传输到云端。
  2. 将文件处理程序插入队列,不同的进程将读取此队列并将文件传输到云存储(多生产者 - 单一消费者模型)。

我找到了使用 goroutine 和通道执行此操作的示例,但我认为这会创建与上传一样多的 goroutine。我想使用第二个选项,但无法理解如何在 golang 中使用它?

此外,如果我使用了错误的方法,请提出建议,还有其他一些有效的方法可以做到这一点。

更新

关于要求和约束的详细信息:
1. 我使用 AWS S3 作为云存储。如果在某个时候,从 Go 服务器到 Amazon S3 的上传失败,文件处理程序应该保留下来以记录失败的上传。(我没有优先考虑这个,我可能会根据客户的反馈改变这个)
2. 成功上传到Amazon S3 后,文件将立即从Go 服务器中删除,以避免重复上传。此外,如果上传的文件具有相同的名称,它将在 Amazon S3 中被替换。
3.正如评论中所指出的,我可以使用通道作为队列。是否可以使用 Go 的 Channels 和 goroutines 设计上述架构?

上传文件的用户可以容忍错误,然后重试。但是当上传的文件只存在于它被上传到的机器上,并且在它被上传到云存储之前出现问题时,危险就存在了。在那种情况下,文件将丢失,这对用户来说将是一个无赖。

好的架构解决了这个问题。这是一个 first-in, first out queue pattern.

此模式最喜欢的 Go 实现是 go-workers perhaps backed by a Redis 数据库。

假设在任何给定时间有 n 台服务器 运行 正在为您提供服务。假设您的后端代码编译了两个单独的二进制文件,一个 server 二进制文件和一个 worker 二进制文件。

理想情况下,接受文件上传的机器都将安装一个共享 Network File System 这样:

  1. 用户将文件上传到 服务器

    一个。 server 向工作队列添加一条记录,其中包含来自 Redis 存储的唯一 ID。

    b。这个唯一 ID 用于创建文件名,文件直接从用户上传到 NFS 服务器上的临时存储。请注意,该文件永远不会驻留在运行连接服务器的机器的存储中。

  2. 文件由工作人员

    上传到云存储

    一个。 worker从工作队列中取出下一条待办记录,它有唯一的ID

    b。使用唯一 ID 在 NFS 服务器上查找文件,worker 将文件上传到云存储

    c。成功后,worker 更新工作队列中的记录以反映成功

    d. worker删除NFS服务器上的文件

通过将服务器流量和工作队列大小作为两个单独的指标进行监控,可以确定有多少服务器应该分别 运行 server/worker 服务。

Marcio Castilho 写了一篇关于类似问题的好文章。它可以在 Handling one million requests per minutes with golang 找到。

他展示了他所犯的错误以及他为纠正这些错误所采取的步骤。学习使用通道、goroutines 和一般并发的好资源。

go-workers mentioned by charneykaye 也是很好的来源。