初识进程 coredump(以中间件为例) 异常宕机

用户头像
清康
关注
发布于: 2020 年 07 月 27 日
初识进程coredump(以中间件为例)异常宕机

在日常工作中我们有可能遇到中间件实例或者其他进程宕机的情况,但往往很多人听说进程宕机事件后惊恐指数就直线拉升,因为我们往往"激进和过于恐慌的"定论宕机事件无迹可寻。其实未必,我们可以提前做一些工作,当遇到这种情况的时候也好相对从容相对专业一点跟进。

    这篇文章主要是模拟进程实例宕机,用一个预知会导致宕机事件的应用部署到中间件,然后解答宕机后怎么寻迹的过程。演示程序是一个简单的Web应用,在Servlet中通过JNI(Java Native Interface)调用C语言程序,C语言程序是铁定导致宕机的。

    通过阅读这篇文章,你应该能了解到JNI相关的知识,以及能够知道如果“被告知中间件进程宕机”时能够知道怎么“淡定的”跟进。

准备C程序

1、Java JNI接口定义,如下所示这个JNI接口类的getNormalValue和coredump两个方法犹如他们的名字一样,第一个方法是正常返回,第二个方法被调用的话是会导致中间件实例宕机引发coredump的

package com.bes.test;
public class CoredumpMe {
public CoredumpMe(String soRootPath){
System.out.println("Load the JNI sharelib.");
String soPath = soRootPath + "/coredump.so";
System.load(soPath);
System.out.println("Loaded the JNI sharelib(coredump.so), fullpath is:" + soPath);
}
/**
* 就这个native关键字,标记了这个接口
*/
public native String getNormalValue();
/**
* 就这个native关键字,标记了这个接口
*/
public native String coredump();
}

2、通过javac编译好如上接口类之后需要使用javah生成头文件,命令为:javah com.bes.test.CoredumpMe,生成的C语言头文件内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_bes_test_CoredumpMe */
#ifndef _Included_com_bes_test_CoredumpMe
#define _Included_com_bes_test_CoredumpMe
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_bes_test_CoredumpMe
* Method: getNormalValue
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_bes_test_CoredumpMe_getNormalValue
(JNIEnv *, jobject);
/*
* Class: com_bes_test_CoredumpMe
* Method: coredump
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_bes_test_CoredumpMe_coredump
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

3、之后是正式的C程序了,C程序对应上面的头文件,并实现getNormanValue和codedump方法。代码比较简单,也是遵从JNI实现。getNormalValue直接范围一个"hello jni(java)"的字符串,而coredump方法则通过访问非法的内存地址导致进程宕机。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "com_bes_test_CoredumpMe.h"
JNIEXPORT jstring JNICALL Java_com_bes_test_CoredumpMe_getNormalValue(JNIEnv *env, jobject obj) {
return (*env)->NewStringUTF(env,"hello jni(java)");
}
JNIEXPORT jstring JNICALL Java_com_bes_test_CoredumpMe_coredump(JNIEnv *env, jobject obj) {
long a=0xffff;
long* buf =(long*)a;
*buf=1;
printf("buf is %p\n",buf);
return (*env)->NewStringUTF(env,"never arrive me.");
}

4、编译动态库,将上面的C程序和C头文件放置到同一个文件夹。然后通过命令进行编译:gcc -fPIC -g -shared -I /home/xuqingkang/jdk1.8.0_211/include -I /home/xuqingkang/jdk1.8.0_211/include/linux/ -o coredump.so jnitest.c。需要说明的是JNI本身需要JDK的支持,JDK的头文件需要指定位置,因此需要根据你的环境(JDK的安装路径)配置-I(大写的i字母)参数。

准备Java Web程序

1、前文中已经编译好了C程序动态库,而且第一步中已经看出了Java是需要加载该动态库的。所以我们将动态库的so直接拷贝到web应用的根目录即可,然后书写一个Servlet调用上一步的CoredumpMe即可。

2、Servlet代码相对比较简单,这里就贴关键代码段。其中request.getRealPath("/")拿到应用包部署后的路径,然后传递给CoredumpMe,CoredumpMe就用这个路径加上coredump.so进行动态库的加载。

public class CoredumpServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
......
/**
* @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
*/
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String realPath = request.getRealPath("/");
CoredumpMe coredumpMe = new CoredumpMe(realPath);
response.getWriter().write(coredumpMe.coredump());
response.getWriter().flush();
// TODO Auto-generated method stub
}

最后打包后的war包主要结构如下,需要注意coredump.so的位置。这个war包在任何一个Web中间件中均可部署运行。



部署和验证、以及寻迹

  部署相对比较简单,选用任何一个Web中间件(比如Tomcat)进行war包部署即可,部署之后可以访问

        http://ip:port/jniTest/normal,如果界面返回"hello jni(java)"则说明测试应用访问成功。

        http://ip:port/jniTest/coredump,则会因为调用了异常的C程序导致中间件实例宕机。



一般来说,中间件实例宕机后生成的coredump文件默认存储在实例的logs或bin路径,需要根据具体的中间件而定,我所测试的自己公司宝兰德中间件为例,路径示例为:/home/xuqingkang/bes952/nodes/node130/instances/cluInstance1/bin/core。此外bin路径下还会有一个hserrpid1886.log的文件。

现在就到了查看这两个文件中到底是什么内容了,查看hserrpid文件,如下所示能看出导致coredump的代码片段。

然后通过gdb调试coredump文件,因中间件进程是java进程,因此gdb命令后需要指定java全路径。其中bt命令可以看到函数的调用栈,相信结合之前的C程序和Web应用包程序大家应该能看明白这里的提示,对于我们诊断coredump有很大的借鉴意义。

结束语

这篇文章主要通过实际的例子演示coredump,现实工作中的coredump往往比这个复杂。但至少我们通过这个简单的例子对应代码和coredump文件片段,看完后应该对coredump产生后的解决思路有一定的梳理和参考。



此外,对coredump文件的生成有前提条件的,如果coredump文件没有生成,可参考如下:

在中间件实例所在主机运行ulimit -a检查core file文件大小设置,假设配置项为0(如”core file size (blocks, -c) 0”),则coredump文件不会生成。
此时,需要修改配置,如将corefile文件大小设置为2G(如“core file size (blocks, -c) 4194304”). ------1个block为512kb
方法为运行命令:
echo " * soft core 4194304" >> /etc/security/limits.conf
echo " * hard core 4194304" >> /etc/security/limits.conf



zcoredump文件名称格式可以通过修改/proc/sys/kernel/core_pattern文件进行定义,如:

echo 'core.%e.%p' > /proc/sys/kernel/core_pattern
其中,变量参数参考如下:
%e: 可执行文件名
%p: 进程的PID
%u: real uid of the process
%g: real gid of the process
%t: the time when the core is dumped
%h: host name
%c: the max size of the core dump file



用户头像

清康

关注

还未添加个人签名 2017.11.30 加入

还未添加个人简介

评论

发布
暂无评论
初识进程coredump(以中间件为例)异常宕机