首页 > 编程语言 >引用 maxmind golang 库导致的程序无法 recover crash 的问题

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

时间:2023-04-26 12:11:32浏览次数:60  
标签:crash err nil mapFile mmap golang 内存 maxmind runtime

新做的 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

 

标签:crash,err,nil,mapFile,mmap,golang,内存,maxmind,runtime
From: https://www.cnblogs.com/piperck/p/17355245.html

相关文章

  • IC99680: SEGMENTATION FAULT AND CRASH DURING DSMSERV FORMAT COMMAND
      APARstatusClosedasprogramerror. ErrordescriptionThedsmservformatprocesscancrashwithasegmentationfaultwheninitiatedbyanadministratorduringmanualinstanceconfiguration.Forexample:$/opt/tivoli/tsm/serv......
  • golang 使用 net包实现 tcp server 示例
    之前用到golang进行网络编程时,主要就是使用net/http和web框架gin,这些网络库的底层其实也还是用的标准库自带的net包,很多是对路由或者其他做封装,而且golang本身的长处之一也是网络IO的处理,这也得益于其底层的IO模型,今天我们分享的是基于TCPserver/client的简单实现,后......
  • Golang - time包获取当前日期/时间戳并格式化输出
    获取时间对象packagemainimport("fmt""time")//24小时制constLAYOUT="2006-01-0215:04:05"//只能用这个日期,据说这是golang的诞生时间funcmain(){//获取当前日期now:=time.Now()fmt.Printf(now.Format(LAYOUT))//2022......
  • golang 中通过strings/bytes/bufio 等包实现相关IO
    在go的IO中,除了io、os等包,我们还有strings、bytes、bufio等实现IO读写,这其中有些实现了io.Reader,有些同时实现了io.Reader和io.Writer接口。接下来我们一个个看相关的使用。1.strings在strings包中,只实现了Reader,我们看看其代码://实现的readertypeRea......
  • golang 通过 os 包进行文件读写
    go中os包主要与操作系统打交道,实际底层还是通过相关的系统调用实现文件的读写操作,今天我们就来聊聊通过os包实现文件的读写操作。我们在使用os包进行相关操作时,主要流程:读操作open->read->close写操作open->read->write->close总体来说,读写操作都......
  • [译]Golang template 小抄
    [译]Golangtemplate小抄目录[−]解析和创建模板命名模板创建模板解析多个模板解析字符串模板执行模板执行简单模板执行命名的模板模板编码和HTML上下文编码安全字符串和HTML注释模板变量.字符模板中的变量模板动作if/else语句移除空格Range......
  • 1 Golang入门
    目录1Golang入门1Golang介绍1.1简介1.2特性1.3go语言发展1.4go语言的应用2go2.1包2.2main包2.3go程序的运行方式2.4声明包与包导入1Golang入门1Golang介绍1.1简介Go即Golang,是Google公司2009年11月正式对外公开的一门编程语言Go是【静态强类型】语言,是区别于......
  • 2 Golang语法基础
    目录2Golang语法基础1变量命名规范2语法规则:变量的定义与使用①完整定义②类型推导:自动推导出变量,是什么类型③简略声明:2Golang语法基础1变量命名规范Go语言中的函数名、变量名、常量名、类型名和包名等所有的命名,都遵循一个简单的命名规则:①一个名字必须以一个字......
  • idea - 在Terminal 交叉编译golang 解决
    1. 背景在ideaTerminal栏执行gobuildxxx.go打包的是exe文件交叉编译配置SETCGO_ENABLE=0SETGOOS=linuxSETGOARCH=arm64gobuildxxx.go发现编译文件仍然是exe2.解决需要切换为cmd指令执行cmd然后在执行交叉编译即可 ......
  • 基于golang实现ssh terminal
    基于golang实现sshterminal实现sshterminal相对比较容易,简单来说需要初始化ssh连接后,通过ssh连接创建一个会话,定义好输入、输出,然后再请求pty(需要定义好modes)与远程会话进行关联。packagemainimport( "fmt" "github.com/gin-gonic/gin" "github.com/gorilla/websocket"......