在一个单独的方法中使用Golang的defer。

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

Using golang's defer in a separate method

问题

我正在使用Go语言的RabbitMQ库进行项目开发,并且在一个单独的包中有一个名为Connect的函数。我在main函数中调用Connect,但是由于我在一个单独的函数中连接到RabbitMQ,所以defer conn.Close()函数被调用,这会在Connect函数内部关闭连接。这是有道理的,但是问题是,我应该在哪里调用conn.Close()呢?

你可以看到上面的代码中,我在建立连接后调用了defer conn.Close(),但是这会立即再次关闭连接。

这里有一个Go Playground的示例,模拟了我所说的情况... 链接

英文:

I'm using the golang RabbitMQ library in a project, and I have a Connect function in a separate package. I'm calling Connect, in my main function, however because I connect to RabbitMQ in a separate function, the defer conn.Close() function is called, which closes the connection within the Connect function. Which makes perfect sense, but that begs the question, where then, do I call conn.Close()?

  1. package drivers
  2. import (
  3. // Core
  4. "log"
  5. "os"
  6. "time"
  7. // Third party
  8. "github.com/streadway/amqp"
  9. )
  10. type Queue struct {
  11. Channel *amqp.Channel
  12. }
  13. func NewQueue() *Queue {
  14. return &Queue{}
  15. }
  16. // Queue interface
  17. type IQueue interface {
  18. Connect(args ...interface{})
  19. Publish(queue string, payload []byte) error
  20. Listen(queue string) (<-chan amqp.Delivery, error)
  21. Declare(queue string) (amqp.Queue, error)
  22. }
  23. // Connect - Connects to RabbitMQ
  24. func (queue *Queue) Connect(args ...interface{}) {
  25. var uri string
  26. if args == nil {
  27. // Get from env vars
  28. uri = os.Getenv("RABBIT_MQ_URI")
  29. if uri == "" {
  30. log.Panic("No uri for queue given")
  31. }
  32. } else {
  33. uri = args[0].(string)
  34. }
  35. // Make max 5 connection attempts, with a 1 second timeout
  36. for i := 0; i < 5; i++ {
  37. log.Println("Connecting to:", uri)
  38. // If connection is successful, return new instance
  39. conn, err := amqp.Dial(uri)
  40. defer conn.Close()
  41. if err == nil {
  42. log.Println("Successfully connected to queue!")
  43. channel, _ := conn.Channel()
  44. queue.Channel = channel
  45. return
  46. }
  47. log.Println("Failed to connect to queue, retrying...", err)
  48. // Wait 1 second
  49. time.Sleep(5 * time.Second)
  50. }
  51. }
  52. // Declare a new queue
  53. func (queue *Queue) Declare(queueName string) (amqp.Queue, error) {
  54. return queue.Channel.QueueDeclare(
  55. queueName,
  56. true,
  57. false,
  58. false,
  59. false,
  60. nil,
  61. )
  62. }
  63. // Publish a message
  64. func (queue *Queue) Publish(queueName string, payload []byte) error {
  65. return queue.Channel.Publish(
  66. "",
  67. queueName,
  68. false,
  69. false,
  70. amqp.Publishing{
  71. DeliveryMode: amqp.Persistent,
  72. ContentType: "application/json",
  73. Body: payload,
  74. },
  75. )
  76. }
  77. // Listen for a new message
  78. func (queue *Queue) Listen(queueName string) (<-chan amqp.Delivery, error) {
  79. return queue.Channel.Consume(
  80. queueName,
  81. "",
  82. true,
  83. false,
  84. false,
  85. false,
  86. nil,
  87. )
  88. }

As you can see in the code above, I'm calling defer conn.Close() after making a connection, however, this immediately closes the connection again.

Here's a Go Playground spoofing what I'm talking about... https://play.golang.org/p/5cz2D4gDgn

答案1

得分: 1

简单的解决方案是从其他地方调用conn.Close()。也许只是我觉得有点奇怪,你不会在Queue中将连接暴露出来,即作为一个字段。从队列中暴露关闭连接的能力将解决这个问题并提供更多的灵活性。

所以可以这样做:

  1. type Queue struct {
  2. // 你原来的字段
  3. Conn amqp.Connection
  4. }
  5. // 在其他地方
  6. queue.Conn.Close()

