故障排查
本指南汇总使用 Fory Go 时的常见问题与解决方案。
错误类型
Fory Go 使用带错误种类的强类型错误:
type Error struct {
kind ErrorKind
message string
// 额外上下文字段
}
func (e Error) Kind() ErrorKind { return e.kind }
func (e Error) Error() string { return e.message }
错误种类
| Kind | 值 | 说明 |
|---|---|---|
ErrKindOK | 0 | 无错误 |
ErrKindBufferOutOfBound | 1 | 读写越过缓冲区边界 |
ErrKindTypeMismatch | 2 | Type ID 不匹配 |
ErrKindUnknownType | 3 | 遇到未知类型 |
ErrKindSerializationFailed | 4 | 通用序列化失败 |
ErrKindDeserializationFailed | 5 | 通用反序列化失败 |
ErrKindMaxDepthExceeded | 6 | 递归深度超 过限制 |
ErrKindNilPointer | 7 | 意外的空指针 |
ErrKindInvalidRefId | 8 | 非法引用 ID |
ErrKindHashMismatch | 9 | 结构体 hash 不匹配 |
ErrKindInvalidTag | 10 | 非法 fory 结构体 tag |
常见错误与解决方案
ErrKindUnknownType:未知类型
错误:unknown type encountered
原因:序列化或反序列化前未注册类型。
解决方式:
f := fory.New()
// 使用前先注册类型
f.RegisterStruct(User{}, 1)
// 之后即可正常序列化
data, _ := f.Serialize(&User{ID: 1})
ErrKindTypeMismatch:类型不匹配
错误:type mismatch: expected X, got Y
原因:序列化数据中的类型与接收端期望的类型不一致。
解决方式:
- 使用正确的目标类型:
// 错误:把 User 数据反序列化到 Order
var order Order
f.Deserialize(userData, &order)
// 正确
var user User
f.Deserialize(userData, &user)
- 保证注册信息一致:
// 序列化端
f1 := fory.New()
f1.RegisterStruct(User{}, 1)
// 反序列化端,必须使用相同 ID
f2 := fory.New()
f2.RegisterStruct(User{}, 1)
ErrKindHashMismatch:结构体 hash 不匹配
错误:hash X is not consistent with Y for type Z
原因:序列化端和反序列化端使用的结构体定义不一致。
解决方式:
- 开启兼容模式:
f := fory.New(fory.WithCompatible(true))
- 确保结构体定义一致:
// 两端必须使用相同结构体定义
type User struct {
ID int64
Name string
}
- 如果使用代码生成,重新生成产物:
go generate ./...
ErrKindMaxDepthExceeded:超过最大深度
错误:max depth exceeded
原因:数据嵌套层级超过了最大深度限制。
常见成因:
- 深层嵌套数据超过默认限制(20)
- 未开启引用跟踪时意外出现循环引用
- 恶意数据:攻击者可能构造极深嵌套载荷,导致资源耗尽
解决方式:
- 提高最大深度限制(默认值为 20):
f := fory.New(fory.WithMaxDepth(50))
- 对循环数据启用引用跟踪:
f := fory.New(fory.WithTrackRef(true))
-
检查数据中是否存在意外循环引用。
-
处理不可信输入时不要盲目调大深度限制,应先校验输入大小与结构。
ErrKindBufferOutOfBound:缓冲区越界
错误:buffer out of bound: offset=X, need=Y, size=Z
原因:读取超出了现有数据长度。
解决方式:
- 确保传输的是完整数据:
// 错误:数据被截断
data := fullData[:100]
f.Deserialize(data, &target)
// 正确:使用完整数据
f.Deserialize(fullData, &target)
- 检查传输过程中是否出现数据损坏。
ErrKindInvalidRefId:非法引用 ID
错误:invalid reference ID
原因:序列化数据中引用了不存在或未知的对象。
解决方式:
- 确保序列化端与反序列化端的引用跟踪配置一致:
f1 := fory.New(fory.WithTrackRef(true))
f2 := fory.New(fory.WithTrackRef(true)) // 必须一致
- 检查数据是否损坏。
ErrKindInvalidTag:非法 struct tag
错误:invalid fory struct tag
原因:结构体 tag 配置非法。
常见成因:
- 非法 tag ID:ID 必须大于等于
-1
// 错误:非法负数 ID(-1 以外)
type Bad struct {
Field int `fory:"id=-5"`
}
// 正确
type Good struct {
Field int `fory:"id=0"`
}
- 重复 tag ID:同一结构体中的字段 ID 必须唯一
// 错误:ID 冲突
type Bad struct {
Field1 int `fory:"id=0"`
Field2 int `fory:"id=0"`
}
// 正确
type Good struct {
Field1 int `fory:"id=0"`
Field2 int `fory:"id=1"`
}
跨语言问题
字段顺序不一致
现象:能够反序列化,但字段值落到了错误的位置。
原因:不同语言的字段排序规则不一致。非兼容模式下,字段会按 snake_case 名称排序。比如 FirstName 会先转换为 first_name 再参与排序。
解决方式:
- 确保转换后的 snake_case 名称一致:
type User struct {
FirstName string // Go: FirstName -> first_name
LastName string // Go: LastName -> last_name
// 最终按 snake_case 的字典序排序:first_name, last_name
}
- 使用字段 ID 保证顺序一致。字段 ID(非负整数)会同时参与排序和反序列化匹配:
type User struct {
FirstName string `fory:"id=0"`
LastName string `fory:"id=1"`
}
对应字段在所有语言里都应使用相同的字段 ID。
名称注册不一致
现象:其他语言反序列化时报 unknown type。
解决方式:保证注册名称完全一致:
// Go
f.RegisterNamedStruct(User{}, "example.User")
// Java,必须完全一致
fory.register(User.class, "example.User");
// Python
fory.register(User, typename="example.User")
性能问题
序列化速度慢
可能原因:
- 对象图太大:减少数据规模或改为增量序列化。
- 引用跟踪过多:如果不需要,可以关闭。
f := fory.New(fory.WithTrackRef(false))