웹 어플리케이션에 hang이 걸리거나 제대로 뜨지 않을 때 원인분석 할 수 있는 좋은 방법은 Thread Dump 를 생성하여 해당 덤프를 분석하는 방법이다.
하지만 문제가 되는 시점에 Thread Dump 를 생성해야 하며 외부에서 맘대로 서버에 접속 할 수 없는 상황이라면 Thread Dump 를 생성하는 jsp 페이지를 하나 만들어 호출하는 방법이 있다.
방법은 다음과 같다.
아래와 같이 현재 수행중인 WAS(tomcat, jeus) 의 PID 를 알아내는 스크립트를 서버의 특정 위치에 저장한다.
(SERVICE_NAME 부분은 각각의 서버 상태에 따라 문자열을 변경해 주어야 한다. 하단에 설명추가)
GIT Source: https://github.com/1004lucifer/JSP_Create_ThreadDump
pid.sh
ps -ef | grep java | grep SERVICE_NAME | awk '{printf $2}'
dump.jsp
<%@ page import="java.util.Date" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<%!
private String executeCommand(String command, String seperator) {
StringBuffer result = new StringBuffer();
String line = null;
BufferedReader br = null;
Process ps = null;
try {
Runtime rt = Runtime.getRuntime();
ps = rt.exec(command);
br = new BufferedReader(new InputStreamReader(new SequenceInputStream(ps.getInputStream(), ps.getErrorStream())));
while( (line = br.readLine()) != null ) {
result.append(line + seperator);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try { br.close(); } catch(Exception e) {}
}
return result.toString();
}
private void saveLog(String file, String contents) {
FileOutputStream fos = null;
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String dt = sdf.format(now);
try {
fos = new FileOutputStream(file+"_"+dt.substring(0, 8)+".log", true); // true:이어쓰기, false:덮어쓰기
for(int i=0; i<contents.length(); i++){
fos.write(contents.charAt(i));
}
fos.write('\r');
fos.write('\n');
} catch (Exception e) {
e.printStackTrace();
} finally{
if(fos != null){
try{fos.close();
}catch(IOException e){}
}
}
}
%>
<body>
<%
// pid.sh 가 위치한 경로
String commandGetPid = "/opt/tomcat/project_name/threadDump/pid.sh";
// ThreadDump 가 기록될 경로
String logPath = "/opt/tomcat/project_name/threadDump/threadDump_tservice_api";
String commandThreadDump = "jstack "; // ThreadDump 를 생성하는 명령어
String pid = null;
String threadDump = null;
try{
pid = executeCommand(commandGetPid, "");
threadDump = pid == null ? null : executeCommand(commandThreadDump + pid, "\n");
if (threadDump != null) {
saveLog(logPath, threadDump);
out.println(threadDump.replaceAll("\n", "<br/>"));
}
}catch(Exception e){
e.printStackTrace();
}
%>
</body>
</html>
PS.
1. pid.sh 추가설명
아래와 같이 해당 WAS 의 pid를 가져오기 위해 awk 명령어를 이용했으며 서버 하나에서 구동하는 WAS가 두개 이상이라면 grep 명령어로 하나만 나올 수 있도록 pid.sh 를 변경해 주면 된다.
[1004lucifer@1004lucifer ~]$ ps -ef | grep java
500 6419 6391 0 12:06 pts/1 00:00:00 grep java
root 19629 1 0 2014 ? 02:45:42 /usr/bin/java -Djava.util.logging.config.file=/opt/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -Xms1024m -Xmx2048m -XX:PermSize=256m -XX:MaxPermSize=512m -Djava.endorsed.dirs=/opt/tomcat/endorsed -classpath /opt/tomcat/bin/bootstrap.jar -Dcatalina.base=/opt/tomcat -Dcatalina.home=/opt/tomcat -Djava.io.tmpdir=/opt/tomcat/temp org.apache.catalina.startup.Bootstrap start
[1004lucifer@1004lucifer ~]$
[1004lucifer@1004lucifer ~]$ ps -ef | grep java | grep Bootstrap
root 19629 1 0 2014 ? 02:45:42 /usr/bin/java -Djava.util.logging.config.file=/opt/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -server -Xms1024m -Xmx2048m -XX:PermSize=256m -XX:MaxPermSize=512m -Djava.endorsed.dirs=/opt/tomcat/endorsed -classpath /opt/tomcat/bin/bootstrap.jar -Dcatalina.base=/opt/tomcat -Dcatalina.home=/opt/tomcat -Djava.io.tmpdir=/opt/tomcat/temp org.apache.catalina.startup.Bootstrap start
[1004lucifer@1004lucifer ~]$
[1004lucifer@1004lucifer ~]$ ps -ef | grep java | grep Bootstrap | awk '{printf $2}'
19629[1004lucifer@1004lucifer ~]$
[1004lucifer@1004lucifer ~]$
2. dump.jsp 위치 추가설명 (중요)
dump.jsp 를 만들어서 문제가 되는 시점에 ThreadDump 를 생성하여 현재의 Thread 상태와 그 상태를 Dump파일로 남기려는게 목적이지만 현재 운영중인 WAS 에 해당 페이지를 넣게된다면 해당 WAS 가 문제가 되었을 경우 경우에 따라 dump.jsp 조차도 페이지가 실행이 되지 않을 수 있다.
처음에는 Context 를 하나 더 만들어 추가하면 되지 않을까 싶었는데 복수개의 Context 가 WAS의 Connection 개수를 같이 공유한다면 특정 Context 가 문제가 있을 때 dump.jsp 를 넣은 다른 Context 에 영향을 미치지 않을까 싶기도 하다.
(WAS에 대해서 잘 아는 분이 있다면 댓글로 설명을 달아줬으면 좋겠다.)
운좋게 현재 상황은 Tomcat Instance 가 하나 더 기동이 되어있어서 다른 Instance(프로세스) 의 Tomcat 에 dump.jsp 를 같이 올렸다.
만일 모니터링 하려는 Tomcat이 hang 발생을 하더라도 안심하고 사용할 수 있게 되었다.
댓글
댓글 쓰기