我有一些代码使用了RabbitMQ队列中的消息。如果一段时间没有收到消息,它将失败。
c1 := make(chan error, 1)
go func() {
for d := range msgs {
if corrID == d.CorrelationId {
err = json.Unmarshal([]byte(uncompress(d.Body)), &v)
if err != nil {
c1 <- err
}
ch.Ack(d.DeliveryTag, false)
c1 <- nil
}
}
}()
select {
case <-time.After(defaultTimeout * time.Second):
log.Printf("Failed to receive response for action %+v\n Payload: %+v\nError: %+v\n", action, body, err)
return errors.New("Failed to receive response in time for action")
case err := <-c1:
failOnError(err, "Failed to process response")
}
return err
我从RabbitMQ手册中获取了消费代码,并尝试了一些有关实现超时的建议。我知道如何用Java做到这一点,但不能在Golang中重复。
谢谢。
更新:
已将选择更改为此:
{{1}}
现在它可以正常工作-如果它没有收到带有正确corellationId的消息,它将因超时而失败。谢谢大家的帮助。
答案 0 :(得分:1)
您的循环有一个select
,有2种情况:超时和default
分支。进入循环后,超时不会触发,因此将执行default
分支。
default
分支在for range
通道上包含一个msgs
,该通道一直从该通道接收直到关闭为止(并且已经从该通道接收到所有值)。通常,这种情况不应该发生,因此不会重新讨论超时情况(仅当发生某些错误并且msgs
关闭时)。
在循环内部使用select
处理2种情况,一种超时,另一种仅从msgs
接收单个值。如果收到消息,请重新启动超时。对于可重启计时器,请使用time.Timer
。
timeout := time.Second
timer := time.NewTimer(timeout)
for {
select {
case <-timer.C:
fmt.Println("timeout, returning")
return
case msg := <-msgs:
fmt.Println("received message:", msg)
// Reset timer: it must be stopped first
// (and drain its channel if it reports false)
if !timer.Stop() {
<-timer.C
}
timer.Reset(timeout)
}
}
请查看此Go Playground示例以了解其运行情况。
请注意,如果在收到消息后不需要重置计时器,请注释掉重置代码。另外,如果不需要重置,则time.After()
更简单:
timeout := time.After(time.Second)
for {
select {
case <-timeout:
fmt.Println("timeout, returning")
return
case msg := <-msgs:
fmt.Println("received message:", msg, time.Now())
}
}
在Go Playground上尝试这个。
最后一点:如果您在发生超时之前退出循环,则不会立即释放后台计时器(仅在发生超时时)。如果您经常需要执行此操作,则可以使用context.WithTimeout()
获得一个context.Context
和一个cancel函数,您可以在返回之前立即调用该函数以释放计时器资源(最好是延迟)。
它是这样的:
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
for {
select {
case <-ctx.Done():
fmt.Println("timeout, returning")
return
case msg := <-msgs:
fmt.Println("received message:", msg, time.Now())
}
}
在Go Playground上尝试这个。
答案 1 :(得分:0)
已将选择更改为此:
c1 := make(chan error, 1)
go func() {
for d := range msgs {
if corrID == d.CorrelationId {
err = json.Unmarshal([]byte(uncompress(d.Body)), &v)
if err != nil {
c1 <- err
}
ch.Ack(d.DeliveryTag, false)
c1 <- nil
}
}
}()
select {
case <-time.After(defaultTimeout * time.Second):
log.Printf("Failed to receive response for action %+v\n Payload: %+v\nError: %+v\n", action, body, err)
return errors.New("Failed to receive response in time for action")
case err := <-c1:
failOnError(err, "Failed to process response")
}
return err