Go的缓冲通道在select语句中会出现穿透现象。

huangapple go评论114阅读模式
英文:

Go buffered channel falls through select

问题

我有2个缓冲通道来处理传入的请求:

rabbitQ = make(chan map[string]interface{}, 1000)
sqsQ = make(chan map[string]interface{}, 1000)

我的调度函数如下所示:

func dispatchMessage(params map[string]interface{}) {
    if !shouldFailoverToSQS {
        select {
        case rabbitQ <- params:
            sentToRabbitMQ++
        case sqsQ <- params:
            sentToSQS++
        default:
            log.Error("Failed to dispatch message to either RabbitMQ or SQS")
        }
    } else {
        sqsQ <- params
    }
}

我期望消息总是被发送到 rabbitQ,除非缓冲区已满,但我发现有大约一半的时间调用会继续执行,并将消息发送到 sqsQ。这不是我想要的 - 我只想在 rabbitQ 满时才发送到 sqsQ。

我该如何强制执行这一点?

英文:

I have 2 buffered channels servicing inbound requests:

rabbitQ = make(chan map[string]interface{}, 1000)
sqsQ = make (chan map[string]interface{}, 1000)

My dispatcher function looks like this:

func dispatchMessage(params map[string]interface{}) {

	if !shouldFailoverToSQS {
    	select {
	    case rabbitQ &lt;- params:
		    sentToRabbitMQ++
		case sqsQ &lt;- params:
    		sentToSQS++
		default:
    		log.Error(&quot;Failed to dispatch mesaage to either RabbitMQ or SQS&quot;)
	    }
	} else {
    	sqsQ &lt;- params
	}

}

I would expect that messages would always be sent to rabbitQ unless the buffer was at capacity but I'm finding that the call is falling through and sending messages to sqsQ about half the time. This is not what I want - I only want to send to sqsQ if rabbitQ is full.

How can I enforce this?

答案1

得分: 1

根据Voker的评论,这是我想出来的代码:

func dispatchMessage(params map[string]interface{}) {

    //log.Debugf("Failover: %t, Len: %d", shouldFailoverToSQS, len(rabbitQ))

    if !shouldFailoverToSQS {
        select {
        case rabbitQ <- params:
            sentToRabbitMQ++
        default:
            select {
            case sqsQ <- params:
                sentToSQS++
            default:
                log.Error("Failed to dispatch mesaage to either RabbitMQ or SQS")
            }
        }
    } else {
        select {
        case sqsQ <- params:
            sentToSQS++
        default:
            log.Error("Failed to dispatch mesaage to either RabbitMQ or SQS")
        }
    }

}
英文:

Per Voker's comment, this is what I came up with:

func dispatchMessage(params map[string]interface{}) {

	//log.Debugf(&quot;Failover: %t, Len: %d&quot;, shouldFailoverToSQS, len(rabbitQ))

	if !shouldFailoverToSQS {
		select {
		case rabbitQ &lt;- params:
			sentToRabbitMQ++
		default:
			select {
			case sqsQ &lt;- params:
				sentToSQS++
			default:
				log.Error(&quot;Failed to dispatch mesaage to either RabbitMQ or SQS&quot;)
			}
		}
	} else {
		select {
		case sqsQ &lt;- params:
			sentToSQS++
		default:
			log.Error(&quot;Failed to dispatch mesaage to either RabbitMQ or SQS&quot;)
		}
	}

}

答案2

得分: 0

对于简洁的答案(从你的回答和线程评论中看起来你已经有了答案),你的选择将随机选择。根据文档:

> 如果一个或多个通信可以进行,将通过均匀伪随机选择选择一个可以进行的通信。否则,如果有默认情况,将选择该情况。如果没有默认情况,则“select”语句将阻塞,直到至少有一个通信可以进行。

此外,我想提供一个完全不使用选择语句的答案。使用len函数,您可以确定通道中排队的值的数量。当然,这与您现在所做的没有任何区别,但我想提供一些不同的东西:

const MaxChan = 1000
func dispatchMessage(params map[string]interface{}) {
    if !shouldFailoverToSQS {
        if len(rabbitQ) < MaxChan {
            sentToRabbitMQ++
        } else if len(sqsQ) < MaxChan {
            sentToSQS++
        } else {
            log.Error("无法将消息分派到RabbitMQ或SQS")
        }
    } else {
        if len(sqsQ) < MaxChan {
            sentToRabbitMQ++
        } else if len(rabbitQ) < MaxChan {
            sentToSQS++
        } else {
            log.Error("无法将消息分派到RabbitMQ或SQS")
        }
    }
}

支持文档:
https://golang.org/ref/spec#Select_statements

英文:

For a concise answer (it looks like you already have it from your answer + the thread comment), your select will choose at random. Per the docs:

> If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the "select" statement blocks until at least one of the communications can proceed.

In addition, I figured I would provide you with an answer that doesn't use selects at all. Using len, you can determine how many values are queued up in a channel. Sure it's no different than what you're doing now, but I figured I would offer something different:

const MaxChan = 1000
func dispatchMessage(params map[string]interface{}) {
    if !shouldFailoverToSQS {
        if len(rabbitQ) &lt; MaxChan {
            sentToRabbitMQ++
        } else if len(sqsQ) &lt; MaxChan {
            sentToSQS++
        } else {
            log.Error(&quot;Failed to dispatch mesaage to either RabbitMQ or SQS&quot;)
        }
    } else {
        if len(sqsQ) &lt; MaxChan {
            sentToRabbitMQ++
        } else if len(rabbitQ) &lt; MaxChan {
            sentToSQS++
        } else {
            log.Error(&quot;Failed to dispatch mesaage to either RabbitMQ or SQS&quot;)
        }
    }
}

Supporting docs:
https://golang.org/ref/spec#Select_statements

答案3

得分: 0

func dispatchMessage(params map[string]interface{}) {

    if !shouldFailoverToSQS {
        select {
        case rabbitQ <- params:
            sentToRabbitMQ++
        case <-time.After(time.Millisecond * 10.0): // rabbit is blocked and 10 milli sec passed.
            select {
            case sqsQ <- params:
                sentToSQS++  
            default:
                log.Error("Failed to dispatch message to either RabbitMQ or SQS")
            }
        }
    } else {
        sqsQ <- params
    }

}
func dispatchMessage(params map[string]interface{}) {

    if !shouldFailoverToSQS {
        select {
        case rabbitQ <- params:
            sentToRabbitMQ++
        case <-time.After(time.Millisecond * 10.0): // rabbit is blocked and 10 milli sec passed.
            select {
            case sqsQ <- params:
                sentToSQS++  
            default:
                log.Error("无法将消息分发到RabbitMQ或SQS")
            }
        }
    } else {
        sqsQ <- params
    }

}
英文:
func dispatchMessage(params map[string]interface{}) {

    if !shouldFailoverToSQS {
        select {
        case rabbitQ &lt;- params:
            sentToRabbitMQ++
        case &lt;-time.After(time.Millisecond * 10.0): // rabbit is blocked and 10 milli sec passed.
            select {
            case sqsQ &lt;- params:
                sentToSQS++  
            default:
            log.Error(&quot;Failed to dispatch mesaage to either RabbitMQ or SQS&quot;)
            }
        }
    } else {
        sqsQ &lt;- params
    }

}

huangapple
  • 本文由 发表于 2017年5月17日 17:28:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/44020814.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定