【问题标题】:understanding go channel handling / buffer overflow [duplicate]了解 Go 通道处理/缓冲区溢出 [重复]
【发布时间】:2022-10-08 05:58:22
【问题描述】:

我继承了一些我仍在尝试理解的代码。它的核心是这样的:

for msg := range this.out {
    for i, handler := range this.handlers {
        select {
        case handler <- msg:
        default:
            this.logger.Printf("Buffer overflow occurred for handler %s", this.names[i])
        }
    }
}

出是chan byte 处理程序是[]chan []byte

看起来像这样从数组中读取并写入处理程序,默认是抱怨缓冲区溢出。我认为。

但我需要细节。我是新手,这是我第一次与陈打交道。所以第一个问题,这真的是这里发生的事情吗?如果是这样......我如何防止缓冲区溢出?

【问题讨论】:

  • 在我看来,这是从out 读取msg,然后尝试将msg 写入全部handlers 数组中的频道。但是,如果handlers 中的任何通道当前有一个完整的缓冲区(或者是一个未准备好接收消息的无缓冲通道)而不是写入该特定通道,则会记录溢出消息。这就是代码的作用,尽管不知道编写此代码的原因,我们无法告诉您原因。
  • 它不会与处理程序一起编译是[]chan []byte。试着给一个minimal-reproducible-example去操场链接:go.dev/play/p/UVDicgckQe-
  • 对我来说,这段代码意味着每个handler 频道接收消息并不重要。可能是有意的,通道可能已满,而没有传递更多消息。确保真正的错误是使用听起来吓人的“缓冲区溢出”短语而不是“退避客户端 %s,它已经落后太远了,以后必须查询丢失的消息”。在不了解用例的情况下,不可能说,但该代码对我来说是可疑的。 Woody 的“使用渠道作为基础设施”这句话让我产生了共鸣。
  • 它确实编译。如果这有什么不同的话,go 版本是“go1.13.15 linux/amd64”。我能做些什么来确保处理程序跟上。 tbh 我什至看不到处理程序在做什么。几乎可以肯定是因为我不熟悉 go 频道的工作原理。设置它的代码如下:
  • ` port := mqPort out := make (chan []byte, bufsize) dist.Subscribe("mq:"+mqPort, out) ` ... 其中 Subscribe 是: ` func (this *Dist) Subscribe(name string, handler chan []byte) { this.names = append(this.names, name) this.handlers = append(this.handlers.handler) } `所以处理程序是由 make() 调用创建的“out”并传递给 Subscribe() ...但它要去哪里?看起来它是一个无处可去的通道,只有一个给定大小的缓冲区被填满。我没有看到任何建立任何添加到该缓冲区的任何内容。

标签: go channel buffer-overflow


【解决方案1】:

在 Go 中,可以通过两种方式创建通道:

c := make(chan []byte)

它创建了一个将同步发送方和接收方的通道。这意味着接收方必须等待发送方发送数据,发送方必须等待接收方获取数据。但是,通过向通道添加缓冲区:

c := make(chan []byte, 100)

您正在有效地使发送者和接收者不同步。在这种情况下,发送方会在缓冲区满时阻塞,而接收方会在缓冲区空时阻塞。

现在,我不知道handler 的容量是多少,但这是您应该期望的一般工作流程:

  1. Go 将检查handler 中是否有空间,如果有,msg 将被写入通道。
  2. 如果没有空间,则会记录错误消息。

    你实际上对缓冲区溢出所做的事情有点棘手。

    首先,作为权宜之计,您可以增加handler 的容量,以便它可以容纳更多消息。如果问题是间歇性的(即流量高峰),这可能代表实际修复。否则,您所做的只是将问题推迟一段时间。

    其次,您可以水平扩展handler 另一端的处理程序以处理更多消息,从而确保通道不会溢出。但是,这样做的问题是,您必须担心管理各种容量级别的处理程序,然后您才能进入自动缩放之类的事情。

    第三,您可以查看来自handler 的处理消息的代码,看看是否可以重新设计以更有效地处理消息。当您的代码的原始设计假设不再成立时,这种重新设计非常普遍。

    最后,您可以完全放弃将通道用作基础架构,并用包含内存的基于云的发布/订阅替换该组件。这样做的好处是,这将允许您根据需要扩展您的应用程序,而不必担心溢出,但它会产生额外的成本并需要更改基础架构。

【讨论】:

  • 仍然有一些我不明白的魔法在发生。您为一百字节数组缓冲区创建了一个通道......但是一个通道在哪里?当我写入该缓冲区时,它会去哪里? “make()”中没有任何内容可以解释这一点。如何处理发送到链接到该缓冲区的缓冲区的字节的代码?程序员在这里使用名称“处理程序”作为通道并没有帮助。不知道实际在哪里处理程序代码是!
  • 我将不得不研究这个“发布/订阅”选项,有关于在哪里阅读的链接吗?
  • @rimiha chan 类型表示 Go 中的一个对象。我不能给你确切的细节,因为这也是我模糊的东西。但是,如果两个函数可以访问handler,一个可以&lt;-handler 接收,另一个可以handler &lt;- {something} 发送。在该发送中,通道可以连接同一进程下的两个 go 例程。
  • @rimiha 您可以查看 AWS 上的 SQS/SNS、Azure 上的事件网格、事件中心或服务总线,或 NATS 以获取与云无关的产品。如果您想扮演自己的角色,也可以使用 TCP。但是,一般来说,chan 用于连接同一应用程序中的线程,因此在切换基础架构之前应该小心。
猜你喜欢
  • 2010-11-11
  • 2013-11-06
  • 2017-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-09
  • 2015-12-16
  • 1970-01-01
相关资源
最近更新 更多