当结构体中包含嵌套结构体时,Golang的RPC调用可能不会返回。

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

golang rpc not returning when struct has a nested struct

问题

更新

从gob编码转换为json修复了问题。但是我仍然想知道为什么使用gob时会失败。

所以我的客户端代码看起来像这样:

  1. account := new(database.Account)
  2. err := client.Call("AccountDb.FindAccount", "username", account)
  3. if err != nil {
  4. logger.FATAL.Print(err.Error())
  5. return
  6. }
  7. logger.INFO.Print(account)

在服务器端,AccountDb.FindAccount看起来像这样:

  1. func (t *AccountDb) FindAccount(args *string, reply *Account) error {
  2. reply.Username = "this is a test"
  3. return nil
  4. }

Account的结构如下:

  1. type Account struct {
  2. Id int
  3. Username string
  4. Password string
  5. Email string
  6. Created time.Time
  7. LastLoggedIn time.Time
  8. AccessLevel int
  9. Banned struct {
  10. reason string
  11. expires time.Time
  12. }
  13. }

如果我尝试执行rpc请求,请求开始并且服务器执行该过程。然而,程序随后挂起,该过程不返回!但是,如果我从Account结构中删除Banned匿名结构,它就可以正常工作!为什么会这样?有解决这个问题的方法吗?

编辑
客户端和服务器注册代码如下:

客户端

  1. client, err = rpc.DialHTTP("tcp", "127.0.0.1:9001")
  2. if err != nil {
  3. logger.FATAL.Panic(err.Error())
  4. }

服务器

  1. defer db.Close()
  2. account := new(database.AccountDb)
  3. account.Database = db
  4. rpc.Register(account)
  5. rpc.HandleHTTP()
  6. l, e := net.Listen("tcp", ":9001")
  7. if e != nil {
  8. logger.FATAL.Fatal("listen error:", e)
  9. }
  10. 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

  1. account := new(database.Account)
  2. err := client.Call("AccountDb.FindAccount", "username", account)
  3. if err != nil {
  4. logger.FATAL.Print(err.Error())
  5. return
  6. }
  7. logger.INFO.Print(account)

On the server side AccountDb.FindAccount looks like this

  1. func (t *AccountDb) FindAccount(args *string, reply *Account) error {
  2. reply.Username = "this is a test"
  3. return nil
  4. }

The struct for Account looks like this

  1. type Account struct {
  2. Id int
  3. Username string
  4. Password string
  5. Email string
  6. Created time.Time
  7. LastLoggedIn time.Time
  8. AccessLevel int
  9. Banned struct {
  10. reason string
  11. expires time.Time
  12. }
  13. }

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

  1. client, err = rpc.DialHTTP("tcp", "127.0.0.1:9001")
  2. if err != nil {
  3. logger.FATAL.Panic(err.Error())
  4. }

Server

  1. defer db.Close()
  2. account := new(database.AccountDb)
  3. account.Database = db
  4. rpc.Register(account)
  5. rpc.HandleHTTP()
  6. l, e := net.Listen("tcp", ":9001")
  7. if e != nil {
  8. logger.FATAL.Fatal("listen error:", e)
  9. }
  10. http.Serve(l, nil)

答案1

得分: 6

显然,gob编码器无法对RPC响应进行编组,因为Banned结构体没有导出的字段。这个playground示例展示了这个错误:

  1. encode error: gob: type struct { reason string; expires time.Time }
  2. has no exported fields

如果通过将reason/expires字段大写来导出它们,gob的往返操作就可以正常工作(参见http://play.golang.org/p/YrYFsk6trQ)。

JSON编码器也要求序列化字段被导出,但如果一个结构体没有导出字段,它不会返回错误。解码只会返回默认/零值。运行http://play.golang.org/p/OBBkB4tPcZ并注意在往返操作中Banned信息丢失了。

如果你需要在rpc包中检测这些类型的错误,有两个选项:

  1. 编辑rpc/debug.go,将logDebug标志设置为true,并重新构建rpc包,或者
  2. 实现一个包装现有实现的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:

  1. encode error: gob: type struct { reason string; expires time.Time }
  2. 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:

  1. Edit rpc/debug.go, set the logDebug flag to true, and rebuild the rpc package, or
  2. Implement a ServerCodec that wraps the existing implementation and logs any errors returned by ReadRequestBody/WriteResponse.

huangapple
  • 本文由 发表于 2014年6月10日 23:32:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/24145058.html
匿名

发表评论

匿名网友

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

确定