톰캣 6.0 클래스로더 구조

http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html

BootStrap CL: JVM 코어, 확장 라이브러리용 클래스 로더
System CL: $CATALINA_HOME/bin/bootstrap.jar와 CATALINA_HOME/bin/tomcat-juli.jar를 가지고 시스템 클래스 로더를 생성함. 일반적인 애플리케이션의 시스템 클래스 로더와는 동작 방식이 다름. 일반적인 애들은 시스템 클래스로더에서 CLASSPATH에 있는 것들을 로딩하지만, 톰캣의 스크립트(($CATALINA_HOME/bin/catalina.sh 또는 %CATALINA_HOME%\bin\catalina.bat)가 이를 무시해버림.
Common CL: $CATALINA_HOME/lib에 위치한 톰캣 내부 클래스와 웹 애플리케이션이 공통으로 참조할 라이브러리를 로딩한다. 
웹 애플리케이션 CL:  각각의 웹 애플리케이션당 해당 애플리케이션의 /WEB-INF/classes와 /WEB-INF/lib 디렉토리에 있는 클래스와 리소스를 로딩한다. 
SRV.9.7.2 Web Application Classloader
The classloader that a container uses to load a servlet in a WAR must allow the
developer to load any resources contained in library JARs within the WAR
following normal J2SE semantics using getResource. It must not allow theWAR to
override J2SE or Java servlet API classes. It is further recommended that the loader
not allow servlets in theWAR access to the web container’s implementation classes.
It is recommended also that the application class loader be implemented so that classes and resources packaged within the WAR are loaded in preference to classes and resources residing in container-wide library JARs.
위와 같은 서블릿 2.3 스펙에 따라 웹 애플리케이션 CL은 다음의 순서로 클래스를 로딩한다.
1. Bootstrap classes of your JVM
2. System class loader classes (described above)
3. /WEB-INF/classes of your web application
4. /WEB-INF/lib/*.jar of your web application
5. $CATALINA_HOME/lib
6. $CATALINA_HOME/lib/*.jar
1번은 웹 애플리케이션 CL의 최상위 CL인 BootStrap CL에 의해 로딩될 것이고, 거기서 못찾는다면
2번 System CL에 의해서 로딩 된다. 거기서도 못찾는거라면 일단 자바의 코어 라이브러리나 확장 라이브러리도 아니며 톰캣의 내부 클래스(톰캣 서버를 실행하는 클래스와 톰캣의 Commons logging API와 java.util.logging LogManager를 찾는게 아닐것이다. 그런 경우.
3번과 4번에서 웹 애플리케이션 CL 자신의 클래스패스에 있는 라이브러리를 찾아보고.. 거기에 없는 경우
5, 6번 Commons CL의 클래스패스에서 찾아본다.
클래스 로더의 동작 방식이 웹 애플리케이션이 아닌 경우가 웹 애플리케이션인 경우가 다르며, 이것 마저도 웹 서버 마다 다를 수 있으니 자신이 사용하는 컨테이너의 클래스로더 동작 방식을 알고 있어야겠다. 
흠.. 스프링의 TC 서버는 어떨지 궁금하다. 그것도 톰캣을 확장한 거니까 이것과 비슷하지 않을까. DM 서버는 왠지 완전히 다를 것 같다.

스프링소스 웨비나: 프로들의 아파치 톰캣 팁과 트릭

마크 토마스 랑 …
– 톰캣 커비터로 5년 넘게 일해왔다.
– 아파치 멤버
– 시니어 소프트웨어 엔지니어 이자 컨선턴트
– 스프링소스 Covalent 부서
– 성능, 트러블슈팅, 보안 전문가

톰캣 버전 얘기
– 6.0.x가 최신 버전
– 7.x가 슬슬 수면위로 들어나고 있다.(서블릿 3.0 스펙, ..)

주요 안건
– 대규모 개발에서 톰캣
– 톰캣 & JVM 업그레이드
– …

setenv.sh – 여기서 커스텀 옵션
– JAVA_HOME
– JAVA_OPTS
– CATALINA_OPTS
– …
– 이 파일은 톰캣이 기본으로 제공하는게 아니니깐 복사해서 가지고 다니면서 수정하라.
– 여기서 자바 버전 설정하면 톰캣 스트립트 수정하지 않아도 된다.
– 인스턴스 데이터를 분리해낼 수 있다.(별도의 톰캣 서버 인스턴스)
– 톰캣이나 JVM을 변경할 때 이 파일을 이용하면 편하다.
– RPM을 사용하면 롤백 과정이 복잡하다.

Production 세팅

server.xml
– <Server port=”8005″ shutdown=”SHUTDOWN” > : will allow unauthorized shut-down of Tomcat instance
– <Server port=”-1″ shutdown=”SHUTDOWN” > : 셧다운 포트 disable.
– 톰캣 멈추기: kill <pid>

애플리케이션 제거하기
– ROOT – 제공할 애플리케이션으로 대체하기
– 다음 폴더는 제거하는게 보안 최선책(security best practice)이다.
– /examples
– /docs
– /manager(원격 배포를 할 수 없게 된다.)
– /host-manager(원격 호스트 관리를 할 수 없게 된다.)

로깅
– conf/loggin.properties: 핸들러 등록
.handlers = \1catalina.org.apache.juli.FileHandler
– 파일에만 로깅한다.
– 개발할 때 가끔은 stdou/stderr가 더 쉬울때도 있다.

Rotating catalina.out(모르는 거)
– http://www.cronolog.org – 예제 도구
>> “$CATALINA_BASE” /logs/catalina.out 2>&1 &
–  하루 주기로 로테이트
2>&1 | /bin/cronlog/ …

접근 로깅(이것도 모르는거..)
– valve를 사용해서 접근 로깅을 할 수 있다.

글로벌 기본값
– ex) 커넥션 풀
– 모든 기본 값은 conf/context.xml 또는 conf/web.xml 에 설정할 수 있다.
– 기본값은 애프리케이션에서 설정한 값이 재정의할 수 있다.

Templating
– catalina.properties의 값을 server.xml에서 사용할 수 있다.

배포 방법
– server.xm에서 <Context> 엘리먼트 사용하기
– WAR 파일 자동 배포
– 디렉토리 자동 배포
– XML 파일 자동 배포
– 원격 배포
– Best Practice는 한 가지 선호하는 방법을 사용하는 것이다. 여러 방법을 혼용하면 얘상치 못한 상황을 보게 될지도 모른다.

Building Tomcat Native(이것도 모르는 거)
– OpenSSL 소스 distro 압축 풀기
– APR 소스 distro 압출 풀기
– APR Util 소스 disto 압축 풀기
– tomcat-nartive.tar.gz distro 압축 풀기
– setenv.sh (64비트로 컴파일 했으면 64 비트 JVM 사용하도록 설정해야 한다.)

커넥터 선택하기(아파치 커넥터 얘기하는건가?)
– BIO(Blocking IO)
– NIO(None-blocking IO)
– 왜 NIO 커넥터를 사용하려는가?
– 네이티브(APR) 커넥터는 솔라시스에서 안정적이지 않다.
– NIO는 순수 자바 솔루션이다.
– SSL로 NIO와 BIO를 교환하는 건 간단하다.

버추얼 호스팅은 무엇인가?
– 단일 톰캣 인스턴스에 여러 호스트 이름 주는거
– http://a.foo.com
– http://b.foo.com

호스트 정의
– server.xml에 호스트 설정
– 자신만의 appBase 필요함
– …

버추어 호스팅 컨텍스트 정의
– 표준 방법 적용 http://apache.org/tomcat-6.0-doc/config/context.html

버추어 호스팅 사용이 적당한 경우
– DNS 가 설정되어 있지 않을 때
– docBase == appBase
– ROOT.war 사용하지 않을 때
– …

문서화되어 있지 않은 옵션
– W3C Extended 로그 파일 포맷
– 캐싱 – private chaching을 권장한다.

스프링소스에서 아파치 톰캣 지원

질문 답변

Q: mod_jk vs mod_proxy_ajp ? Which is preferred for Apache2.2 to Tomcat6.0.x?
A: We recommend them in the following order
 mod_proxy_http, mod_jk, mod_proxy_ajp
 we prefer the HTTP protocol, if you want AJP, mod_jk is more stable than mod_proxy_ajp (which is the newest addition)

Q: mysql-connector: why not shared/lib?
A: since connection pool library doesn’t have access to shared/lib
 if you want mysql jars in the webapp, you can add a connection pool library in your webapp and configure it there. Tomcat has to follow the class loader rules

Q: Currently we see that with 3 apache servers having each 100 clients (prefork + mod_jk) each tomcat server (10) has also 100 threads. Which doesn’t look logical. Is there a mod_jk config option which could change this behaviour?
A: in httpd.conf –
 JkOptions +DisableReuse
 this will turn of AJP keep alive, and you no longer have to balance threads

Q: Is Log Valves a tomcat6 feature?
A: AccessLogValves have been around in Tomcat since Tomcat 5 (possible 4) Can be found under the Valves section of the Tomcat Configuration Reference

Q: does the connector preference also apply to AJP connections (using mod_jk)?
A: Yes, with the exception of SSL. No SSL support on AJP, but you can keep more connections than threads using APR/AJP, so you no longer have to carefully balance maxThreads(Tomcat) with MaxClients(httpd)

Q: Is there a way to solve the problem of “PermGen memory error” that usually arises after many reloads of the contexts?
A: PermGen error, are very hard to resolve, as they are very subtle bugs in the web applications
 If the bug can’t be fixed, increase -XX:MaxPermSize large enough to be able to reload applications during the day and then do a restart of tomcat at night

Q: If you are passing .jsp pages to Tomcat from Apache , do the Tomcat docBase and the Apache Document root need to be the same for the given Virtual Server ?
A: For security reasons, you don’t want httpd to have access to the Tomcat file system (webapps), as one can exploit your configuration to download your code

ps: 큰일이네;; PPT에 집중하니까 귀는 하나도 안 들리네;;

아파치 톰캣 연동

URL을 어떻게 사용할꺼냐에 따라 설정하는 방법이 다를 수 있는데, 저는 whiteship.me/wiki whiteship.me/jira 이런식으로 구성하기로 했습니다. wiki.whiteship.me와 jira.whiteship.me 이런식으로 하는 것 보다 URL이 조금 길다는 단점이 있지만, 도메인 네임서버 호스팅 설정을 많이 하지 않아도 되고,(도메인 서비스 업체에서 와일드카드를 제공하면 몰겠지만, 제가 사용하는 곳은 안 그런거 같더라구요.) 아파치에서 버츄얼 호스트 설정 역시 많이 하지 않아도 되기 때문에 이 방법을 선택했습니다.

1. 아파치/conf/workers.properties

work.list 에 워커 추가.

work.list = whiteship

워서 설정

worker.whiteship.type = ajp13
worker.whiteship.port = 연동할 톰캣 포트

2. 아파치/conf/extra/httpd-vhosts.conf

버츄얼 호스트 등록하기

<VirtualHost *:80>
    ServerAdmin 메일계정(ex. whiteship@whiteship.me)
    DocumentRoot /usr/local/apache2/htdocs
    ServerName 도메인주소(ex. www.whiteship.me)
    ErrorLog 로그파일 위치(ex. logs/www.whiteship.me-errorLog)
    CustomLog 로그파일 위치
                                          
    JkMount /* 워커이름(위에서 whiteship이라는 워커를 등록했으니. whiteship으로..) 
    JkMount / 워커이름
</VirtualHost>

3. 톰캣/conf/server.xml

맨 아래 쪽에 name=”localhost”로 설정되어 있는 부분을 2번에서 설정한 도메인 주소로 변경..

   <Host name=”도메인주소”  appBase=”webapps”                          
            unpackWARs=”true” autoDeploy=”true”                                   
            xmlValidation=”false” xmlNamespaceAware=”false”>   

끝… 나머지는 톰캣에서 새로운 App 설정할 때 마다 /wiki /jira 이런식으로만 주면, 버츄얼 호스팅 설정도 안건드려도 되고, 도메인 네임서버 호스팅 설정도 안 해도 됩니다. 적용되는데 시간도 좀 걸리고 번거로운대다가.. 네임서버 호스팅 갯수도 보통 4~5개 로 제한적이더군요.. *.whiteship.me를 쓸 수 있게 해주던지;; 왜 다섯개밖에 못 쓰남;;

@Configurable + 톰캣

테스트 코드는 다음과 같습니다.

public class MemberTestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        Member member = new Member();
        if(member.getMemberRepository() == null)
            System.out.println(“Opps Repository Null”);
        if(member.getMemberRepository().getSessionFactory() == null)
            System.out.println(“Opps SessionFactory Null”);
        System.out.println(“Good!!!”);
    }

}

간단하죠. 뷰에 디스패칭을 하지도 않았습니다. 그냥 콘솔에 Good!!만 출력하도록 했습니다. 그 이외의 경우(Null)에는 화면에 뭐가 Null인지 출력하도록 했죠. 그리고 이 녀석을 web.xml에 등록했습니다.

    <servlet>
        <servlet-name>memberTest</servlet-name>
        <servlet-class>web.MemberTestServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>memberTest</servlet-name>
        <url-pattern>/memberTest.do</url-pattern>
    </servlet-mapping>

그리고 브라우저에서 /memberTest.do 를 호출하고 콘솔 창을 봤습니다.

사용자 삽입 이미지
사용자 삽입 이미지
결론 : @Configurable은 웹 서버에서도 잘 동작 합니다.

Eclipse에서 톰캣 웹 모듈 설정하기

Server 뷰에 있는 서버 중에 하나 선택
사용자 삽입 이미지
Overview 탭이 기본으로 보이는데 여기서 Module을 선택합니다.
사용자 삽입 이미지
docBase에 뭔가 있다면 전부 선택해서 제거(remove)합니다. 그리고 오른쪽에 Add External web module을 선택합니다.
사용자 삽입 이미지
그리고 Document base에서 서버에 구동한 프로젝트의 웹 기본 폴더(보통은 web 또는 이클립스는 기본으로 WebContent) 를 선택합니다.
사용자 삽입 이미지
Auto reloading enabled는 선택해도 되고 안해도 되지만 하지 않겠습니다. 그다음 서버를 구동합니다.
사용자 삽입 이미지
웹 브라우저에서 localhost:8080 으로 접속하면 해당 웹 프로젝트의 index 페이지를 볼 수 있습니다.
만약에 콘솔에 다음과 같은 에러가 보인다면 Docbase가 잘못 설정된 것입니다.

심각: Error starting static Resources
java.lang.IllegalArgumentException: Document base
D:\eclipse\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\studywith
does not exist or is not a readable directory