使用ZeroMQ在Go中处理中断的惯用方式

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

Idiomatic way of handling interrupts in Go using ZeroMQ

问题

我目前正在学习Go和ZeroMQ,并且在这个过程中,我试图为Zguide贡献Go的示例代码。我在处理中断示例时遇到了一些困难。我不确定处理这个问题的惯用方式是什么。

我目前的解决方案是:我创建一个接收SIGINT信号的通道。当接收到信号时,我在另一个通道上写入一个布尔值,该布尔值在主循环中用于中断。问题是,Recv是阻塞的,循环永远不会检查循环条件。我通过将NOBLOCK常量传递给Recv来绕过这个问题。但是我觉得应该有更好的方法,因为Recv应该在被中断时返回EINTR(据我所知它并没有返回)。你们读者比我更有能力回答这个问题,你们觉得呢?

为了方便起见,这是我目前的代码:

package main

import (
  	"os/signal"
	"os"
	"fmt"
	zmq "github.com/alecthomas/gozmq"
)

func listenForSignals(exit_channel chan bool) {
	signal_channel := make(chan os.Signal)
	signal.Notify(signal_channel)
	<- signal_channel
	fmt.Println("stopping")
	exit_channel <- true
}

func main() {
	exit := make(chan bool)
	exit_signal := false
	go listenForSignals(exit)

	context, _ := zmq.NewContext()
	defer context.Close()

	socket, _ := context.NewSocket(zmq.REP)
	defer socket.Close()
	socket.Bind("tcp://*:5555")

	for exit_signal == false {
	  select {
	  case exit_signal = <- exit:
		fmt.Println("W: interrupt received, killing server...")
	  default:
		msgbytes, err := socket.Recv(zmq.NOBLOCK)
		fmt.Printf("%s.\n", string(msgbytes))
	  }
	}

}

编辑 根据反馈,简化了代码。

英文:

I'm learning both Go and ZeroMQ at the moment and in that spirit I'm trying to contribute Go examples for the Zguide. I'm struggling a bit with the interrupt example. I'm unsure what the idiomatic way of handling the problem would be.

The solution I currently have is the following: I create a channel which receives the SIGINT signal. When it does I write a bool on an other channel which is used in the main loop to break. The problem is, is that Recv is blocking, the loop never gets to check the loop condition. I circumvent the problem by passing the NOBLOCK constant to Recv. But I feel there is a better way as Recv should return a EINTR when interrupted (which it doesn't as far as I can tell). You readers are much better equipped to answer this question then I am, what do you think?

For your convenience the code I have so far:

package main

import (
  	&quot;os/signal&quot;
	&quot;os&quot;
	&quot;fmt&quot;
	zmq &quot;github.com/alecthomas/gozmq&quot;
)

func listenForSignals(exit_channel chan bool) {
	signal_channel := make(chan os.Signal)
	signal.Notify(signal_channel)
	&lt;- signal_channel
	fmt.Println(&quot;stopping&quot;)
	exit_channel &lt;- true
}

func main() {
	exit := make(chan bool)
	exit_signal := false
	go listenForSignals(exit)

	context, _ := zmq.NewContext()
	defer context.Close()

	socket, _ := context.NewSocket(zmq.REP)
	defer socket.Close()
	socket.Bind(&quot;tcp://*:5555&quot;)

	for exit_signal == false {
	  select {
	  case exit_signal = &lt;- exit:
		fmt.Println(&quot;W: interrupt received, killing server...&quot;)
	  default:
		msgbytes, err := socket.Recv(zmq.NOBLOCK)
		fmt.Printf(&quot;%s.\n&quot;, string(msgbytes))
	  }
	}

}

Edit simplified the code somewhat based on feedback

答案1

得分: 2

如果您要使用select语句来切换通道,您还应该将socket.Recv的结果返回到一个通道上。这样还可以在goroutine上运行socket.Recv,以避免阻塞的问题。

实际上,您还应该处理您获得的错误。您可以通过向整个程序添加另一个通道来实现。

func main() {
    ...

    data := make(chan []byte)
    errors := make(chan error)
    go function() {
      for {
        msgbytes, err := socket.Recv(0)
        if err != nil {
          errors <- err
        } else {
          data <- msgbytes
        }
      }
    }()
    
    for exit_signal == false {
      select {
      case exit_signal = <- exit:
        fmt.Println("W: interrupt received, killing server...")
      case err := <- errors:
        fmt.Println("Receive Error:", err.Error())
      case msgbytes := <- data:
        fmt.Printf("%s.\n", string(msgbytes))
      }
    }

}
英文:

If you are going to use a select statement to switch over channels, you should return the result of socket.Recv on a channel as well. This also lets you run socket.Recv on a goroutine so the blocking nature isn't an issue.

Realistically, you should probably also handle errors that you get as well. That you can do by adding another channel to the whole shebang.

func main() {
    ...

    data := make(chan []byte)
    errors := make(chan error)
    go function() {
      for {
        msgbytes, err := socket.Recv(0)
        if err != nil {
          errors &lt;- err
        } else {
          data &lt;- msgbytes
        }
      }
    }()
    
    for exit_signal == false {
      select {
      case exit_signal = &lt;- exit:
        fmt.Println(&quot;W: interrupt received, killing server...&quot;)
      case err := &lt;- errors:
        fmt.Println(&quot;Receive Error:&quot;, err.Error())
      case msgbytes := &lt;- data:
        fmt.Printf(&quot;%s.\n&quot;, string(msgbytes))
      }
    }

}

答案2

得分: 0

这看起来有点太复杂了。在主函数中,你可以启动一个goroutine来等待中断信号:

go func() {
    sigchan := make(chan os.Signal, 10)
    signal.Notify(sigchan, os.Interrupt)
    <-sigchan
    log.Println("应用程序被终止!")
    // 在真正死亡之前,可以做一些事情,比如写下你的遗愿或者诅咒杀手
    os.Exit(2)
}()
// 开始你的应用程序需要做的事情
英文:

This looks a little too complex. In the main you can start a goroutine waiting for the interrupt signal :

go func() {
	sigchan := make(chan os.Signal, 10)
	signal.Notify(sigchan, os.Interrupt)
	&lt;-sigchan
	log.Println(&quot;Application killed !&quot;)
	// do things like writing your last will or cursing the killer before you really die		
	os.Exit(2)
}()
// starts the things your application has to do

huangapple
  • 本文由 发表于 2012年6月18日 18:12:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/11080777.html
匿名

发表评论

匿名网友

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

确定