본문 바로가기

Developer/Knowledge Base

Quartz의 이중실행 증상

序.
로컬에서의 테스트에서는 극히 정상적으로 quartz스케쥴러가 작동하였고, 이를 테스트서버와 실서버로 옮겼을때는 동시에 두번씩 동작하는 문제가 발생했다.
세 서버의 Tomcat/Spring/Quartz의 버전은 완전히 동일...

1. 일단 큰 차이랄 수있는 OS/JVM의 차이를 의심하였다. 구글링을 해보니 비슷한 증상(윈도우 정상작동, 리눅스 스테이징으로 옮기니 발생)인 사람들을 여럿 발견... 희망이 보이기 시작....

벗뜨.. 이런 얘기한 사람들은 다 답을 얻지 못함...

2. 다음은 Spring 혹은 Quartz의 버그를 의심.
Spring + Quartz의 조합으로 비슷한 증상을 호소한 사람들이 미쿡(= 영어였다는 얘기다;;;)에는 꽤 많았다...
이는 web.xml에서 Spring의 ContextLoader가 중복호출되어 복수의 Quartz인스탄스가 생겨 발생하는 경우였다. 웹어플리케이션 상에서 둘 이상의 Listener or Servlet을 등록때 하나의 context정보 파일을 두 번 이상 부를 수 있는 경우가 있다는 것이다.

webapp의 lifecycle과 Spring의 Bean생성 과정을 자세하게 알지 못하면 쉽게 할 수 있는 실수였다... 드디어 찾았구나~ 하고 좋아했다...
 
근데...

이 경우라면 로컬에서는 이상없는 것이 설명되지 않았다...
로컬에서 이상이 없는 이유를 찾느라 한참을 소비해야했다...

결론적으로... 내 경우의 Spring설정은 완벽했다;;;

3. 처음부터 서버 설정을 크게 의심하지 않은 것은, 테스트 서버와 실서버가 모두 잘 돌고 있었기 때문이다. 또한 설정파일 역시 모두 공유해서 사용하고 있었기 때문에 시야를 벗어나는 좋은 이유가 되었다. 처음부터 톰캣 인스탄스가 2개인것은 의심해 보았다. 2개가 떠있어서 하나는 포트를 못열고 놀고만 있는... 그러나 스케줄러는 작동되고 있는... 하지만 이것에 해당하지는 않았다.

유일하게 공유되고 있지 않은 설정파일인 톰캣의 server.xml파일을 열어봤다...

....
<Host name="localhost" appBase="/tomcat/webapps"
    unpackWARs="true" autoDeploy="false"
    xmlValidation="false" xmlNamespaceAware="false">
    <Context path="" docBase="/tomcat/webapps/test" reloadable="false" />
</Host>
....

뭔가 감이 왔다;

그렇다, Host의 appBase는 여러 webapp들이 들어갈 母 디렉토리다. 즉, 이하의 디렉토리는 webapp의 자격조건을 가지고 있으면 자동으로 webapp로 구동되는 것이다.
Context는 이와는 별도로 webapp의 위치를 직접 지정가능하다.

결국 위의 설정은 두개의 'test' webapp를 구동하도록 만들었다. 스케쥴러도 두개가 뜬것이다.

Tomcat에서 동일한 Web Application Context 두 개가 뜬다한들 작동에는 별 문제가 없을 것이다. 둘의 ContextPath가 다르다면 말이다. 위의 경우 Host에서는 "test"라는 ContextPath로 webapp가 떴을 것이고, Context에서는 지정한대로 ""(=루트) ContextPath로 떴으니 아무런 문제가 없었던 건이다.

여튼 Tomcat설정시 Host의 appBase위에 Context로 설정할 app를 올리지 말아야한다. 이는 의도하지않은 작동을 야기한다. 본인은 다양한 이유로 appBase의 위치를 바꾸는 방법을 택했다.

結.
Quartz의 스케쥴러가 이중작동하는경우(굳이 이러한 경우가 아니더라도, 서버구동시 한번만 실행되도록 했는데 두번 실행되는 모든 경우).....

우선 Spring을 사용하는 경우는 Spring설정에 주의를 기울여라. Spring이 인스탄스의 라이프사이클을 제어하는 경우가 많아서, 개발자가 미처 인지하지 못하는 사이 인스탄스화가 일어날 수 있다. 구체적인 사례는 여기를 참조한다.

이게 아니라면 Container의 라이프사이클을 의심하라. 혹시 프로세스가 두개가 뜨지는 않았는지... 프로세스는 하나지만 복수의 Context를 구동하게 되지는 않았는지... 이 모든 것은 Container의 설정으로 해결해야 할 것이다. 1번의 경우(OS, JVM의심)도 결국 이러한 상황이었던 것으로 보인다.