你的另一个选择是连接,然后使用该连接执行所有你想要的操作,然后关闭连接。我想的是这样的:

  1. func action(conn amqp.Connection, args ...interface{}) (<-chan bool) {
  2. done := make(chan bool)
  3. go func(amqpConn amqp.Connection, dChan chan bool){
  4. // 使用连接执行你想要的操作
  5. dChan <- true
  6. }(conn, done)
  7. return done
  8. }
  9. func (queue *Queue) Connect(args ...interface{}) {
  10. // 你的连接代码
  11. doneChans := make([](chan bool), 5)
  12. for i := 0; i < 5; i++ {
  13. conn, err := amqp.Dial(uri)
  14. defer conn.Close()
  15. if err != nil {
  16. // 处理错误
  17. }
  18. done := action(conn)
  19. }
  20. // 这个循环将阻塞,直到5个操作调用完成
  21. for j := range doneChans {
  22. isFinish := <-doneChans[j]
  23. if !isFinish {
  24. // 处理错误状态
  25. }
  26. }
  27. }
英文:

The simple solution is to call conn.Close() from elsewhere. This might just be me, but I think it's kinda odd that you wouldn't expose the connection elsewhere, i.e. as a field in Queue. Exposing the ability to close the connection from the Queue would solve this and give you more flexibility.

So this:

  1. type Queue struct {
  2. // your original fields
  3. Conn amqp.Connection
  4. }
  5. // Somewhere else
  6. queue.Conn.Close()

You're other option is connecting, then doing all the actions you want with that connection, then closing. I'm thinking something like:

  1. func action(conn amqp.Connection, args ...interface{}) (&lt;-chan bool) {
  2. done := make(chan bool)
  3. go func(amqpConn amqp.Connection, dChan chan bool){
  4. // Do what you want with the connection
  5. dChan &lt;- true
  6. }(conn, done)
  7. return done
  8. }
  9. func (queue *Queue) Connect(args ...interface{}) {
  10. // your connection code
  11. doneChans := make([](chan bool), 5)
  12. for i := 0; i &lt; 5; i++ {
  13. conn, err := amqp.Dial(uri)
  14. defer conn.Close()
  15. if err != nil {
  16. // handle error
  17. }
  18. done := action(conn)
  19. }
  20. // This for loop will block until the 5 action calls are done
  21. for j := range doneChans {
  22. isFinish := &lt;-doneChans[j]
  23. if !isFinish {
  24. // handle bad state
  25. }
  26. }
  27. }

答案2

得分: 1

一种选择是让Connect函数返回conn,并在调用者中使用defer conn.Close()

  1. package driver
  2. // 导入等
  3. func (queue *Queue) Connect(args ...interface{}) (amqp.Connection, error) {
  4. // ...
  5. conn, err := amqp.Dial(uri)
  6. if err != nil {
  7. return nil, err
  8. }
  9. // ...
  10. return conn, nil
  11. }
  12. 然后在另一个包中
  13. package stuff
  14. // 导入等
  15. func doStuff() {
  16. queue := driver.NewQueue()
  17. conn, err := queue.Connect(args...)
  18. if err != nil {
  19. log.Fatalf("出错了!%v!", err)
  20. }
  21. defer conn.Close()
  22. // 做一些事情
  23. }
英文:

One option is to have Connect return conn, and call defer conn.Close() in the caller.

  1. package driver
  2. // imports, etc
  3. func (queue *Queue) Connect(args ...interface{}) amqp.Connection, error {
  4. // ...
  5. conn, err := amqp.Dial(uri)
  6. if err != nil {
  7. return nil, err
  8. }
  9. // ...
  10. return conn, nil
  11. }

Then in another package:

  1. package stuff
  2. // imports, etc
  3. func doStuff() {
  4. queue = driver.NewQueue()
  5. conn, err := queue.Connect(args...)
  6. if err != nil {
  7. log.Fatalf(&quot;oh no! %v!&quot;, err)
  8. }
  9. defer conn.Close()
  10. // Do stuff
  11. }

huangapple
  • 本文由 发表于 2016年11月19日 00:31:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/40681935.html
匿名

发表评论

匿名网友

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

确定