英文:
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{}) (<-chan bool) {
done := make(chan bool)
go func(amqpConn amqp.Connection, dChan chan bool){
// Do what you want with the connection
dChan <- true
}(conn, done)
return done
}
func (queue *Queue) Connect(args ...interface{}) {
// your connection code
doneChans := make([](chan bool), 5)
for i := 0; i < 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 := <-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("oh no! %v!", err)
}
defer conn.Close()
// Do stuff
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论