引用 maxmind golang 库导致的程序无法 recover crash 的问题

新做的 Gateway 程序打算使用一个 maxmind 第三方库来解析地理信息,想了一下比较简单找了一个库直接使用。

项目跑了一天得到了一堆 panic,程序崩溃超过 1s 丢了不少数据。

 从 stack 信息可以看到调用 amxminddb-golang 这个库的 readLeft 出现了错误,最后抛出了一个 unexcepted fault address。我搜了一下 recover 不了的原因,像这样的错误系统直接抛出的会直接 fatal 而不是 panic. 导致 recover 失效。

于是联系了一下作者想看看其他人有没有遇到这个情况。

跟作者沟通了一下,发现作者在 open mmdb file 的时候使用了 mmap 方法,而我的环境似乎不能完全兼容 mmap。我推测内存错误应该是在调用指定地址的时候内存被回收导致的。

于是尝试将 db 里面的 geo 数据一次性读取到内存中。我测试了两天没有再出现上面的情况,所以应该是使用 mmap 的问题。查看源码作者使用了 mmap_unix 包中的 mmap 方法。

func Open(file string) (*Reader, error) {
    mapFile, err := os.Open(file)
    if err != nil {
        _ = mapFile.Close()
        return nil, err
    }

    stats, err := mapFile.Stat()
    if err != nil {
        _ = mapFile.Close()
        return nil, err
    }

    fileSize := int(stats.Size())
    mmap, err := mmap(int(mapFile.Fd()), fileSize)
    if err != nil {
        _ = mapFile.Close()
        return nil, err
    }

使用 mmap 的好处显而易见,可以极大的节省常驻内存空间。对比我直接将数据装载进内存,一来一回少用 300m。但是在第三方库中使用也有方向,因无法确定调用者的系统环境,报出 fatal 错误将使调用者环境直接崩溃,无法捕捉。

 

最后作者还提供了一个方法帮助我从内存崩溃中恢复,将 runtime.SetPanicOnFault 至为 true 可以终止 fatal 错误报出 panic, 这样就可以被 recover 捕捉。

func SetPanicOnFault ¶
added in go1.3
func SetPanicOnFault(enabled bool) bool
SetPanicOnFault controls the runtime's behavior when a program faults at an unexpected (non-nil) address. Such faults are typically caused by bugs such as runtime memory corruption, 
so the default response is to crash the program. Programs working with memory-mapped files or unsafe manipulation of memory may cause faults at non-nil addresses in less dramatic situations;
SetPanicOnFault allows such programs to request that the runtime trigger only a panic, not a crash. The runtime.Error that the runtime panics with may have an additional method:

不过我还是选择将数据装入内存,这样可以暂时避免由内存导致的 fatal 问题。

 

 

Reference:

https://github.com/oschwald/maxminddb-golang/issues/104

https://www.cnblogs.com/luozhiyun/p/15585415.html

 

posted @ 2023-04-26 12:02  piperck  阅读(98)  评论(0编辑  收藏  举报