zoukankan      html  css  js  c++  java
  • 在k8s中导出jvm内存错误dump文件到OSS

    现状

    加参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/test.dump 可以实现在jvm发生内存错误后 会生成dump文件 方便开发人员分析异常原因。

    当运行在k8s中,如果进程发生错误 导出dump文件后 ,k8s会重启dokcer容器,上一次崩溃生成的dump文件就没有了。如果应用并没有完全崩溃 此时极其不稳定 最好也能通知到技术人员来处理。这样不方便我们排查原因 所有写了一个小工具。大概原理如下

    1、 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/test.dump 当发生内存错误的时候 导出堆文件
    2、 -XX:OnOutOfMemoryError=./dumpError.sh 当发生内存溢出的时候,让JVM调用一个shell脚本 这个shell脚本可以做一些资源整理操作 比如kill掉当前进程并重启

    依赖上面2点jvm特性 就能做到把dump文件收集起来 是通知技术人员也好(比如发送订单、短信报警等)、然后再把dump文件上传到OSS 或者其他的文件存储中。 需要值得注意的是-XX:OnOutOfMemoryError=xx.sh 执行的脚本不能传脚本参数,所以尽可能把参数都封装在另一个脚本中。

    方案实现

    基于Go简单的写了一个上传阿里OSS的方法 这里用其他任何语言都可以的,至于用GO的原因很简单,有第三方库可以调用、运行的机器上也不用安装sdk、比较轻量。
    大致逻辑如下

    jvmdump.go

    init获取程序的输入参数

    func init(){
    	fmt.Println("init....")
    	flag.StringVar(&env, "env", "test", "test") //用于区分环境 
    	flag.StringVar(&ddtoken, "ddtoken", "", "ddtoken") //用于报警用的 钉钉机器人TOKEN
    	flag.StringVar(&dumpFile, "dfile", "", "dfile") // dump文件的地址
    	flag.StringVar(&pod, "pod", "", "pod") //k8s中的pod  只是记录一下 方便排查 
    }
    
    

    main函数逻辑

    
    func main() {
    	fmt.Println("start invoke dump...")
    	flag.Parse() //解析输入参数
    	fmt.Printf("dumpFile %s ,env %s token %s\n",dumpFile,env,ddtoken)
    	exist, err := FileExists(dumpFile) //验证dump文件是否存在 只有存在的时候才去处理收集dump文件逻辑
    	if err != nil {
    		fmt.Printf("验证文件是否存在发生错误![%v]\n", err)
    		return
    	}
    	if exist {
    		//https://help.aliyun.com/document_detail/88604.html
    		var url=uploadOSS(dumpFile) //上传阿里oss
    		fmt.Printf("OSS上传完成 %s\n", url)
    		if enabledd{
    		//钉钉群机器人发送工具 https://github.com/braumye/grobot 
    			notifyDD(url) //通知钉钉群机器人
    		}
    	}else{
    		fmt.Printf("dump文件不存在 %s\n",dumpFile)
    	}
    }
    
    

    构建可执行文件

    
      set GOOS=linux
      go build -ldflags "-w -s"
    
    

    测试 验证go脚本是否正确

      echo "ffff">/opt/ttt.dump
     ./jvmdump -env test -dfile /opt/ttt.dump
    

    如果能成功上传 就可以集成到jvm上跑了,不能成功上传的话 就需要调一下go了。

    另外分享一个-XX:OnOutOfMemoryError=./dumpError.sh 参考。

    有这个shell的原因是因为 由于jvmOnOutOfMemoryError目前没有找到可以传递脚本参数的方法。 所有不能调用./jvmdump文件 故包装一下,把参数都封装在dempError.sh中 ,把所有生成的dump文件 后缀命名都设置为.dump,主要是为了方便查找。放在一个独立的目录也是可以的。

    dumpError.sh

    #!/bin/bash
    
    #循环目录
    traverse_dir()
    {
        filepath=$1
    
        for file in `ls -a $filepath`
        do
            if [ -d ${filepath}/$file ]
            then
                if [[ $file != '.' && $file != '..' ]]
                then
                    #递归
                    traverse_dir ${filepath}/$file
                fi
            else
                #调用查找指定后缀文件
                check_suffix ${filepath}/$file
            fi
        done
    	#看需要 可以kill掉进程,避免jvm没有完全崩溃 k8s不会重启pod的情况 造成应用假死问题。
    }
    
    #查找指定后缀的文件 这里在k8s环境里一般只会有一个dump文件,如果可能存在多个的dump文件文件的情况 可能需要变更一下逻辑
    check_suffix()
    {
        file=$1
    
    	#如果找到dump就调用go写的jvmdump脚本 
        if [ "${file##*.}"x = "dump"x ];then
            lib/jvmdump -e test -dfile $file -pod $HOSTNAME -ddtoken xxx
        fi
    }
    traverse_dir /opt/logs
    
    

    完整代码参考

    https://github.com/peachyy/jvmdump4k8s.git

    转载请注明出处。

    作者:peachyy

    出处:http://www.cnblogs.com/peachyy/

  • 相关阅读:
    数据库连接池
    Apache- DBUtils框架学习
    权限表的设计
    Java的I/O对文件的操作
    Java下载文件
    Java连接MySQL数据库
    C#用log4net记录日志
    C#多线程和线程池
    C#利用反射动态调用DLL并返回结果,和获取程序集的信息
    CephRGW 在多个RGW负载均衡场景下,RGW 大文件并发分片上传功能验证
  • 原文地址:https://www.cnblogs.com/peachyy/p/15539217.html
Copyright © 2011-2022 走看看