英文:
golang rpc not returning when struct has a nested struct
问题
更新
从gob编码转换为json修复了问题。但是我仍然想知道为什么使用gob时会失败。
所以我的客户端代码看起来像这样:
account := new(database.Account)
err := client.Call("AccountDb.FindAccount", "username", account)
if err != nil {
logger.FATAL.Print(err.Error())
return
}
logger.INFO.Print(account)
在服务器端,AccountDb.FindAccount
看起来像这样:
func (t *AccountDb) FindAccount(args *string, reply *Account) error {
reply.Username = "this is a test"
return nil
}
Account的结构如下:
type Account struct {
Id int
Username string
Password string
Email string
Created time.Time
LastLoggedIn time.Time
AccessLevel int
Banned struct {
reason string
expires time.Time
}
}
如果我尝试执行rpc请求,请求开始并且服务器执行该过程。然而,程序随后挂起,该过程不返回!但是,如果我从Account结构中删除Banned匿名结构,它就可以正常工作!为什么会这样?有解决这个问题的方法吗?
编辑
客户端和服务器注册代码如下:
客户端
client, err = rpc.DialHTTP("tcp", "127.0.0.1:9001")
if err != nil {
logger.FATAL.Panic(err.Error())
}
服务器
defer db.Close()
account := new(database.AccountDb)
account.Database = db
rpc.Register(account)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":9001")
if e != nil {
logger.FATAL.Fatal("listen error:", e)
}
http.Serve(l, nil)
英文:
UPDATE
moving from gob encoding to json fixed the issue. However I would still like to know why this was failing to work with gob.
so my client code looks like this
account := new(database.Account)
err := client.Call("AccountDb.FindAccount", "username", account)
if err != nil {
logger.FATAL.Print(err.Error())
return
}
logger.INFO.Print(account)
On the server side AccountDb.FindAccount
looks like this
func (t *AccountDb) FindAccount(args *string, reply *Account) error {
reply.Username = "this is a test"
return nil
}
The struct for Account looks like this
type Account struct {
Id int
Username string
Password string
Email string
Created time.Time
LastLoggedIn time.Time
AccessLevel int
Banned struct {
reason string
expires time.Time
}
}
if I attempt to perform the rpc the requests starts and the server executes the procedure. However the program then hangs and the procedure does not return! If however I remove the Banned anonymous struct from the Account struct it works fine! Why is this? is there a solution to this problem?
edit
the client and server register code looks like this
Client
client, err = rpc.DialHTTP("tcp", "127.0.0.1:9001")
if err != nil {
logger.FATAL.Panic(err.Error())
}
Server
defer db.Close()
account := new(database.AccountDb)
account.Database = db
rpc.Register(account)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":9001")
if e != nil {
logger.FATAL.Fatal("listen error:", e)
}
http.Serve(l, nil)
答案1
得分: 6
显然,gob编码器无法对RPC响应进行编组,因为Banned
结构体没有导出的字段。这个playground示例展示了这个错误:
encode error: gob: type struct { reason string; expires time.Time }
has no exported fields
如果通过将reason/expires字段大写来导出它们,gob的往返操作就可以正常工作(参见http://play.golang.org/p/YrYFsk6trQ)。
JSON编码器也要求序列化字段被导出,但如果一个结构体没有导出字段,它不会返回错误。解码只会返回默认/零值。运行http://play.golang.org/p/OBBkB4tPcZ并注意在往返操作中Banned
信息丢失了。
如果你需要在rpc
包中检测这些类型的错误,有两个选项:
- 编辑rpc/debug.go,将
logDebug
标志设置为true,并重新构建rpc
包,或者 - 实现一个包装现有实现的
ServerCodec
,并记录ReadRequestBody
/WriteResponse
返回的任何错误。
英文:
Evidently, the gob encoder fails to marshal the RPC response because the Banned
struct has no exported fields. This playground example exhibits the error:
encode error: gob: type struct { reason string; expires time.Time }
has no exported fields
If you export the reason/expires fields by capitalizing them, the gob round-trip works OK (see http://play.golang.org/p/YrYFsk6trQ).
The JSON encoder also requires that serialized fields are exported, but it does not return an error if a struct has none. The decode just returns default/zero values. Run http://play.golang.org/p/OBBkB4tPcZ and note that the Banned
information is lost on the round-trip.
If you need to detect these kinds of errors in the rpc
package, there are two options:
- Edit rpc/debug.go, set the
logDebug
flag to true, and rebuild therpc
package, or - Implement a
ServerCodec
that wraps the existing implementation and logs any errors returned byReadRequestBody
/WriteResponse
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论