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

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

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()?

package drivers

import (

	// Core
	"log"
	"os"
	"time"

	// Third party
	"github.com/streadway/amqp"
)

type Queue struct {
	Channel *amqp.Channel
}

func NewQueue() *Queue {
	return &Queue{}
}

// Queue interface
type IQueue interface {
	Connect(args ...interface{})
	Publish(queue string, payload []byte) error
	Listen(queue string) (<-chan amqp.Delivery, error)
	Declare(queue string) (amqp.Queue, error)
}

// Connect - Connects to RabbitMQ
func (queue *Queue) Connect(args ...interface{}) {

	var uri string

	if args == nil {

		// Get from env vars
		uri = os.Getenv("RABBIT_MQ_URI")

		if uri == "" {
			log.Panic("No uri for queue given")
		}
	} else {
		uri = args[0].(string)
	}

	// Make max 5 connection attempts, with a 1 second timeout
	for i := 0; i < 5; i++ {

		log.Println("Connecting to:", uri)

		// If connection is successful, return new instance
		conn, err := amqp.Dial(uri)
        defer conn.Close()

		if err == nil {
			log.Println("Successfully connected to queue!")
			channel, _ := conn.Channel()
			queue.Channel = channel
			return
		}

		log.Println("Failed to connect to queue, retrying...", err)

		// Wait 1 second
		time.Sleep(5 * time.Second)
	}
}

// Declare a new queue
func (queue *Queue) Declare(queueName string) (amqp.Queue, error) {
	return queue.Channel.QueueDeclare(
		queueName,
		true,
		false,
		false,
		false,
		nil,
	)
}

// Publish a message
func (queue *Queue) Publish(queueName string, payload []byte) error {
	return queue.Channel.Publish(
		"",
		queueName,
		false,
		false,
		amqp.Publishing{
			DeliveryMode: amqp.Persistent,
			ContentType:  "application/json",
			Body:         payload,
		},
	)
}

// Listen for a new message
func (queue *Queue) Listen(queueName string) (<-chan amqp.Delivery, error) {
	return queue.Channel.Consume(
		queueName,
		"",
		true,
		false,
		false,
		false,
		nil,
	)
}

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中将连接暴露出来,即作为一个字段。从队列中暴露关闭连接的能力将解决这个问题并提供更多的灵活性。

所以可以这样做:

type Queue struct {
    // 你原来的字段
    Conn amqp.Connection
}

// 在其他地方
queue.Conn.Close()

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

func action(conn amqp.Connection, args ...interface{}) (<-chan bool) {
    done := make(chan bool)
    go func(amqpConn amqp.Connection, dChan chan bool){
        // 使用连接执行你想要的操作
        dChan <- true
    }(conn, done)
    return done
}

func (queue *Queue) Connect(args ...interface{}) {
     // 你的连接代码
     doneChans := make([](chan bool), 5)
     for i := 0; i < 5; i++ {
          conn, err := amqp.Dial(uri)
          defer conn.Close()
          if err != nil {
              // 处理错误
          }
          done := action(conn)
     }
     // 这个循环将阻塞,直到5个操作调用完成
     for j := range doneChans {
         isFinish := <-doneChans[j]
         if !isFinish {
             // 处理错误状态
         }
     }
}
英文:

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:

type Queue struct {
    // your original fields
    Conn amqp.Connection
}

// Somewhere else
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:

func action(conn amqp.Connection, args ...interface{}) (&lt;-chan bool) {
    done := make(chan bool)
    go func(amqpConn amqp.Connection, dChan chan bool){
        // Do what you want with the connection
        dChan &lt;- true
    }(conn, done)
    return done
}

func (queue *Queue) Connect(args ...interface{}) {
     // your connection code
     doneChans := make([](chan bool), 5)
     for i := 0; i &lt; 5; i++ {
          conn, err := amqp.Dial(uri)
          defer conn.Close()
          if err != nil {
              // handle error
          }
          done := action(conn)
     }
     // This for loop will block until the 5 action calls are done
     for j := range doneChans {
         isFinish := &lt;-doneChans[j]
         if !isFinish {
             // handle bad state
         }
     }
}

答案2

得分: 1

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

package driver

// 导入等

func (queue *Queue) Connect(args ...interface{}) (amqp.Connection, error) {
    // ...

    conn, err := amqp.Dial(uri)
    if err != nil {
        return nil, err
    }

    // ...

    return conn, nil
}

然后在另一个包中

package stuff

// 导入等

func doStuff() {
    queue := driver.NewQueue()

    conn, err := queue.Connect(args...)
    if err != nil {
        log.Fatalf("出错了!%v!", err)
    }

    defer conn.Close()

    // 做一些事情
}
英文:

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

package driver

// imports, etc

func (queue *Queue) Connect(args ...interface{}) amqp.Connection, error {
    // ...

    conn, err := amqp.Dial(uri)
    if err != nil {
        return nil, err
    }

    // ...

    return conn, nil
}

Then in another package:

package stuff

// imports, etc

func doStuff() {
    queue = driver.NewQueue()

    conn, err := queue.Connect(args...)
    if err != nil {
        log.Fatalf(&quot;oh no! %v!&quot;, err)
    }

    defer conn.Close()

    // Do stuff
}

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:

确定