英文:
Runtime panic on calling resource from another package
问题
我正在尝试使用fiber + zap + pgx创建一个简单的API服务器,但是遇到了一个无法解决的错误。
错误信息如下:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0xcdca67]
goroutine 1 [running]:
go.uber.org/zap.(*Logger).check(0x0, 0xc000060500, 0xff0b96, 0x15, 0x0)
C:/Users/risharan/go/pkg/mod/go.uber.org/zap@v1.18.1/logger.go:268 +0x987
go.uber.org/zap.(*Logger).Info(0x0, 0xff0b96, 0x15, 0x0, 0x0, 0x0)
C:/Users/risharan/go/pkg/mod/go.uber.org/zap@v1.18.1/logger.go:191 +0x4b
freya/src/db.Init()
C:/Users/risharan/Documents/GitHub/freya/src/db/db.go:19 +0x6db
main.init.0()
C:/Users/risharan/Documents/GitHub/freya/src/freya.go:15 +0x31
exit status 2
你的代码如下:
// package main
func init() {
logger.Init()
env.Init()
db.Init()
}
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
log.Fatal(app.Listen(":3000"))
}
// package logger
var Say *zap.Logger
func Init() {
Say, _ := zap.NewProduction()
defer Say.Sync()
}
// package db
var Conn *pgxpool.Pool
func Init() {
c, err := pgxpool.Connect(context.Background(), env.Conf["DATABASE_URL"])
if err != nil {
logger.Say.Fatal("Can't connect to DB", zap.String("Details", err.Error()))
} else {
logger.Say.Info("Connected to Database") // 这是错误信息中的第19行,位于db包中
}
Conn = c
defer Conn.Close()
var greeting string
err = Conn.QueryRow(context.Background(), "select 'Hello, this is db!'").Scan(&greeting)
if err != nil {
logger.Say.Error("DB Query failed", zap.String("Details", err.Error()))
}
logger.Say.Info("Connected to DB. DB says;", zap.String("Details", greeting))
}
错误信息中指示的行数是:
func init() {
logger.Init()
env.Init()
db.Init() // 这是错误信息中的第15行,位于main包中
}
func Init() {
c, err := pgxpool.Connect(context.Background(), env.Conf["DATABASE_URL"])
if err != nil {
logger.Say.Fatal("Can't connect to DB", zap.String("Details", err.Error()))
} else {
logger.Say.Info("Connected to Database") // 这是错误信息中的第19行,位于db包中
}
}
到目前为止,Go的错误信息一直很难解读,我不确定我在这里做错了什么,或者应该如何修复我的代码。
注意:有人建议我使用依赖注入架构。我个人觉得这种模式相当复杂,而且我还在学习Go,所以我希望尽可能保持简单。因此,除非无法修复当前基于全局变量的模式,否则请不要建议我切换模式。
英文:
I am trying to create a simple API server using fiber + zap + pgx
I am getting an error which I am unable to solve.
PS C:\Users\risharan\Documents\GitHub\freya> go run .\src\freya.go
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0xcdca67]
goroutine 1 [running]:
go.uber.org/zap.(*Logger).check(0x0, 0xc000060500, 0xff0b96, 0x15, 0x0)
C:/Users/risharan/go/pkg/mod/go.uber.org/zap@v1.18.1/logger.go:268 +0x987
go.uber.org/zap.(*Logger).Info(0x0, 0xff0b96, 0x15, 0x0, 0x0, 0x0)
C:/Users/risharan/go/pkg/mod/go.uber.org/zap@v1.18.1/logger.go:191 +0x4b
freya/src/db.Init()
C:/Users/risharan/Documents/GitHub/freya/src/db/db.go:19 +0x6db
main.init.0()
C:/Users/risharan/Documents/GitHub/freya/src/freya.go:15 +0x31
exit status 2
The code so far is;
// package main
func init() {
logger.Init()
env.Init()
db.Init()
}
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
log.Fatal(app.Listen(":3000"))
}
// package logger
var Say *zap.Logger
func Init() {
Say, _ := zap.NewProduction()
defer Say.Sync()
}
// package db
var Conn *pgxpool.Pool
func Init() {
c, err := pgxpool.Connect(context.Background(), env.Conf["DATABASE_URL"])
if err != nil {
logger.Say.Fatal("Can't connect to DB", zap.String("Details", err.Error()))
} else {
logger.Say.Info("Connected to Database")
}
Conn = c
defer Conn.Close()
var greeting string
err = Conn.QueryRow(context.Background(), "select 'Hello, this is db!'").Scan(&greeting)
if err != nil {
logger.Say.Error("DB Query failed", zap.String("Details", err.Error()))
}
logger.Say.Info("Connected to DB. DB says; ", zap.String("Details", greeting))
}
The lines indicated by the error message
C:/Users/risharan/Documents/GitHub/freya/src/db/db.go:19 +0x6db
main.init.0()
C:/Users/risharan/Documents/GitHub/freya/src/freya.go:15 +0x31
are:
func init() {
logger.Init()
env.Init()
db.Init() // this is the line 15 as shown in error message in package main
}
and
func Init() {
c, err := pgxpool.Connect(context.Background(), env.Conf["DATABASE_URL"])
if err != nil {
logger.Say.Fatal("Can't connect to DB", zap.String("Details", err.Error()))
} else {
logger.Say.Info("Connected to Database") // this is the line 19 in the error message in the package db
}
So far go's error messages have been hard to decode and I am not sure what am i doing wrong here or what i should do to fix my code.
Note: I have been told to go for a Dependency Injection arch. I personally find that pattern rather complex, and as I am still learning go, i'd prefer to keep it as simple as I can for now. So would prefer answers to not advise me to switch patterns, unless it is not possible to fix the current global variable based pattern.
答案1
得分: 4
你的各种Init()
函数中至少存在两个问题。
-
你使用了
:=
,它在函数作用域内声明了一个新的变量,并不会修改你在作用域外声明的变量。 -
你在
Init()
函数内部使用了defer
,这意味着当Init()
返回时,延迟执行的函数将被执行。这实际上意味着你的Init
函数创建并销毁资源,使其对任何后续代码都无用。
如果你想要给在外部作用域声明的变量赋值,不要使用:=
,而是使用=
。如果你打算在该函数之外使用conn
,则不要在初始化函数内部执行defer conn.Close()
等操作。
var Say *zap.Logger
func Init() {
Say, _ = zap.NewProduction()
}
var Conn *pgxpool.Pool
func Init() {
c, err := pgxpool.Connect(context.Background(), env.Conf["DATABASE_URL"])
if err != nil {
logger.Say.Fatal("无法连接到数据库", zap.String("详情", err.Error()))
} else {
logger.Say.Info("已连接到数据库")
}
Conn = c
var greeting string
err = Conn.QueryRow(context.Background(), "select 'Hello, this is db!'").Scan(&greeting)
if err != nil {
logger.Say.Error("数据库查询失败", zap.String("详情", err.Error()))
}
logger.Say.Info("已连接到数据库。数据库返回:", zap.String("详情", greeting))
}
func init() {
logger.Init()
env.Init()
db.Init()
}
func main() {
defer func() {
db.Conn.Close()
logger.Say.Sync()
}()
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
log.Fatal(app.Listen(":3000"))
}
英文:
At least two problems in your various Init()
functions.
-
You're using
:=
which declares a new variable within the scope of the function and does not modify the variable that you declared outside of the scope. -
You're using
defer
insideInit()
which is where the defered function will be executed upon whenInit()
returns. This means, essentially, that yourInit
creates and then destroys the resource, making it useless for any subsequent code.
If you want to assign to a variable that's declared in an outer scope do NOT use :=
, instead use =
. And do not do things like defer conn.Close()
inside an initializing function if you intend to use conn
outside of that function.
var Say *zap.Logger
func Init() {
Say, _ = zap.NewProduction()
}
var Conn *pgxpool.Pool
func Init() {
c, err := pgxpool.Connect(context.Background(), env.Conf["DATABASE_URL"])
if err != nil {
logger.Say.Fatal("Can't connect to DB", zap.String("Details", err.Error()))
} else {
logger.Say.Info("Connected to Database")
}
Conn = c
var greeting string
err = Conn.QueryRow(context.Background(), "select 'Hello, this is db!'").Scan(&greeting)
if err != nil {
logger.Say.Error("DB Query failed", zap.String("Details", err.Error()))
}
logger.Say.Info("Connected to DB. DB says; ", zap.String("Details", greeting))
}
func init() {
logger.Init()
env.Init()
db.Init()
}
func main() {
defer func() {
db.Conn.Close()
logger.Say.Sync()
}()
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
log.Fatal(app.Listen(":3000"))
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论