-
Notifications
You must be signed in to change notification settings - Fork 136
Yobi 0.8.2에서 deprecated된 Thread.stop()을 쓰는 이유
Java에서 Thread.stop()은 안전하지 않기 때문에 deprecated된 메서드입니다. 그러나 그럼에도 불구하고 Yobi 0.8.2에서는 어쩔 수 없이 저 메서드를 사용하게 되었습니다. 그 이유는, Java 8에 탑재된 자바스크립트 엔진인 Nashorn을 강제로 중단시킬 방법이 그것 뿐이었기 때문입니다.
Yobi 0.8.0 이전에는 마크다운 렌더링을 자바스크립트로 작성된 마크다운 렌더러인 marked를 이용해 웹 브라우저 상에서 처리하고 있었습니다. 그런데 이렇게 하면 커밋이나 이슈 번호에 링크를 걸 때(#1234 이런 식으로) ) 실제로 해당 커밋이나 이슈가 있는 경우에만 링크가 만들어지도록 하기가 어렵습니다. 이에 우리는 마크다운 렌더링을 서버에서 처리하도록 변경하기로 결정하였습니다.
그런데 자바로 작성된 마크다운 렌더러 라이브러리 중 marked와 똑같은 렌더링 결과물을 만들어내는것은 아무리해도 찾을 수가 없었습니다. 이에 우리는 별 수 없이 그냥 자바가 내장하고 있는 자바스크립트 엔진인 nashorn으로 makred를 실행해서 마크다운 렌더링을 하도록 만들었습니다. 좀 느리긴 하겠지만 큰 문제야 있겠냐 하고 생각했습니다.
하지만 큰 문제가 있었습니다. Yobi 0.8.0을 릴리즈하고 한달정도 지나서, 특정 이슈를 보려고 할 때 너무나도 많은 시간이 걸리는 문제가 발견되었습니다. 수 초가 걸리는 것이 있는가 하면, 심한 경우 수 일이 지나도 끝나지 않는 경우까지 있었습니다.
문제를 분석한 결과, 우리는 문제의 원인이 특정 패턴의 마크다운 텍스트를 렌더링 할 때 서버에서 어마어마하게 많은 시간이 걸리는 것임을 발견했습니다. 우리는 이 문제를 해결하기 위해 마크다운 렌더링을 시작한 지 일정 이상의 시간이 지나면 강제로 마크다운 렌더링을 중단시키도록 결정하였습니다.
스레드를 중단시키는 안전한 방법은 Thread.interrupt()를 쓰는 것입니다. 하지만 그 방법은 의도대로 동작하지 않았습니다. 자바스크립트 엔진인 Nashorn이 interrupt에 대해 응답하지 않는 것이 원인인 것으로 보였습니다. 결국 마크다운 렌더링을 중단시킬 적절한 방법을 찾아내지 못해서 부적절한 방법임에도 불구하고 우리는 Thread.stop()을 사용하게 되었습니다. 그 코드는 다음과 같습니다.
// Try to render and wait at most 5 seconds.
final String[] rendered = new String[1];
@SuppressWarnings("deprecation")
Thread marked = new Thread() {
@Override
public void run() {
try {
rendered[0] = (String) ((Invocable) engine).invokeFunction(
"marked", source, options);
} catch (Exception e) {
play.Logger.error("[Markdown] Failed to render: " + source, e);
}
}
};
marked.start();
marked.join(5000);
if (rendered[0] == null) {
// This is the only way to stop the script engine. Thread.interrupt does not work.
marked.stop();
return "〈pre〉" + StringEscapeUtils.escapeHtml(source) + "〈/pre〉";
} else {
return rendered[0];
}
이상이 우리가 어쩔 수 없이 Thread.stop()을 사용하게 된 사연입니다. 혹시 더 괜찮은 해결책을 알고 계시거나 아이디어가 있으신 분은 한국 요비 사용자 그룹이나 Github의 Yobi 프로젝트로 의견 혹은 풀리퀘스트 보내주시면 정말 감사하겠습니다.