제4장 웹 서버 연결과 클러스터링

내용 목차

4.1. 개요
4.2. 웹 서버 연결
4.2.1. 리스너
4.2.2. Worker Thread와 Worker Thread Pool
4.2.3. Thread Pool의 Active-Management와 상태 통보
4.2.4. 여러 개의 웹 컨테이너와 웹 서버 클러스터링
4.3. 리스너 설정
4.3.1. HTTP, TCP, HTTPS 리스너 설정
4.3.2. WebtoB 리스너 설정
4.3.3. AJP13 리스너 설정
4.3.4. Tmax 리스너 설정
4.3.5. 자동 Thread Pool 관리 설정(Thread 상태 통보)
4.4. 리스너 연동과 클러스터링을 위한 웹 서버 설정
4.4.1. Apache 웹 서버 설정과 클러스터링
4.4.2. IIS 웹 서버 설정과 클러스터링
4.4.3. SunOne(Iplanet) 웹 서버 설정과 클러스터링
4.4.4. WebtoB 웹 서버 설정과 클러스터링
4.5. TCP 리스너 사용
4.5.1. 맞춤 통신 프로토콜 정의
4.5.2. Dispatcher 설정 클래스 구현
4.5.3. TCP Handler 구현
4.5.4. 맞춤 프로토콜 코드를 위한 TCP 리스너 설정
4.5.5. TCP 클라이언트 구현
4.5.6. TCP 클라이언트 컴파일과 실행
4.6. 보안(SSL) 리스너 사용
4.6.1. 개요
4.6.2. 디렉터리 구조
4.6.3. 보안 리스너 설정
4.6.4. SSL Keystore 설정
4.6.5. SSL Truststore 설정
4.6.6. 보안 리스너 속성 설정
4.6.7. 보안 리스너 시작하기
4.6.8. 보안 리스너에 연결하기
4.7. 리스너 튜닝

본 장에서는 웹 컨테이너의 앞 단에서 사용할 수 있는 1개 이상의 웹 서버를 설정할 때 알아야 할 사항들과 자체적으로 가지고 있는 웹 서버를 최대한 이용하는 방법에 대하여 설명한다.

4.1. 개요

웹 컨테이너를 사용하기 위해서는 HTTP 클라이언트와 웹 컨테이너 사이에서 중간자 역할과 코디네이터 역할을 하는 1개 이상의 웹 서버를 설정해야 한다. 이러한 관점에서 웹 서버는 클라이언트와 웹 컨테이너 구조에서 중간 계층 역할을 수행한다.

웹 서버의 기능은 기본적으로 클라이언트의 HTTP 요청을 받고 분석하고, 뒷 단의 웹 컨테이너에 있는 Context(웹 애플리케이션)에 전달해야 할 요청이라고 판단되면 컨테이너로 요청을 전달하는 것이다. 컨테이너는 그 요청을 분석하고 수행하여 클라이언트에게 응답을 전달할 수 있는 웹 서버에게 돌려보낸다.

그러한 중간 계층으로서 웹 서버는 2가지 종류가 있다.

WebtoB 웹 서버와 Apache 웹 서버가 그것이다. 이 2가지의 웹 서버 외에 컨테이너 자체에는 개발과 테스트 용도로 사용할 수 있는 2개의 간단한 웹 서버와 “TCPListener”라는 특수 목적을 가진 웹 서버가 존재한다.

Apache, IIS, SunOne(Iplanet) 웹 서버와의 연결을 위한 AJP13, WebtoB, HTTP, HTTPS, TCP, TMAX 등의 리스너를 통하여 여러 종류의 웹 서버, 클라이언트 연결이 존재한다. 앞의 2개의 리스너는 앞 단의 웹 서버를 통하여 클라이언트의 요청을 컨테이너에게 중재시켜주는 역할을 한다. Tmax 리스너는 Tmax와의 연동을 위한 특별한 리스너이고, 나머지는 웹 컨테이너에 통합된 형태의 클라이언트 리스너들이다.

각기 다른 연결 방법에 따라 어떻게 설정하는지와 웹 컨테이너와 연결하기 위해 웹 서버에 어떤 것들을 설정해야 하는지도 설명한다. 그리고 간단히 웹 서버 클러스터를 구성하는 방법도 설명한다.

4.2. 웹 서버 연결

본 절에서는 웹 컨테이너, 웹 서버, 클라이언트 간의 기본적인 구조를 설명하고 웹 컨테이너와 연결할 수 있는 여러 종류의 웹 서버 리스너들에 대하여 설명한다.

다음은 웹 서버 연결에 관련된 웹 컨테이너의 주요 부분을 보여주고 있다.

[그림 4.1] JEUS 웹 컨테이너 중 웹 서버 연결 부분 컴포넌트

JEUS 웹 컨테이너 중 웹 서버 연결 부분 컴포넌트


4.2.1. 리스너

우선 “리스너”가 어떤 의미를 가지고 있는지에 대하여 분명히 이해해야 한다.

리스너는 일반적으로 웹 서버나 HTTP 클라이언트가 직접 접근 할 수 있는 웹 컨테이너 측의 소켓이라고 생각할 수 있다. 이 리스너(소켓)는 웹 서버(또는 HTTP 클라이언트)로부터 요청을 받고 웹 컨테이너에서 처리한 static 또는 dynamic content를 반환한다. dynamic content는 JSP 나 Servlet과 같은 Java 기반의 콘텐츠를 의미하고 static content는 HTML 페이지나 이미지 파일과 같은 이미 생성된 데이터를 의미한다.

리스너에는 2가지 종류가 존재한다.

[그림 4.2] 웹 컨테이너의 웹 서버와 클라이언트 리스너

웹 컨테이너의 웹 서버와 클라이언트 리스너

  • 웹 서버 리스너

    외부의 웹 서버들과 연결되고 이들과 통신을 위해서 맞춤 프로토콜을 사용한다. 클라이언트는 이 웹 서버를 통하여 웹 컨테이너와 통신한다.

  • 클라이언트 리스너

    주로 클라이언트와 직접 연결되고 HTTP 프로토콜을 사용한다.

이 리스너들은 종류에 상관없이 컨테이너 레벨에서 설정되지 않고, Context Group 레벨에서 설정된다.

주목할 점은 Context Group 리스너의 또 다른 사항은 이론적으로는 다수의 리스너가 각 Context Group에 설정될 수 있다는 것이다. 단, 한 가지의 제약 조건은 각 리스너들이 각기 다른 리스닝 포트를 지정 받아 설정되어야 한다는 것이다.

HTTP/HTTPS 리스너

HTTP/HTTPS 리스너는 static content와 JSP/Servlet, SOAP over HTTP 웹 서비스에 대한 HTTP 요청을 웹 컨테이너가 직접 받을 때 사용한다. HTTP 리스너는 SSL을 지원하며 “4.3. 리스너 설정”“4.6. 보안(SSL) 리스너 사용”에 상세한 설명이 있다.

WebtoB 또는 Apache/IIS/SUNOne 웹 서버 등의 웹 서버가 웹 컨테이너 앞 단에 위치하는 경우에는 WebtoB 리스너 혹은 AJP13 리스너를 사용한다.

TCP 리스너

TCP 리스너는 일반적인 리스너에 맞춤 프로토콜(HTTP를 사용하지 않음)을 사용하는 리스너이다.

“4.3. 리스너 설정”“4.5. TCP 리스너 사용”에서 자세히 설명한다.

WebtoB 리스너

WebtoB는 JEUS 웹 애플리케이션 서버의 기본 웹 서버다. WebtoB는 static 페이지 전송, CGI, SSI, PHP 등 기본적인 웹 서버 기능들을 모두 지원한다. JEUS 웹 컨테이너와 인터페이스할 때에는 Servlet/JSP 서비스도 제공한다.

WebtoB 리스너는 위에서 언급한 리스너와 조금 다른 종류의 리스너라고 할 수 있다. WebtoB 리스너는 다른 리스너와 달리 리스너가 WebtoB 서버의 위치를 찾아서, 접속하고자 하는 특징을 가진다. 그러므로, WebtoB 리스너를 사용할 때에는 WebtoB 서버가 리스닝 모드로 대기를 하고, WebtoB 리스너(즉, 웹 컨테이너)가 연결을 시도한다. 이러한 연결 방식을 Reverse Connection Pooling이라 한다.

참고

위 문장은 WebtoB 서버가 웹 컨테이너보다 먼저 구동 중에 있어야 한다는 것을 의미한다.

이런 특징의 결과로 방화벽 밖에 WebtoB 서버를 위치하고 안쪽에서 리스너를 이용하여 연결을 맺을 수 있다. 이것은 방화벽이 주로 외부로부터의 연결 시도를 억제하고 내부로부터의 연결은 가능하게 하여 방화벽의 장점을 그대로 살릴 수 있는 특징을 부여한다. 둘째, WebtoB와 웹 컨테이너가 같은 머신 내에 존재하면 둘 간의 통신은 Pipe 통신을 사용한다. 일반적인 소켓 방식을 사용하는 것보다 Pipe 통신을 함으로써 월등한 성능 향상을 기대할 수 있다. Pipe 통신은 UNIX(LINUX) 계열의 장비에만 사용이 가능하다(Windows 계열의 장비에는 일반 소켓만 사용 가능하다).

“4.3. 리스너 설정”“4.4. 리스너 연동과 클러스터링을 위한 웹 서버 설정”에서 자세히 설명한다.

참고

JEUS 내부적으로 제공하는 WS 엔진은 노드당 하나만을 사용할 수 있다.

Ajp13 리스너

위에서 언급한 WebtoB 이외의 다른 웹 서버, 예를 들면 Apache, IIS, SunOne(Iplanet) 등을 사용할 경우에도 JEUS 웹 애플리케이션과의 상호 연동이 가능하도록 해주는 프로토콜이다. mod_jk module을 통해 지원을 하며 AJP1.3 프로토콜을 사용한다. “4.3. 리스너 설정”“4.4. 리스너 연동과 클러스터링을 위한 웹 서버 설정”에서 자세히 설명한다.

Tmax 리스너

Tmax는 분산 환경에서 이질적인 자원을 통합해 주는 시스템 소프트웨어이다.

Tmax 리스너는 Tmax와 연동하기 위한 특수한 리스너로 WebtoB 리스너와 마찬가지로 활동적인 리스너이기 때문에 웹 컨테이너를 구동시키기 전에 Tmax가 먼저 구동되어 있어야 한다. Tmax 리스너는 JEUS와 Tmax 간의 정보를 주고받거나, http 요청을 Tmax의 게이트웨이를 통해 받음으로써 통신 채널을 일원화 하는 등의 용도로 사용될 수 있다.

4.2.2. Worker Thread와 Worker Thread Pool

웹 서버 리스너와 연관된 중요한 개념 중 하나가 “Worker Thread”에 관한 것이다.

각 리스너에는 Pool(Worker Thread Pool)이라는 것이 포함되어 있다. 이것은 Worker Thread들을 관리한다. 리스너의 포트로 요청이 도착했을 때 1개의 Worker Thread가 이 Pool에서 꺼내지고, 요청을 처리하기 위해 지정받은 후 응답을 만들어 낸다. 여기에서의 “처리”라는 개념은 static content를 가져오는 것에부터 JSP나 Servlet을 실행하는 것까지 모두를 포함한다.

“Worker Thread”라는 개념은 이 문서의 많은 부분에서 거론된다. 예를 들어 Context Group의 Active-Management 설정은 직접적으로 이 Worker Thread Pool에 연관되어 있다. 그러므로, Thread Pool 포트 또는 Thread Pool 주소라는 개념이 사용될 때에는 Worker Thread를 주관하는 리스너의 포트 번호와 IP 주소를 의미한다(그리고 간접적으로 Worker Thread도 의미한다). “Worker Thread Pool” 대신에 “Thread Pool”을 사용하기도 한다.

웹 서버 리스너를 설정할 때에는 Thread Pool의 일정한 사양도 같이 설정해야 한다.

4.2.3. Thread Pool의 Active-Management와 상태 통보

각 리스너에 설정되는 Thread Pool은 Active-Management에 관한 설정도 포함되어 있다.

Active-Management는 관리자가 설정된 상태가 되면 웹 컨테이너가 경고성 e-mail 통지하거나 웹 컨테이너가 자동으로 재시작하도록 설정할 수 있다.

설정되어야 할 조건은 Servlet Worker Thread가 Block되었다고 판단하는 기준 시간에 대한 설정이며, 설정한 시간에 따라가 Servlet Worker Thread가 Block되었다고 판단을 한다. 경고 e-mail 통보 또는 컨테이너 재시작같은 특정 작업이 시작되기 전에 몇 개까지의 Block된 Thread들이 존재할 수 있는지를 설정한다.

주의

재시작 옵션은 임시적인 해결책이고, 재시작이 발생할 경우에는 관리자가 원인을 찾아 반드시 문제를 해결해야 한다. 그리고 Active-Management 기능을 설정할 때 너무 자주 발생하지 않도록 유의해야 한다.

4.2.4. 여러 개의 웹 컨테이너와 웹 서버 클러스터링

여러 개의 웹 서버와 리스너들을 연결시키는 것을 클러스터링이라 하고 이 과정에서 생성된 조직체를 클러스터라고 한다. 거대한 사이트에서는 많은 양의 클라이언트 요청을 처리하기 위하여 “부하 분산”이라는 기술과 함께 반드시 사용되어야 하는 기술이다.

부하 분산은 기본적으로 클러스터 내의 어떤 서버에게 요청을 처리하도록 지정할 것인지를 정의하여 클러스터 내의 서버들에게 골고루 요청이 전달되어 처리되도록 한다. 부하 분산기는 모든 클라이언트 요청을 받아 그 순간 가장 여유로운 웹 서버에게 지정해 주는 소프트웨어이다.

다음은 2개의 웹 서버(WebtoB 또는 Apache)가 각각 2개의 웹 컨테이너에 연결되어 있는 작은 클러스터 구조를 보여주고 있다.

[그림 4.3] 2개의 웹 서버가 2개의 웹 컨테이너에 각각 연결되어 있는 작은 클러스터

2개의 웹 서버가 2개의 웹 컨테이너에 각각 연결되어 있는 작은 클러스터


참고

위 그림과 같은 설정은 각 웹 컨테이너에 동일한 Context Group 과 Context를 가지고 있어야 한다(즉, Context A, B, C, D는 모두 같은 것이어야 한다).

또한, 웹 서버와 리스너의 연동과 클러스터링에서 중요한 것은 클라이언트 Session을 어떻게 Tracking하는지에 대한 것이다. 클라이언트 Session 데이터가 여러 개의 웹 컨테이너(Context)에 정확하게 분배되는지에 대한 사항은 클러스터 규모가 커질수록 더 중요해 진다. 이 중요한 사항에 대해서는 “제5장 Session Tracking”에서 설명한다. 본 장에서는 Session 데이터의 존재에 대하여 무시하고 진행한다.

이러한 웹 서버 클러스터를 실제로 어떻게 설정하는지에 대하여 본 장의 “4.4. 리스너 연동과 클러스터링을 위한 웹 서버 설정”에서 자세히 설명한다.

4.3. 리스너 설정

리스너를 설정하는 작업은 매우 간단하다. 그러나 웹 서버 리스너를 실제 웹 서버(WebtoB, Apache, IIS, SunOne, etc...)와 연동시킬 경우에는 웹 서버 측에서도 설정이 필요하지만, 클라이언트 타입 리스너는 그 자체가 완전한 웹 서버 역할을 하기 때문에 별도의 설정이 필요하지 않다.

본 절에서는 웹 컨테이너 측의 Context Group 리스너의 설정에 대해서만 설명한다. WebtoB 리스너, AJP13 리스너, Tmax 리스너가 다른 종류의 리스너 설정과는 차이점이 있기 때문에 WebtoB 리스너, AJP13 리스너, Tmax 리스너에 관련된 사항들을 제외한 다른 나머지 리스너에 관련된 사항들만을 설명한다.

WebtoB와 다른 웹 서버의 해당 설정은 “4.4. 리스너 연동과 클러스터링을 위한 웹 서버 설정”을 참고한다.

참고

Context Group의 웹 리스너 설정은 WebAdmin에서도 가능하다.

모든 리스너는 WEBMain.xml의 <context-group><webserver-connection> 태그 하위에 설정된다. 각 Context Group에는 단 하나의 <webserver-connection> 태그가 존재할 수 있지만 그 하위에는 여러 개의 리스너 설정이 존재할 수 있다는 의미이다. 각 Context Group은 단 한 개의 <webserver-connection> 설정을 가져야 한다.

[예 4.1] 리스너 설정 : <<WEBMain.xml>>

<?xml version="1.0"?>
<web-container xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    . . .
    <context-group>
        . . .
        <webserver-connection>
            <webtob-listener>
                . . .
            </webtob-listener>
            <http-listener>
                . . .
            </http-listener>
            <http-listener>
                 . . . 
                <scheme>https</scheme>
                <ssl-config>
                    <enable-secure>true</enable-secure>
                 . . .
                </ssl-config>
            </http-listener>
            <tcp-listener>
                . . .
            </tcp-listener>
            <ajp13-listener>
                . . .
            </ajp13-listener>
            . . .
        </webserver-connection>
        . . .
    </context-group>
    . . .
</web-container>


4.3.1. HTTP, TCP, HTTPS 리스너 설정

HTTP, TCP, HTTPS 리스너의 설정은 단 한 가지의 예외를 제외하고 모두 동일하므로 모두 같은 방법으로 설명한다.

다음의 XML 태그들은 HTTP, TCP, HTTPS 리스너를 설정할 때 사용되는 것들이다.

  • HTTP 리스너: <http-listener>

  • TCP 리스너: <tcp-listener>

  • 보안/SSL 리스너: <http-listener>,<ssl-config>

다음의 XML 예제는 HTTP 리스너의 설정이며, 그 밖의 다른 리스너에 대해서는 순차적으로 설명한다.

[예 4.2] HTTP, TCP, HTTPS 리스너 설정 : <<WEBMain.xml>>

<?xml version="1.0"?>
<web-container xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    . . .
    <context-group>
        . . .
        <webserver-connection>
            <http-listener>
                <listener-id>WebListener1</listener-id>
                <ip>190.0.1.2</ip>
                <port>8007</port>
                <output-buffer-size>16384</output-buffer-size>
                <thread-pool>
                    <min>10</min>
                    <max>20</max>
                    <step>2</step>
                    <max-idle-time>60000</max-idle-time>
                    <max-wait-queue>10</max-wait-queue>
                    <max-queue>50</max-queue>
                    <thread-state-notify>
                        . . .    <!-- see next sub-section -->
                    </thread-state-notify>
                </thread-pool>
                <postdata-read-timeout>
                    40000
                </postdata-read-timeout>
                <max-post-size>1024</max-post-size>
                <max-parameter-count>1024</max-parameter-count>
                <max-header-count>100</max-header-count>
                <max-header-size>1024/max-header-size>
                <max-querystring-size>1024/max-querystring-size>
                <scheme>http</scheme>
                <back-log>100</back-log>
                <server-access-control>
                    true
                </server-access-control>
                <allowed-server>127.0.0.1</allowed-server>
            </http-listener>
        </webserver-connection>
        . . .
    </context-group>
    . . .
</web-container>


위 태그들의 하위 태그(설정)들은 다음과 같다.

  • <listener-id>

    • 리스너의 유일한 식별자이다. 이름은 WEBMain.xml 파일 내부에서 유일해야 한다.

  • <ip>

    • 클라이언트가 연결되어 있는 IP 주소이다. 보통의 경우에는 이 설정을 하지 않아도 머신의 IP 주소가 사용되므로 설정할 필요가 없고, IP 주소가 2개 이상이어서 그 중 하나를 지정해야 하는 등의 특별한 경우에만 설정한다.

    • HTTP 리스너에서 설정 가능한 태그이다.

  • <port>

    • 클라이언트가 연결되어 있는 포트 번호이다.

    • HTTP를 위해서는 기본적으로 80번을 사용하고 SSL을 위해서는 443을 사용한다.

      (IANA, http://www.iana.org/에서 정의)

    • JEUS 설정에서는 어떤 포트도 기본으로 설정되어 있지 않으므로 이 태그는 모든 리스너에서 반드시 지정해야 한다.

  • <connection-type>

    • 각 리스너 또는 커넥터를 통해 나가는 응답의 "Connection" 헤더를 강제로 설정한다.

    • 다음 중에 하나를 설정한다.

      구분설명
      keep-alive응답을 보낸 후에도 연결을 계속 유지하고자 할 경우 설정한다.
      close응답을 보낸 후 연결을 끊고자 할 경우 설정한다.
      none요청 헤더에 정의된 속성에 따라 응답의 "Connection" 헤더를 따를 경우 설정한다. 웹 엔진은 아무 설정도 하지않을 경우 "none"과 같이 동작한다.

  • <output-buffer-size>

    • Servlet 결과가 임시적으로 저장되는 내부 Cache 버퍼의 크기를 결정한다. 버퍼가 가득 찼을 때에는 한 번에 클라이언트에게 모두 보내진다.

    • 이 옵션은 성능 향상을 위해 사용되지만 일반적으로 사용되지 않는다.

  • <thread-pool>

    • 각 리스너의 Worker Thread Pool의 크기와 행동 방식을 결정짓는다.

      Worker Thread가 클라이언트 요청을 처리하기 위해서 사용되며 Pool 크기가 크면 클수록 더 많은 요청이 동시에 처리될 수 있다(시스템 리소스를 많이 사용한다).

    • 다음의 하위 태그를 이용해서 Thread Pool 정보를 설정한다.

      태그설명
      <min>, <max>Pool에서 관리할 최소 및 최대 Worker Thread의 개수이다.
      <step>Pool의 크기가 증가할 때 Thread가 몇 개씩 증가할 것인지를 지정한다.
      <maximum-idle-time>

      Pool 내에 존재하던 Thread가 제거되기 전까지의 사용되고 있지 않은 시간을 지정한다(결과로 시스템 자원은 늘어난다).

      각 Worker Thread Pool은 request wait queue를 가지고 있다. 이 Queue는 실제 가용한 Worker Thread보다 많은 요청이 들어 올 때 사용된다. 이 Queue는 소켓 리스너에 의해 유지되는 낮은 레벨의 Backlog Queue보다 상위 레벨의 Queue이다.

      다음의 두 태그는 이 Queue와 관련된 것들이다.

      <max-wait-queue>

      더 많은 Worker Thread가 Pool에 생성되기 전에 얼마나 많은 요청들이 Request Wait Queue에 존재할 수 있는지에 대해 결정한다(몇 개의 Thread가 증가될지는 step 설정에서 정의된다).

      단, NIO(Non-blocking I/O) 방식의 리스너에서는 이 설정값은 무시된다.

      <max-queue>

      설정은 Queue에 대기할 수 있는 최대 요청 값을 설정한다.

      이 Queue가 가득 찬 후에 더 많은 요청이 도착하면 busy 페이지가 클라이언트에게 반환된다. 각 Thread Pool은 장애가 발생하였을 때 취하는 액션을 정의하는 <thread-state-notify> 태그를 가지고 있다. 이것에 대해서는 다음 하위 절들을 참조한다.

      값이 -1일 경우 Queue 크기에 제한을 두지 않는 것을 의미한다(Blocking 방식의 리스너의 경우). 만약 Listener가 NIO(Non-blocking I/O)방식을 사용한다면 엔진 내부적으로 Bounded Queue를 사용하므로 항상 0보다 커야 한다.

      NIO(Non-blocking I/O)방식에서 0보다 같거나 작은 값을 설정한 경우는 디폴트 값인 4096을 사용한다.

    참고

    NIO(Non-blocking I/O)방식에서는 Thread의 <max>, <min>, <max-queue>, <max-idle-time>에 따른 동작 방식이 Blocking방식과 약간 다르다. NIO(Non-blocking I/O)방식의 경우 최초 <min> 값 만큼 Worker Thread를 생성하고 Queue가 full 상태일 때 Worker Thread를 <max>까지 늘려준다. 또한 <min>보다 늘어난 Worker Thread에 대해서는 <max-idle-time>의 설정 시간동안 Thread가 idle 상태가 될 때 <min>까지 Idle Thread를 종료시킨다.

    Blocking방식에서는 Queue가 <max-wait-queue>만큼 되면 Worker Thread를 <max>까지 늘린다.

  • <postdata-read-timeout>

    • 웹 서버나 Web Client에서 post-data를 읽어 들일 때 기다릴 수 있는 최대시간 값이다.

    • 읽기는 request.getInputStream().read() 메소드로 한다. (기본값: 30000, 단위: ms)

  • <max-post-size>

    • POST 요청의 데이터로 너무 큰 크기가 들어와 이 데이터를 읽고 분석하고 처리하는데 많은 부하가 걸려 다른 요청들의 처리에 장애가 생길 경우 이 설정을 하여 특정 크기이상의 요청에 대한 처리 부담을 줄일 수 있다.

    • 이 설정은 POST 요청의 경우 요청에 따라오는 데이터의 최대 크기를 바이트 단위로 제한한다.

      POST 요청의 Content-Type이 x-www-form-urlencoded의 경우, 요청에 따라오는 데이터의 바이트 크기가 설정된 값을 넘을 경우, chunked일 경우, 들어온 데이터의 크기의 합이 설정된 값보다 클 경우, JEUS는 해당 요청은 처리하지 않고 413 Request entity too large 응답을 보내고 해당 요청 처리를 끊낸다.

      POST 요청이 multipart/form-data인 경우 파일을 업로드할 경우에는 이 설정이 적용되지 않는다. 다만 데이터가 이름/값의 쌍으로 이루어진 파라미터의 경우에만 이 설정이 적용된다.

    • 수로 설정했을 경우는 -1과 같이 동작하며 데이터 크기의 제한이 없다는 것을 의미한다.

      (기본값: -1)

    • 이 설정은 TCP 리스너와 Tmax 커넥터의 경우 의미가 없다. HTTP 요청을 받는 리스너/커넥터의 경우만 의미가 있다.

  • <max-parameter-count>

    • 한 요청에 너무 많은 이름/값 쌍(파라미터)이 포함되어, 이 파라미터들의 분석/관리의 부담이 증가하는 것을 방지할 경우 이 설정을 사용한다.

    • 이 설정은 GET과 POST 요청에 포함된 이름/값 쌍, 즉 파라미터의 총 갯수를 설정값으로 제한한다.

    • GET 요청의 query string, POST 요청의 데이터나 multipart/form에 이름/값 쌍으로 들어온 모든 파라미터의 갯수가 설정 값 이상이 넘을 경우 413 requesy entity too large 응답을 보내고, 해당 요청의 처리를 중단한다.

    • 음수로 설정했을 경우는 기본값과 같이 동작하며, 파라미터 갯수의 제한이 없음을 의미한다.

      (기본값: -1)

    • 이 설정은 TCP 리스너와 Tmax 커넥터의 경우 의미가 없다. HTTP 요청을 받는 리스너/커넥터의 경우만 의미가 있다.

  • <max-header-count>

    • 한 요청에 포함된 헤더가 무수히 많을 경우 이 요청을 읽는 부하가 많이 걸린다. 따라서 이럴 경우 해당 요청을 거부하여 서버의 부하를 줄일 수 있다.

    • 이 설정은 설정된 값 이상의 헤더 갯수가 포함된 요청은 처리하지 않고 400 Bad request 응답을 보낸 후 해당 연결을 끊는다. 즉, 헤더 이후의 데이터는 읽지 않고 버림으로써 서버의 부담을 줄이게 된다.

    • 음수로 설정했을 경우는 -1과 같이 동작하며, 헤더 갯수의 제한이 없음을 의미한다.

      (기본값: -1)

    • 이 설정은 TCP 리스너와 Tmax 커넥터의 경우 의미가 없다. HTTP 요청을 받는 리스너/커넥터의 경우만 의미가 있다.

  • <max-header-size>

    • 한 요청에 포함된 헤더가 제한없이 클 경우 이 헤더를 읽고 처리하는 부하가 많이 걸린다. 따라서 이럴 경우 해당 요청을 거부하여 서버의 부하를 줄일 수 있다.

    • 이 설정은 설정된 값 보다 큰 바이트 크기(이 때 헤더의 바이트는 요청의 헤더하나를 타나내는 헤더이름, 구분자, 헤더 값을 모두 포함한 크기)의 헤더가 포함된 요청은 처리하지 않고 400 Bad request 응답을 보낸 후 해당 연결을 끊는다. 즉, 헤더 이후의 데이터는 읽지 않고 버림으로써 서버의 부담을 줄이게 된다.

    • 음수로 설정했을 경우는 -1과 같이 동작하며, 헤더 바이트의 제한이 없음을 의미한다. (기본값: -1)

    • 이 설정은 TCP 리스너와 Tmax 커넥터의 경우 의미가 없다. HTTP 요청을 받는 리스너/커넥터의 경우만 의미가 있다.

  • <max-querystring-size>

    • GET 요청의 query string이 길 경우 이를 분석/관리하는 부하가 생길 수 있다. 이런 경우 query string의 크기를 제한하여 이 부담을 줄일 수 있다.

    • 설정된 바이트 크기이상의 query string을 포함한 요청이 들어올 경우 해당 요청에 대하여 400 Bad Rqeust 응답을 보낸 후 해당 요청의 나머지 데이터들은 읽지 않고 버림으로써 서버의 부담을 줄인다.

    • 음수로 설정했을 경우는 qurey string의 크기에 제한을 두지 않는다는 의미이나 JEUS는 내부적으로 request line (요청의 첫 줄)의 크기를 64킬로 바이트 제한을 함으로 이 이상은 의미가 없게된다.

      (기본값: 8192Byte)

    • 이 설정은 TCP 리스너와 Tmax 커넥터의 경우 의미가 없다. HTTP 요청을 받는 리스너/커넥터의 경우만 의미가 있다.

  • <scheme>

    • javax.servlet.http.HttpServletRequest.getScheme() 메소드에 의해 반환되는 프로토콜의 값을 정의한다.

    • WebtoB나 Apache에서 SSL 기능을 사용할 경우에는 “https” 값이 지정되어야 한다. 보안 리스너의 경우에는 특별히 설정하지 않아도 "https"로 동작한다.

  • <back-log>

    • 요청이 매우 빈번하고 리스너가 더 이상 지탱하지 못할 경우에 Queuing될 수 있는 최대 클라이언트 요청 값을 지정한다.

    • 리스너 Server 소켓이 인스턴스화되었을 때 java.net.ServerSocket(int port, int backlog)에 전달된다. Queuing된 클라이언트 연결 요청 값이 backlog 크기를 초과할 경우에는 클라이언트는 “connection refused” 메시지를 받는다.

  • <server-access-control>

    • Boolean 스위치는 클라이언트 또는 웹 서버 접근 제한을 켜고 끈다.

  • <allowed-server>

    • <server-access-control>이 켜져 있을 때만 사용할 수 있다.

    • 목록은 어떤 클라이언트 또는 서버들이 이 리스너에 접근할 수 있는지 지정한다. 목록의 클라이언트들은 그들의 IP 주소를 이용하여 식별한다.

  • <use-nio>

    • HTTP, TCP 리스너에서만 설정 가능하며 이것을 true로 할 경우 NIO(Non-blocking I/O) 리스너로 동작하게 된다.

    • NIO 리스너를 사용하면 커넥션을 담당하는 Thread와 Worker Thread가 분리되어 있어서 기존 방식에서 동시에 처리할 수 있는 커넥션의 개수가 Worker Thread 수보다 작을 수 밖에 없는 문제점을 해결할 수 있다.

  • <selector-count>

    • NIO HTTP, TCP 리스너를 사용하는 경우(use-nio=true) Selector의 개수를 지정하는 설정이다.

      기본으로 한 개로 설정되며 커넥션의 개수가 많아 성능이 저하될 경우 Selector 개수를 적절히 증가시켜 주면 성능이 향상될 수 있다.

  • <dispatcher-config-class>

    • TCP 리스너에서는 정식 명칭의 “dispatcher config class” 클래스 이름을 지정하는 태그이다. TCP 리스너는 HTTP 프로토콜을 사용하지 않는다. 대신, TCP 리스너와 그 클라이언트 사이에 한정된 맞춤 프로토콜을 제공한다. 이 맞춤 프로토콜은 <dispatcher-config-class>를 이용하여 정의된다.

    • <dispatcher-config-class> 태그는 jeus.servlet.tcp.TCPDispatcherConfig로 구현한 정식 클래스명을 정의한다. 구현된 클래스는 반드시 JEUS_HOME\lib\application 아래에 위치해야 한다.

    • TCP 클라이언트를 서비스하기 위해서는 jeus.servlet.tcp.TCPServlet을 구현한 클래스를 생성해야 하며 이를 web.xml에 매핑해야 한다. 자세한 내용은 “4.5. TCP 리스너 사용”을 참고한다.

4.3.2. WebtoB 리스너 설정

WebtoB 리스너는 다른 종류의 리스너와는 다르다. 일반적인 개념과는 달리 JEUS가 클라이언트가 되어서 WebtoB로 커넥트하는 구조다. 단, 커넥트 방향만 그럴 뿐 HTTP 요청은 WebtoB로부터 JEUS로 전달된다.

다음은 WebtoB 리스너 설정의 예로 WEBMain.xml 파일 내 <webserver-connection><webtob-listener> 태그에서 설정한다.

[예 4.3] WebtoB 리스너 설정 : <<WEBMain.xml>>

<?xml version="1.0"?>
<web-container xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    . . .
    <context-group>
        . . .
        <webserver-connection>
            <webtob-listener>
                <listener-id>WebListener2</listener-id>
                <webtob-address>
                    111.111.111.111
                </webtob-address>
                <registration-id>regid1</registration-id>
                <webtob-home>/webtob</webtob-home>
                <port>9900</port>
                <output-buffer-size>16384</output-buffer-size>
                <thread-pool>
                    <min>10</min>
                    <max>10</max>
                </thread-pool>
                <postdata-read-timeout>
                    40000
                </postdata-read-timeout>
                <max-post-size>1024</max-post-size>
                <max-parameter-count>1024</max-parameter-count>
                <max-header-count>100</max-header-count>
                <max-header-size>1024/max-header-size>
                <max-querystring-size>1024/max-querystring-size>
                <scheme>http</scheme>
                <hth-count>2</hth-count>
                <request-prefetch>true</request- prefetch>
                <disable-pipe>true</disable-pipe>
                <read-timeout>120000</read-timeout>
                <reconnect-timeout>60000</reconnect-timeout>
                <webtob-backup>
                    <port>9901</port>
                    <output-buffer-size>
                        16384
                    </output-buffer-size>
                    <thread-pool>
                        . . .
                    </thread-pool>
                    . . .
                    <webtob-address>
                        111.111.111.112
                    </webtob-address>
                    . . .
                </webtob-backup>
                <secure>
                    <trust-store-file-path>
                        server_certstore.jks
                    </trust-store-file-path>
                    <trust-store-file-password>
                        password
                    </trust-store-file-password>
                    <key-store-file-path>
                        webtob_client.p12
                    </key-store-file-path>
                    <key-store-file-password>
                        password_ks
                    </key-store-file-password>
                    <private-key-password>
                        password_pkey
                    </private-key-password>
                    <key-store-file-type>
                        PKCS12
                    </key-store-file-type>
                </secure>
            </webtob-listener>
        </webserver-connection>
        . . .
    </context-group>
    . . .
</web-container>


다음은 설정 태그에 대한 설명이다.

태그설명
<listener-id>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”에서 설명한 유일한 이름이다.
<port>WebtoB 서버와 연결할 포트로, 포트 값은 WebtoB 설정 파일 내의 JSVPORT 설정 값과 일치해야 한다.
<connection-type>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”에서 설명한 것과 같다.
<output-buffer-size>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”에서 설명한 것과 같다.
<thread-pool>

“4.3.1. HTTP, TCP, HTTPS 리스너 설정”에서 설명한 것과 같다.

WebtoB에서 다른 점은 <min>, <max> 값이 WebtoB 설정 파일의 *SERVER 절에 지정된 MinProc, MaxProc 값과 일치해야 한다.

<min>과 <max> 값을 설정하기 전에 “4.4. 리스너 연동과 클러스터링을 위한 웹 서버 설정”을 먼저 읽어 보길 바란다. 또한, <max-queue> 크기 설정은 사용하지 않지만 WebtoB 설정 파일에서 MaxQCount 값으로 설정될 수 있다.

<postdata-read-timeout>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”을 참고한다.
<max-post-size>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”을 참고한다.
<max-parameter-count>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”을 참고한다.
<max-header-count>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”을 참고한다.
<max-header-size>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”을 참고한다.
<max-querystring-size>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”을 참고한다.
<scheme>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”을 참고한다.
<hth-count>

WebtoB 설정 파일의 *NODE 절에 지정된 HTH 프로세스 개수와 일치해야 한다. “4.4. 리스너 연동과 클러스터링을 위한 웹 서버 설정”을 참조한다.

<request-prefetch>

웹 컨테이너 측에서 요청을 처리하는 동안 다음 요청을 WebtoB로부터 미리 받아올 것인지 정한다(Boolean값).

기능이 활성화되면, 웹 컨테이너는 각 WebtoB Worker Thread 마다 하나의 Queue를 할당한다.

Queue는 Worker Thread가 현재 요청을 처리하는 동안 WebtoB로 부터오는 요청을 버퍼링한다. 따라서 웹 컨테이너는 WebtoB로부터 다음 요청을 받는 시간을 단축할 수 있다. 이 기능의 단점은 요청을 처리하는 도중 웹 컨테이너에 심각한 문제가 발생하면, Queue에 축적된 요청들을 잃어버릴 수 있다는 점이다.

<disable-pipe>설정이 true로 설정되어 있으면 WebtoB와 웹 컨테이너의 효율적인 Pipe 통신을 불가능하게 한다. WebtoB 서버와 웹 컨테이너가 다른 머신에서 수행되고 있으면 필요한 항목이다.
<webtob-address>웹 컨테이너와 연결을 맺을 WebtoB의 IP 주소 항목이다.
<registration-id>WebtoB 설정 파일의 *SERVER 값과 일치해야 한다. 설정하지 않은 경우 기본값으로 이 값은 Context Group의 이름과 같은 값으로 설정된다. 설정값은 15자 이내로 설정해야 한다.
<webtob-home>

JEUS_WSDIR이라는 환경변수로 정의된 WebtoB의 기본 WebtoB Home과 다를 경우에 설정한다(또는 그 환경변수가 설정되어 있지 않을 때).

2개 이상의 WebtoB 인스턴스가 로컬 머신에서 운영되고 있고 웹 컨테이너가 이 2개 이상의 WebtoB 인스턴스에 연결될 필요가 있을 때 유용하다.

<read-timeout>

WebtoB 웹 서버는 지속적으로 웹 컨테이너에게 WebtoB의 설정 파일에 정의된 “svrchktime” 변수 값의 간격으로 “ping”메시지를 보낸다.

웹 컨테이너는 여기에서 정의한 시간 간격으로 WebtoB의 ping을 체크한다. WebtoB의 ping이 여기에서 설정된 시간 간격 내에서 발견되지 않으면 통신 연결은 끊어진 것으로 간주되어 다시 설정된다.

그러므로, 여기의 값은 “svrchktime”보다 커야 한다.

<reconnect-timeout>

WebtoB 서버와 WebtoB 리스너 사이의 연결들이 운영도중 끊어지는 경우가 발생할 수 있다. reconnect-timeout은 이러한 경우에 재연결 시도를 위한 제한시간이다.

제한된 시간이 지나면 현재의 모든 WebtoB 연결은 끊어지고, 만약 WebtoB 백업이 지정되어 있으면 웹 컨테이너는 다음 WebtoB 서버에 Fail-over를 시도한다. 다음 WebtoB 백업마저 장애가 발생한다면 그 다음의 것을 시도한다. 최후의 WebtoB 마저 장애가 발생하면 주 WebtoB 리스너에 시도하게 된다.

  • -1: 무한대 반복 시도

  • 0: 재연결 시도를 하지 않음

<secure>

WebtoB와 연결할 때 TLS 또는 SSL 프로토콜을 사용할지 결정하는 옵션이다. WebtoB가 방화벽 외부에 존재할 경우 JEUS와의 통신 내용이 스니핑되는 것을 막기 위한 방편이다. 이 기능을 사용할 경우 TLS에 사용하는 알고리즘에 따라 성능 저하가 발생한다. TLS에 사용하는 알고리즘은 사용자가 결정하는 것이다. 성능과 안정성을 적절히 고려해서 결정하기 바란다.

<webtob-backup>

WebtoB 리스너의 설정과 비슷하다.

하지만 WebtoB backup 태그는 Automatic Connection Recovery기능을 수행하기 위해서 주 서버가 심각한 장애 상태가 되었을 때, 두 번째 WebtoB 서버에 연결하기 위한 연결 정보를 정의한다. 따라서 Backup WebtoB의 설정은 Main WebtoB 장애가 발생하면 Backup WebtoB로 연결하기 때문에 무장애 시스템 구현을 가능하게 한다.

여기서 주의할 것은 백업 서버에 Thread Pool에 대한 설정이 없다면 주 WebtoB 리스너의 설정에서 상속 받는다는 것이다.

참고

“4.4.4. WebtoB 웹 서버 설정과 클러스터링”에서는 어떻게 실제의 WebtoB 서버가 설정되어 이 리스너에게 연결되는지 설명한다. 또한 리스너 설정과 WebtoB의 설정 파일 간의 관계도 설명한다.

<secure> 설정

WebtoB가 방화벽 외부에 존재할 경우 JEUS와의 통신 내용이 스니핑되는 것을 막기 위한 방편이다. 이 기능을 사용할 경우 TLS에 사용하는 알고리즘에 따라 요청 처리 성능이 저하되므로 사용자가 이점을 고려해서 해당 알고리즘을 결정해야 한다. 단, TLS에 사용하는 알고리즘은 인증서 및 비밀키 생성시 결정하는 것이므로 JEUS와 연관된 사항은 없다.

일반적인 개념과 달리 JEUS가 WebtoB로 연결하는 클라이언트 입장이다. TLS 핸드쉐이크 시에 WebtoB가 보내는 서버 인증서를 확인하기 위해서 <trust-store-file-path>와 <trust-store-file-password>를 반드시 설정해야 한다. 기본적으로 JKS 파일 형식이지만 PKCS12 형식일 경우 <trust-store-file-type>에 PKCS12라고 지정해야 한다.

만약 Client Authentication을 사용하고 싶은 경우 <key-store-file-path>, <key-store-file-password>, <private-key-password>를 지정한다. 주의할 점은 암호문을 똑같게 지정할 수는 있겠지만 비밀키를 보관한 파일의 암호와 비밀키의 암호는 서로 다르다는 것이다. 암호문이 똑같을 경우에는 <key-store-file-password>만 지정해도 된다. 이 파일 역시 JKS 파일 형식이 기본이지만 <key-store-file-type>에 PKCS12로 지정 가능하다.

참고

암호문의 경우 {AES}와 같이 암호화된 값으로 적을 수 있다. 이는 encryption 툴을 이용해서 생성 가능하다. 자세한 사항은 JEUS Reference Book”의 “4.6. encryption”을 참고한다.

4.3.3. AJP13 리스너 설정

AJP13 리스너는 WEBMain.xml 파일 내 <webserver-connection> 아래의 <ajp13-listener> 태그에서 설정한다.

다음은 AJP13 리스너의 XML 설정 예이다.

[예 4.4] AJP13 리스너 설정 : <<WEBMain.xml>>

<?xml version="1.0"?>
<web-container xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    . . .
    <context-group>
        . . .
        <webserver-connection>
           <ajp13-listener>
               <listener-id>WebListener3</listener-id>
               <port>9901</port>
               <output-buffer-size>8192</output-buffer-size>
               <thread-pool>
                   <min>10</min>
                   <max>10</max>
               </thread-pool>
               <max-post-size>1024</max-post-size>
               <max-parameter-count>1024</max-parameter-count>
               <max-header-count>100</max-header-count>
               <max-header-size>1024/max-header-size>
               <max-querystring-size>1024/max-querystring-size>
           </ajp13-listener>
        </webserver-connection>
        . . .
    </context-group>
    . . .
</web-container>


다음은 설정 태그에 대한 설명이다.

태그설명
<listener-id>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”에서 설명한 유일한 이름이다.
<ip>

클라이언트가 연결되어 있는 IP 주소이다. 보통의 경우에는 이 설정을 하지 않아도 머신의 IP 주소가 사용되므로 설정할 필요가 없고, IP 주소가 2개 이상이어서 그 중 하나를 지정해야 하는 등의 특별한 경우에만 설정한다. AJP13 리스너에서 설정 가능한 태그이다.

<port>

특정 웹 서버와 연결할 포트를 설정한다. 이 값은 특정 웹 서버 설정 파일내의 JEUS에 해당하는 서버에 대한 설정 값과 일치해야 한다. 특정 웹 서버 설정과 관련된 사항은 “4.4. 리스너 연동과 클러스터링을 위한 웹 서버 설정”을 참고한다.

<output-buffer-size>

“4.3.1. HTTP, TCP, HTTPS 리스너 설정”에서 설명한 것과 같다.

다만 AJP13 리스너의 경우 이 설정 값을 mod_jk module의 workers.properties파일의 max_packet_size와 일치시킨다. mod_jk module의 설정에 대해서는 “4.4. 리스너 연동과 클러스터링을 위한 웹 서버 설정”을 참고한다.

<thread-pool>

“4.3.1. HTTP, TCP, HTTPS 리스너 설정”에서 설명한 것과 같다. 다만 AJP13 리스너의 경우 min, max 값을 서로 다르게 줄 특별한 이유가 없다.

특별한 상황이 아닌이상 min, max를 일치시킨 후 이 값을 mod_jk module의 workers.properties 파일의 connection_pool_size, connection_pool_size_min_size와 일치시킨다.

mod_jk module의 설정에 대해서는 “4.4. 리스너 연동과 클러스터링을 위한 웹 서버 설정”을 참고한다.

<postdata-read-timeout>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”에서 설명한 것과 같다.
<max-post-size>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”을 참고한다.
<max-parameter-count>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”을 참고한다.
<max-header-count>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”을 참고한다.
<max-header-size>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”을 참고한다.
<max-querystring-size>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”을 참고한다.

4.3.4. Tmax 리스너 설정

각 Tmax 리스너는 WEBMain.xml 파일 내 <webserver-connection> 아래의 <tmax-listener> 태그에서 설정한다.

다음은 Tmax 리스너의 XML 설정예이다.

[예 4.5] Tmax 리스너 설정 : <<WEBMain.xml>>

<?xml version="1.0"?>
<web-container xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    . . .
    <context-group>
        . . .
        <webserver-connection>
            <tmax-listener>
                <listener-id>WebListener4</listener-id>
                <port>9902</port>
                <output-buffer-size>16384</output-buffer-size>
                <thread-pool>
                    <min>10</min>
                    <max>10</max>
                    <step>4</step>
                    <max-idle-time>60000</max-idle-time>
                    <max-wait-queue>2</max-wait-queue>
                    <thread-state-notify>
                        <max-thread-active-time>
                            150000
                        </max-thread-active-time>
                        <notify-threshold>100</notify-threshold>
                        <restart-threshold>
                            18
                        </restart-threshold>
                        <notify-subject>
                            JEUS WEB CONTAINER THREAD STATE WARNING
                        </notify-subject>
                        <restart-subject>
                            JEUS WEB CONTAINER RESTART WARNING
                        </restart-subject>
                    </thread-state-notify>
                </thread-pool>
                <postdata-read-timeout>
                    40000
                </postdata-read-timeout>
                <tmax-address>111.111.111.111</tmax-address>
                <reconnect-timeout>60000</reconnect-time>
                <server-group-name>TmaxGroup</server-group-name>
                <server-name>SVR1</server-name>
                <tmax-backup-address>
                    111.111.111.112
                </tmax-backup-address>
                <tmax-backup-port>9902</tmax-backup-port>
            </tmax-listener>
        </webserver-connection>
        . . .
    </context-group>
    . . .
</web-container>


다음은 설정 태그에 대한 설명이다.

태그설명
<listener-id>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”에서 설명한 유일한 이름이다.
<port>Tmax 서버와 연결할 포트로, 이 포트 값은 Tmax 설정 파일내의 JEUS에 해당하는 서버에 대한 설정 값과 일치해야 한다.
<connection-type>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”에서 설명한 것과 같다.
<output-buffer-size>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”에서 설명한 것과 같다.
<thread-pool>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”에서 설명한 것과 같다. Tmax 리스너의 경우도 WebtoB 리스너와 동일하게 동작한다.
<postdata-read-timeout>“4.3.1. HTTP, TCP, HTTPS 리스너 설정”에서 설명한 것과 같다.
<tmax-address>웹 컨테이너와 연결을 맺을 Tmax 서버의 IP 주소 항목이다.
<reconnect-timeout>Tmax 서버와 Tmax 리스너 사이의 연결 중 일부가 운영도중 끊어질 경우가 생길 수 있다. <reconnect-timeout>은 이러한 경우에 재연결 시도를 위한 제한 시간이다. 역시 WebtoB와 동일하나 Tmax 리스너의 경우는 Backup을 하나만 설정할 수 있다는 것이 차이점이다.
<server-group-name>Tmax 리스너를 설정하여 Tmax와 연결할 때에 Tmax에 어떤 서버와 연결할 것인지를 설정해야 한다. <server-group-name>은 연결할 서버가 속해 있는 그룹을 설정한다. 이 설정은 반드시 해야 하는 설정이다.
<server-name>실제로 Tmax 리스너가 연결할 Tmax의 서버 이름을 설정한다. 이 설정은 반드시 해야 하는 설정이다.
<tmax-backup-address>Tmax 서버에 장애가 발생했을 때 backup으로 동작하는 서버의 주소이다. WebtoB Backup Server 설정과 동일하나 Tmax 리스너의 경우는 주소와 포트만 설정하고 나머지는 primary server의 설정을 그대로 가져간다.
<tmax-backup-port>Tmax 서버에 장애가 발생했을 때 backup으로 동작하는 서버에 연결할 포트 번호를 설정한다. tmax-backup-address가 설정되어 있다면 tmax-backup -port도 함께 설정되어야 한다.
<use-nio>

true로 할 경우 NIO(Non-blocking I/O) Tmax 리스너로 동작하게 된다.

NIO 방식을 사용하면 커넥션을 담당하는 Thread와 Worker Thread가 분리되어 있어서 기존 방식에서 동시에 처리할 수 있는 커넥션 개수가 Worker Thread 수보다 작을 수 밖에 없는 문제점을 해결할 수 있다.

설정하지 않았을 경우 기본값을 false이다.

<selector-count>

NIO를 사용할 때(use-nio=true) Selector의 개수를 지정하는 설정이다.

기본으로 1개로 설정이 되며 커넥션의 개수가 많아 성능이 저하될 경우 Selector 개수를 적절히 증가시켜 주면 성능이 향상될 수 있다.

4.3.5. 자동 Thread Pool 관리 설정(Thread 상태 통보)

<thread-state-notify> 태그는 e-mail 통지나 자동 재시작의 실행을 위해 필요한 최소의 Worker Thread 수와 관련된 “오류” 조건을 정의한다.

다음은 e-mail 알림자를 포함하고 있는 설정 예이다. <thread-state-notify> 태그는 웹 서버 리스너의 <thread-pool> 태그 아래에 설정된다.

[예 4.6] 자동 Thread Pool 관리 설정 : <<WEBMain.xml>>

<?xml version="1.0"?>
<web-container xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    . . .
    <context-group>
        . . .
        <webserver-connection>
            <http-listener>
                <listener-id>WebListener1</listener-id>
                <port>8007</port>
                <output-buffer-size>16384</output-buffer-size>
                <thread-pool>
                    <min>10</min>
                    <max>20</max>
                    <step>2</step>
                    <max-idle-time>60000</max-idle-time>
                    <max-wait-queue>10</max-wait-queue>
                    <max-queue>50</max-queue>
                    <thread-state-notify>
                        <max-thread-active-time>
                            150000
                        </max-thread-active-time>
                        <notify-threshold>100</notify-threshold>
                        <restart-threshold>
                            18
                        </restart-threshold>
                        <notify-subject>
                            JEUS WEB CONTAINER THREAD STATE WARNING 
                        </notify-subject>
                        <restart-subject>
                            JEUS WEB CONTAINER RESTART WARNING
                        </restart-subject>
                        <thread-interrupt-execution>
                            true
                        </thread-interrupt-execution>
                        <restart-engine-execution>
                            true
                        </restart-engine-execution>
                        <active-timeout-notificatio>
                            true
                        </active-timeout-notificatio>                        
                    </thread-state-notify>
               </thread-pool>
                <postdata-read-timeout>
                    40000
                </postdata-read-timeout>
                <scheme>http</scheme>
                <back-log>100</back-log>
                <server-access-control>
                    true
                </server-access-control>
                <allowed-server>127.0.0.1</allowed-server>
            </http-listener>
        </webserver-connection>
        . . .
    </context-group>
    . . .
</web-container>


다음과 같은 항목들이 반드시 설정되어야 한다.

태그설명
<max thread active time>

Block되기 전까지 Worker Thread가 사용될 수 있는 최대시간 값을 말한다.

이 시간은 Worker Thread가 클라이언트 요청을 서비스할 때부터 측정된다(Servlet이 수행될 때). 여기서 정한 시간이 초과되었거나 또는 Thread가 자유롭게 되어 Worker Pool로 되돌아 갈 때 만료된다(Thread가 Block되지 않는 상황). 설정 값이 0이거나 음수이면 무시된다.

<notify-threshold-ratio>

존재할 수 있는 Block된 Thread의 최대 비율을 설정한다. 전체 Thread 개수 대비 Block된 Thread의 비율이 초과되면, 오류 조건으로 결정되어 e-mail 알림자를 통하여 e-mail 통보가 전송된다.

1보다 작은 소숫점으로 설정하며 0이거나 음수이면 무시된다.

<restart-threshold-ratio>

존재할 수 있는 Block된 Thread의 최대 비율을 설정한다. 전체 Thread 개수 대비 Block된 Thread의 비율이 초과되면, 오류 조건으로 결정되어 e-mail 알림자를 통하여 e-mail 통보가 전송된다. 그 후에 restart-engine-execution에 의해 웹 컨테이너가 재시작될 수 있다.

1보다 작은 소숫점으로 설정하며 0이거나 음수이면 이 설정이 무시된다.

<notify-subject>경고 e-mail을 전송할 때 사용된다.
<restart-subject>컨테이너 로그 및 통보할 때 사용하는 e-mail에 사용된다.
<thread-interrupt-execution>active-timeout 발생할 때 해당 Thread의 interrupt 발생 유무를 결정한다. true인 경우 interrupt를 발생시키며, 기본값은 false이다.
<restart-engine-execution>

Thread 비율이 <restart-threshod-ratio>를 넘어선 경우 해당 엔진의 재시작 여부를 결정한다.

true인 경우 엔진이 재시작되며, 기본값은 false이다.

<active-timeout-notification>

active-timeout 발생이 email notificatin 유무를 결정한다.

true인 경우 e-mail 전송이 이루어지며, 기본값은 false이다.

4.4. 리스너 연동과 클러스터링을 위한 웹 서버 설정

본 절에서는 어떻게 여러 웹 서버를 설정하여 해당 리스너와 연동할 수 있는지에 대해 설명한다.

여러 개의 웹 서버와 리스너들이 서로 연결되어 클러스터를 형성하는 상황 및 설정하는 방법에 대해 살펴보도록 하겠다. 클러스터링에 대한 자세한 내용은 “4.2.4. 여러 개의 웹 컨테이너와 웹 서버 클러스터링”, Session이 어떻게 관리되는지에 대한 설명은 “제5장 Session Tracking”을 참고한다.

4.4.1. Apache 웹 서버 설정과 클러스터링

Apache 웹 서버를 웹 컨테이너 앞단에서 사용하기 전에 다음과 같은 과정이 선행되어야 한다.

  1. 컨테이너 리스너와 Apache Server 간의 통신을 위하여 mod_jk 라이브러리를 설치한다.

  2. WEBMain.xml에서 AJP13 리스너를 설정한다. (“4.3.3. AJP13 리스너 설정” 참조)

  3. workers.properties 파일을 생성하고 편집한다.

  4. Apache 웹 서버의 httpd.conf 파일에 연동 구절을 삽입한다.

  5. Apache 웹 서버를 재시작한다.

참고

Apache의 경우 Apache HTTP Server 2.2.4, mod_jk의 경우 1.2.20을 기준으로 설명한다.

mod_jk 라이브러리 설치하기

Apache 웹 서버를 웹 컨테이너의 앞 단에서 사용하기 위해서는 'mod_jk'라는 모듈을 Apache 설치 모듈에 추가해야 한다. 'mod_jk' 모듈은 서버와 컨테이너 간의 통신 프로토콜을 구현한 것이다. 이 프로토콜은 Apache JServ Protocol 1.3(AJP 1.3)이다.

다음의 URL을 통해 해당 binary를 down받을 수 있으며 적절한 위치에 복사한다.

http://www.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/

down받은 library파일을 일반적으로 'mod_jk.so' 이름을 변경한다.

참고

만약 위의 URL과 JEUS에서도 특정 OS의 적절한 바이너리를 구하지 못할 경우는 source code를 다운받아 새로 컴파일해야 한다.

WEBMain.xml 설정

<ajp13-listener> 태그를 원하는 Context Group에 추가한다. WEBMain.xml에서 포트 번호를 주의해야 하며 이 포트 번호는 다음에 설명할 workers.properties 파일의 포트 정보와 일치되어야 한다.

다음은 Apache Listener의 설정 예이다.

[예 4.7] Apache 웹 서버 설정 : <<WEBMain.xml>>

<?xml version="1.0"?>
<web-container xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    . . .
    <context-group>
        . . .
        <webserver-connection>
           <ajp13-listener>
               <listener-id>WebListener3</listener-id>
               <port>9901</port>
               <output-buffer-size>8192</output-buffer-size>
               <thread-pool>
                   <min>10</min>
                   <max>10</max>
               </thread-pool>
           </ajp13-listener>
        </webserver-connection>
        . . .
    </context-group>
    . . .
</web-container>


위의 예에서는 9901번 포트가 설정되어 있고 output-buffer-size는 16384Bytes, Thread Pool은 10개로 설정되어 있다. 이 값들은 다음의 workers.properties 설정과 같아야 한다.

workers.properties 설정

workers.properties 파일은 mod_jk module을 위한 설정 파일이며, AJP13 프로토콜을 사용할 때 가장 중요한 설정이다.

mod_jk module에는 여러 버전이 존재하나 현 시점에서 가장 최신 버젼인 1.2.20을 기준으로 설명한다. 먼저 JEUS의 노드는 johan, johan1의 2개의 노드가 있으며, 서로 다른 호스트의 각각 <host_name> johan, johan1으로 존재한다고 가정한다.

각 노드에는 서블릿 엔진이 engine1, engine2로 각각 2개씩 있다고 가정한다. 즉, host johan에는 johan이라는 노드에 johan_servlet_engine1, johan_servlet_engine2의 엔진이 존재하고 host johan1에는 johan1이라는 노드에 johan1_servlet_engine1, johan1_servlet_engine2가 존재한다. 총 2개의 노드에 4개의 서블릿 엔진이 존재하며 각 엔진당 AJP13 리스너들이 서로 다른 포트 번호로 등록되어 있다. 편의상 순서대로 9901, 9902, 9903, 9904번을 리스너 포트로 설정했다고 가정하자.

다음은 위의 JEUS 환경 구성에서의 AJP13 프로토콜을 사용하기 위한 예제이다.

[예 4.8] AJP13 프로토콜을 사용 : <<workers.properties>>

worker.list=jeus_load_balancer_workers
worker.jeus_load_balancer_workers.type=lb
worker.jeus_load_balancer_workers.balance_workers=johan_servlet_engine1,johan_s
ervlet_engine2,johan1_servlet_engine1,johan1_servlet_engine2
worker.jeus_load_balancer_workers.sticky_session=true

###########################################
# listener specific configuration
###########################################
worker.johan_servlet_engine1.host=johan
worker.johan_servlet_engine1.port=9901
###########################################
# common configuration
###########################################
worker.johan_servlet_engine1.type=ajp13
worker.johan_servlet_engine1.lbfactor=1
worker.johan_servlet_engine1.socket_timeout=30
worker.johan_servlet_engine1.connection_pool_timeout=30
worker.johan_servlet_engine1.connection_pool_size=10
worker.johan_servlet_engine1.connection_pool_minsize=10
worker.johan_servlet_engine1.max_packet_size=8192
worker.johan_servlet_engine1.retries=1

worker.johan_servlet_engine2.host=johan
worker.johan_servlet_engine2.port=9902
worker.johan_servlet_engine2.reference=johan_servlet_engine1

worker.johan1_servlet_engine1.host=johan1
worker.johan1_servlet_engine1.port=9903
worker.johan1_servlet_engine1.reference=johan_servlet_engine1

worker.johan1_servlet_engine2.host=johan1
worker.johan1_servlet_engine2.port=9904
worker.johan1_servlet_engine2.reference=johan_servlet_engine1


workers.properties 파일은 몇몇 정의를 제외하고 일반적으로 "worker.<worker_name>.<directive>=<value>"의 형식으로 설정을 정의한다. 다음은 중요한 몇몇 설정에 대한 설명이다.

  • worker.list

    • 해당 worker를 정의하는 항목이다. worker의 이름들은 콤마(',')로 구분되며 여러 worker들을 나열할 수 있다.

    • 다음은 위 예제에 대한 설명이다.

      worker.list=jeus_load_balancer_workers

      이 예제에서는 'jeus_load_balancer_workers'라는 이름의 worker를 하나 정의하였다.

  • worker.<worker_name>.type

    • worker의 타입을 정의한다.

    • 다음 중에 하나의 값을 설정한다.

      • ajp13

      • ajp14

      • jni

      • lb

      • status

      AJP13을 지원하기 위해 jeus에서는 "ajp13", "lb"만의 설정으로도 충분하다.

    • 다음은 위 예제에 대한 설명이다.

      worker.jeus_load_balancer_workers.type=lb

      'jeus_load_balancer_workers'는 load balancer type으로 정의한다.

  • worker.<worker_name>.balance_workers

    • 콤마(',')로 구분하여 load balance를 원하는 worker들을 나열 할 수 있다. 위의 woker.list에 적은 worker들이 나타나서는 안 된다.

    • JEUS의 AJP13 리스너와 연동하는 경우 올바른 load balancer 및 sticky session 역할을 하기 위해서는 worker의 이름을 JEUS의 서블릿 엔진 이름으로 해야 한다. JEUS의 경우 Session ID의 라우팅 정보로 서블릿 엔진 이름을 이용하며 mod_jk에서는 worker의 이름을 이용하기 때문이다.

    • 다음은 위 예제에 대한 설명이다.

      worker.jeus_load_balancer_workers.balance_workers=johan_servlet_engine1,...

      'jeus_load_balancer_workers'는 johan_servlet_engine1, johan_servlet_engine2, johan1_servlet_engine1, johan1_servlet_engine2 총 4개의 worker를 load balance한다.

  • worker.<worker_name>.sticky_session

    • Session ID를 기반으로 라우팅을 지원할지에 대한 설정이다.

    • "true"(or 1) 또는 "false"(or 0)로 설정을 할 수 있다. 기본값은 true이며, JEUS에서는 sticky session을 기본으로 지원하므로 항상 "true"로 설정한다.

    • 다음은 위 예제에 대한 설명이다.

      worker.jeus_load_balancer_workers.sticky_session=true

      'jeus_load_balancer_workers'는 sticky session을 지원한다.

  • worker.<worker_name>.host

    • JEUS의 AJP13 리스너가 존재하는 호스트 이름 혹은 IP 주소를 입력한다.

    • 다음은 위 예제에 대한 설명이다.

      worker.johan_servlet_engine1.host=johan

      'johan_servlet_engine1' worker는 <host_name>이 johan이다.

  • worker.<worker_name>.port

    • JEUS의 AJP13 리스너의 포트 번호를 설정한다.

    • 다음은 위 예제에 대한 설명이다.

      worker.johan_servlet_engine1.port=9901

      'johan_servlet_engine1' worker는 포트를 9901을 사용한다. 이는 johan_servlet_engine1에 해당하는 WEBMain.xml의 AJP13 리스너의 포트와 동일한 값을 입력한 것이다.

  • worker.<worker_name>.lbfactor

    • load balancer worker에 해당하는 worker에만 의미가 있다. 해당 worker가 얼마나 많은 일을 할지를 정의하는 것으로 쉽게 worker의 priority를 지정한다고 생각해도 된다.

      만약 5로 설정을 하고 다른 worker가 1로 설정되어 있다면 해당 worker는 5배의 많은 요청을 받게 될 것이다. 기본적으로 모두 1로 설정을 하면 큰 무리가 없다.

  • worker.<worker_name>.socket_timeout

    • mod_jk와 remote host 간의 통신에 있어서 초 단위의 socket timeout을 설정한다.

      쉽게 설명하자면 JEUS의 Servlet Container가 지정한 시간내에 응답을 보내오지 않으면 mod_jk에서 에러를 발생시키는 시간 제한을 의미한다(waiting timeout의 의미).

    • 0(default)일 경우 timeout이 없는 것을 의미한다. 넉넉한 값을 주도록 한다(JEUS와 연동하는 경우 0이외의 다른 값 으로 설정할 것을 추천한다).

    • 다음은 위 예제에 대한 설명이다.

      worker.johan_servlet_engine1.socket_timeout=30 

      'johan_servlet_engine1' worker는 30초의 socket timeout을 사용한다.

  • worker.<worker_name>.connection_pool_timeout

    • 초 단위의 timeout을 설정한다.

    • connection pool에서 소켓을 close하기까지 open된 소켓을 유지하는 시간을 의미한다. 쉽게 설명하자면 JEUS의 서블릿 컨테이너와 맺은 커넥션이 설정한 시간동안 inactive 상태이면 소켓을 종료하고 pool의 Thread를 줄인다(inactive timeout의 의미).

    • 0(default)일 경우 Timeout이 없는 것을 의미한다. 넉넉한 값을 설정하도록 한다(JEUS와 연동하는 경우 0이외의 다른 값으로 설정할 것을 권장한다).

    • 다음은 위 예제에 대한 설명이다.

      worker.johan_servlet_engine1.connection_pool_timeout=30

      'johan_servlet_engine1' worker는 30초의 socket timeout을 사용한다. 즉, connection pool에서 소켓을 close하기까지 30초 동안 커넥션을 유지한다.

  • worker.<worker_name>.connection_pool_size

    • JEUS의 리스너와 mod_jk 간의 커넥션을 cache하는 크기(size)를 의미한다.

    • JEUS AJP13 리스너의 min, max 값과 일치시켜 설정한다.

      (JEUS AJP13에서는 min, max를 동일한 값으로 설정할 것을 권장한다)

    • 다음은 위 예제에 대한 설명이다.

      worker.johan_servlet_engine1.connection_pool_size=10

      'johan_servlet_engine1' worker는 Thread Pool size가 10이다. 이는 johan_servlet_engine1에 해당하는 WEBMain.xml의 AJP13 리스너의 Thread Pool의 max와 동일한 값을 넣어준 것이다.

      (min, max는 동일하게 맞추어 주는것을 권장한다)

  • worker.<worker_name>.connection_pool_minsize

    • JEUS의 리스너와 mod_jk간의 커넥션을 cache하는 최소의 크기(size)를 의미한다.

    • JEUS AJP13 리스너의 min, max 값과 일치시켜 설정한다.

      (JEUS AJP13에서는 min, max를 동일한 값으로 설정할 것을 권장한다)

  • worker.<worker_name>.max_packet_size

    • 최대 크기의 AJP packet size를 byte 단위로 설정한다. 최대 65536Bytes까지 가능하다.

    • JEUS AJP13 리스너의 output-buffer-size와 일치하여 설정하면 무리가 없다. (기본값: 8192)

    • 다음은 위 예제에 대한 설명이다.

      worker.johan_servlet_engine1.max_packet_size=8192 

      'johan_servlet_engine1' worker는 최대 8192의 max ajp13 packet을 이용한다. 이는 johan_servlet_engine1에 해당하는 WEBMain.xml의 AJP13 리스너의 output-buffer-size와 동일한 값을 넣어준 것이다(JEUS 및 mod_jk에서는 8192Bytes의 default를 이용한다).

  • worker.<worker_name>.retries

    • JEUS의 서블릿 컨테이너로부터 error를 받았을 경우 재시도할 횟수를 지정한다. 기본으로 3의 값이 주어진다. JEUS AJP13 리스너와 연동하는 경우는 1을 추천한다. 1의 경우 retry를 하지 않겠다는 의미이다.

    • 다음은 위 예제에 대한 설명이다.

      worker.johan_servlet_engine1.retries=1 

      'johan_servlet_engine1' worker는 retry를 사용하지 않는다. JEUS와 연동할 경우 추천사항이다.

  • worker.<worker_name>.reference

    • 많은 load balancer worker들을 설정할 때 유용하며 지정된 값에 해당하는 worker의 설정값을 reference할 수 있는 설정이다.

      예를 들어 "worker.castor.reference=worker.pollux"라고 설정했다면 명시적으로 castor worker에서 설정한 값을 제외하고 모든 pollux worker의 설정들을 상속받게 된다.

    • 다음은 위 예제에 대한 설명이다.

      worker.<node-name>_servlet_<engine-name>.reference=johan_servlet_engine1 

      해당 서블릿 엔진 즉, 해당 worker는 특정 설정을 제외하고는 모두 'johan_servlet_engine1' worker의 설정과 동일한 값들을 상속받아 사용한다.

      참고

      호스트, 포트 정보를 제외하고는 모두 "johan_servlet_engine1"의 속성들을 그대로 사용함을 알 수 있다. 물론 WEBMain.xml의 read-timeout, output-buffer-size, Thread의 min, max가 4개의 서블릿 엔진의 AJP13 리스너에서 동일하다고 가정한 것이다. 만약 다를 경우 해당 설정 값들을 재정의하여 사용한다.

다음은 위의 예제에 항목 외의 의미있는 항목에 대한 설명들이다.

  • worker.<node-name>_servlet_<engine-name>.host=<host-name>

    • 해당 Servlet Engine 즉, 해당 worker의 적절한 호스트 이름 또는 IP 주소를 적절하게 설정한다.

  • worker.<node-name>_servlet_<engine-name>.port=<port>

    • 해당 Servlet Engine 즉, 해당 worker의 적절한 포트를 설정한다.

httpd.conf 설정

httpd.conf 파일에는 mod_jk 모듈을 이용하기 위해 다음 사항을 추가해야 한다(Windows에 Apache 웹 서버가 설치되어 있다고 가정한다).

[예 4.9] <<httpd.conf>>

. . .
LoadModule    jk_module  "c:/ajp13/lib/mod_jk.so"
JkWorkersFile "c:/ajp13/conf/workers.properties"
JkLogFile     "c:/ajp13/log/mod_jk.log"
JkLogLevel    debug
JkMount /examples/* jeus_load_balancer_workers
. . .


다음은 설정항목에 대한 설명이다.

항목설명
LoadModule

위에서 down받은 mod_jk.so를 Apache 웹 서버에 load한다.

mod_jk.so의 경우 "c:/ajp13/lib"위치에 저장하였다고 가정한다.

JkWorkersFileworkers.properties 파일이 위치한 경로를 지정한다. 위에서 작성한 workers.properties의 경우 "c:/ajp13/conf" 위치에 저장하였다고 가정한다.
JkLogFile

로그 파일들이 남을 경로를 지정한다.

로그들은 "c:/log"위치에 'mod_jk.log'란 이름으로 저장할 것임을 설정한다.

JkLogLevel

로그를 저장할 때의 적절한 레벨을 설정한다.

저장 로그 레벨은 "debug"로 한다. "debug"이외에 "error", "info" 등으로 설정 가능하다.

JkMount

가장 중요한 설정으로, 어떤 URL요청이 들어 왔을때 어떤 worker로 분배할지를 선택한다. workers.properties에서 설정한 특정 worker를 지정하면 된다.

여기서는 /examples의 하위 모든 요청을 workers.properties에서 설정한 load balancer worker인 "jeus_load_balancer_workers"로 보낸다. "jeus_load_balancer_workers"는 또 다른 4명의 다른 worker들로 구성되고 웹 서버로 요청이 들어오면 4명의 worker에게 적절히 load balance될 것이다.

4.4.2. IIS 웹 서버 설정과 클러스터링

IIS 웹 서버를 웹 컨테이너 앞단에서 사용하기 전에 다음의 과정이 선행되어야 한다.

  1. 컨테이너 리스너와 IIS 웹 서버 간의 통신을 위하여 mod_jk 라이브러리(ISAPI Redirector)를 설치한다.

  2. WEBMain.xml에서 AJP13 리스너를 설정한다(“4.3.3. AJP13 리스너 설정” 참조).

  3. workers.properties 파일을 생성하고 편집한다.

  4. uriworkermap.properties 파일을 생성하고 편집한다.

  5. IIS 웹 서버의 환경을 설정한다.

  6. IIS 웹 서버를 재시작한다.

참고

IIS의 경우 IIS 6, mod_jk의 경우 1.2.20을 기준으로 설명한다.

mod_jk 라이브러리 설치하기(ISAPI Redirector설정)

IIS 웹 서버를 웹 컨테이너의 앞 단에서 사용하기 위해서는 "isapi_redirect"라는 IIS plugin을 IIS에 추가해야 한다.

다음의 URL에서 Windows용 binary를 down받을 수 있으며 일반적으로 isapi_redirect.dll를 이름으로 설정하여 사용한다.

http://www.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/

참고

만약 위의 URL과 JEUS에서도 특정 OS의 적절한 바이너리를 구하지 못할 경우는 source code를 down받아 새로 컴파일해야 한다.

"isapi_redirect.dll"이 "c:\ajp13"에 위치한다고 가정하고 이를 복사한다.

c:\ajp13\isapi_redirect.dll

WEBMain.xml 설정

“4.4.1. Apache 웹 서버 설정과 클러스터링”의 "WEBMain.xml 설정"과 동일하다.

workers.properties 설정

“4.4.1. Apache 웹 서버 설정과 클러스터링”의 "workers.properties 설정"과 동일하다.

uriworkermap.properties 설정

uriworkermap.properties 설정은 매우 간단하다. 웹 서버로 들어온 요청들에 대해 URI 패턴을 검사하여 JEUS로 forwarding하는 rule mapping을 정의하는 설정 파일이며, 형식은 다음과 같다.

/<application_name>=<worker_name>

만약 /examples의 하위 모든 요청을 worker1에게 매핑하려면 "/examples/*=worker1"과 같이 설정한다. “4.4.1. Apache 웹 서버 설정과 클러스터링”의 "workers.properties 설정"에서 이미 load balancer worker인 jeus_load_balancer_workers를 설정하였으므로 다음과 같이 설정한다.

[예 4.10] <<uriworkermap.properties>>

. . .
/examples/*=jeus_load_balancer_workers
. . .

IIS 웹 서버 환경설정

IIS 웹 서버 환경설정을 위해서 다음의 사항을 가정한다.

  • isapi_redirector.dll은 "c:\ajp13\isapi_redirect.dll"에 위치한다고 가정한다.

  • workers.properties, uriworkermap.properties의 경우 "c:\ajp13\conf" 하위에 존재한다고 가정한다.

  • 'isapi.log'란 이름의 로그를 "c:\ajp13\log" 하위에 저장한다고 가정한다.

다음은 isapi_redirect를 IIS plugin에 추가하는 방법이다.

  1. Windows에서 레지스트리 편집기를 실행하고 regedit명령을 실행한다.

  2. "HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0"과 같은 레지스트리 키를 생성한다.

  3. 'extension_uri'라는 문자열 값을 추가한 후 값으로 "/jakarta/isapi_redirect.dll"을 입력한다.

  4. 'log_file'이라는 문자열 값을 추가한 후 값으로 로그 파일 정보를 입력한다.

    "c:\ajp13\log\isapi.log"
  5. 'log_level'이라는 문자열 값을 추가한 후 값으로 "debug"를 입력한다.

    "debug" 외에 "error", "emerg"등을 입력 할 수 있다. 적절한 레벨의 정보를 입력한다.

  6. 'worker_file'이라는 문자열 값을 추가한 후 값으로 worker 설정 파일이 존재하는 경로를 입력한다.

    예: "c:\ajp13\conf\workers.properties"
  7. 'worker_mount_file이라는 문자열 값을 추가한 후 값으로 uriworkermap 설정 파일이 존재하는 경로를 입력한다.

    예: "c:\ajp13\conf\uriworkermap.properties"
  8. IIS 관리 콘솔 툴을 실행하고 "jakarta"라는 새로운 가상 디렉터리를 생성한다. 이름은 반드시 "jakarta"여야 하며 물리적은 경로는 isapi_redirect.dll이 위치한 곳을 선택해야 한다. (예: "c:\ajp13")

    또한 가상 디렉터리 액세스 권한에 "실행(ISAPI응용프로그램 또는 CGI)"을 체크한다.

  9. IIS 관리 콘솔에서 해당 [웹 사이트] > [속성] > [ISAPI 필터] 탭에서 필터를 하나 추가한다. 이름 부분은 적당히 "ajp13 filter for jeus", 실행 파일 부분에는 isapi_redirector.dll이 위치한 full path를 지정한다.

    예: c:\ajp13\isapi_redirect.dll
  10. IIS 관리 콘솔에서 [웹 서비스 확장]을 선택하고 마우스 오른쪽 버튼을 클릭해서 [새 웹 서비스 확장]을 선택한다. 확장 이름 부분은 적당히 "ajp13 filter for jeus"를 입력한다. [추가] 버튼을 통해 isapi_redirect.dll이 위치한 곳을 선택하거나 0이 파일이 위치한 full path를 입력한다. (예: c:\ajp13\isapi_redirect.dll)

    이어서 확장 상태를 허용됨으로 설정을 체크하고 [확인] 버튼을 클릭한다.

  11. IIS 관리 콘솔에서 IIS를 재시작한다.

    [웹 사이트] > [속성] > [ISAPI 필터] 탭에서 "ajp13 filter for jeus" 필터 부분 상태 정보에 녹색 화살표가 위로 향하고 있음을 반드시 확인한다. 웹 서비스 확장에서 "ajp 13 filter for jeus"로 등록한 웹 서비스 확장의 상태가 허용됨으로 되어 있음을 확인한다.

4.4.3. SunOne(Iplanet) 웹 서버 설정과 클러스터링

SunOne(Iplanet) 웹 서버를 웹 컨테이너 앞 단에서 사용하기 전에 다음과 같은 과정이 선행되어야 한다.

  1. 컨테이너 리스너와 SunOne(Iplanet) 웹 서버 간의 통신을 위하여 mod_jk 라이브러리(NSAPI Redirector)를 설치한다.

  2. WEBMain.xml에서 AJP13 리스너를 설정한다(“4.3.3. AJP13 리스너 설정” 참조).

  3. workers.properties 파일을 생성하고 편집한다.

  4. SunOne 웹 서버 설정(magnus.conf, obj.conf파일)을 수정한다.

  5. SunOne 웹 서버를 재시작한다.

참고

SunOne(iplanet)의 경우 Sun ONE 웹 서버 6.1, mod_jk의 경우 1.2.20을 기준으로 설명한다.

mod_jk 라이브러리 설치하기

SunOne(Iplanet) 웹 서버를 웹 컨테이너의 앞 단에서 사용하기 위해서는 "nsapi_redirect"라는 SunOne(Iplanet) plugin을 SunOne(Iplanet) 웹 서버에 추가해야 한다.

다음의 경로에서 Windows용 binary를 down받을 수 있으며 일반적으로 "nsapi_redirect.dll"를 이름으로 설정하여 사용한다.

http://www.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/

참고

만약 위의 URL과 JEUS에서도 특정 OS의 적절한 binary를 구하지 못할 경우는 source code를 다운받아 새로 컴파일해야 한다.

"nsapi_redirect.dll"이 "c:\ajp13"에 위치한다고 가정하고 이를 복사한다.

c:\ajp13\nsapi_redirect.dll

WEBMain.xml 설정

“4.4.1. Apache 웹 서버 설정과 클러스터링”의 "WEBMain.xml 설정"과 동일하다.

workers.properties 설정

“4.4.1. Apache 웹 서버 설정과 클러스터링”의 "workers.properties 설정"과 동일하다.

SunOne(Iplanet) 웹 서버 설정

SunOne(Iplanet)에서 웹 서버를 설정하기 위해서 다음을 가정한다.

  • nsapi_redirector.dll은 "c:\ajp13\nsapi_redirect.dll"에 위치한다고 가정한다.

  • workers.properties의 경우 "c:\ajp13\conf" 하위에 존재한다고 가정한다.

  • nsapi.log란 이름의 로그를 "c:\ajp13\log"하위에 저장한다고 가정한다.

다음은 nsapi_redirect를 SunOne(Iplanet)에 추가하는 방법이다. 해당 magnus.conf에 다음과 같은 설정을 추가한다.

[예 4.11] <<magnus.conf>>

. . .
Init fn="load-modules" funcs="jk_init,jk_service" shlib="c:/ajp13/nsapi_redirec
t.dll" shlib_flags="(global|now)"
Init fn="jk_init" worker_file="c:/ajp13/conf/workers.properties" log_level="deb
ug" log_file="c:/ajp13/log/nsapi.log"
. . .


"/exmaples"의 하위 모든 요청에 대해 workers.properties에서 설정한 'jeus_load_balancer_workers'로 forward하는 설정을 magnus.conf에 추가한다. <Object name= "default"> 태그 안에 'jknsapi'란 이름을 설정한다. 반드시 'jknsapi'라고 설정할 필요는 없고 새로운 Object를 선언하기 위한 것이므로 적절한 이름을 설정한다.

'jknsapi'란 이름으로 정의 하였다면 <Object name="jknsapi"> 태그를 정의하고 worker를 할당하는 설정을 추가한다.

[예 4.12] <<magnus.conf>>

<Object name="default">
 . . .
 NameTrans fn="assign-name" from="/examples/*" name="jknsapi" 
 . . .
</Object>
. . .
<Object name="jknsapi">
 ObjectType fn="force-type" type="text/plain"
 Service fn="jk_service" worker="jeus_load_balancer_workers" path="/examples/*"
</Object>
. . .


4.4.4. WebtoB 웹 서버 설정과 클러스터링

본 절에서는 2개의 예를 통하여 간단한 WebtoB 클러스터 환경을 구성하는 방법을 설명한다. 각 컨테이너가 각각의 WebtoB 서버를 가지는 간단한 평행 구조 설정과 각 Context Group과 컨테이너가 클러스터 내의 모든 WebtoB 서버와 연결되는 설정이다.

간단한 구성의 경우

첫 번째의 간단한 예에서는 2개의 WebtoB 서버가 각각 2개씩의 전용 웹 컨테이너에 각각 연결된다(총 4개의 컨테이너). 다음은 간단한 평행 구조 설정을 표현한 그림이다.

[그림 4.4] 2개의 WebtoB 웹 서버가 2개의 웹 컨테이너와 연결된 경우

2개의 WebtoB 웹 서버가 2개의 웹 컨테이너와 연결된 경우


따라서, 2개의 JEUS 노드의 Context Group들은 해당 그룹들이 동일하도록 설정해야 한다(즉, Context Group A, B, C, D는 모두 동일하다).

그리고, 이런 구성에서는 Session Server를 사용해야 한다. 간단한 Session Routing 기술은 모든 웹 서버와 Context Group들이 서로 연결되어 있을 경우에만 작동되기 때문이다(좀 더 복잡한 시나리오는 나중에 소개한다). 그러나 WebtoB 서버가 부하 분산기로 사용되면(그림에서는 타사제품의 부하 분산기 사용되고 있음), SessionServer가 없더라도 Session Routing이 가능하다(기본적으로 SessionServer의 사용은 권장 사항이다).

다음 그림은 점선 안의 모습을 좀 더 자세히 보여주는 그림이다. 이 안에서는 2개의 Context Group/리스너들(리스너 A, B)가 2개의 서로 다른 웹 컨테이너에 설정되고 1개의 WebtoB Server(A)에 연결되어 있는 모습을 보여준다.

[그림 4.5] 2개의 WebtoB 리스너가 1개의 WebtoB에 연결된 경우

2개의 WebtoB 리스너가 1개의 WebtoB에 연결된 경우


WebtoB 서버가 HTH 프로세스를 가지고 있음을 주시해보자.

각 HTH 프로세스는 몇 개의 하위 프로세스를 가지고 있는 컨테이너처럼 행동한다. 이러한 하위 프로세스는 WebtoB 리스너의 한 “Worker Thread”에 연결될 때 사용된다. 이것은 HTH의 수, HTH의 최소 및 최대값, 연동되는 WebtoB 리스너의 수가 리스너들의 최대 및 최소 Worker Thread Pool 값의 설정과 관계가 있음을 나타낸다.

주의해서 설정해야 할 파라미터는 WebtoB 리스너의 Worker Thread Pool <max> 값과 WebtoB 서버 설정의 'MaxProc' 값이다. HTH 프로세스의 개수는 WebtoB 서버 설정에 존재하는 HTH 프로세스 개수 값을 그대로 WebtoB 리스너의 hth-count 설정에 반영하기만 하면 된다.

WebtoB 서버의 MaxProc 값과 WebtoB 리스너의 thread-pool max 값은 다음과 같은 공식으로 표현될 수 있다.

MinProc = Listener A min threads + Listener B min threads + … + Listener X min threads setting.
MaxProc = Listener A max threads + Listener B max threads + … + Listener X max threads setting.

WebtoB 서버의 연결은 이 값들이 제대로 설정되어 있지 않아도 무방하다. 그러나 성능에 악영향을 끼칠 수 있음을 감안해야 한다. 또한 WEBMain.xml의 <hth-count> 태그는 WebtoB 설정 파일의 *NODE 요소 설정 중 HTH 프로세스 개수와 동일해야 한다.

다음은 WebtoB 리스너 A와 B가 해당 WEBMain.xml에 어떻게 설정되어야 하는지에 대한 예이다.

[예 4.13] 웹 컨테이너 A : <<WEBMain.xml>>

<?xml version="1.0"?>
<web-container xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    . . .
    <context-group>
        . . .
        <webserver-connection>
            <webtob-listener> <!--Listener A-->
                <listener-id>WebListenerA</listener-id>
                <port>9900</port>
                <output-buffer-size>16384</output-buffer-size>
                <thread-pool>
                    <min>2</min>
                    <max>10</max>
                    <step>2</step>
                    <max-idle-time>60000</max-idle-time>
                    <max-wait-queue>5</max-wait-queue>
                </thread-pool>
                <postdata-read-timeout>
                    40000
                </postdata-read-timeout>
                <scheme>http</scheme>
                <hth-count>2</hth-count>
                <disable-pipe>false</disable-pipe>
                <webtob-address>
                    111.111.111.111
                </webtob-address>
                <registration-id>MyGroup</registration-id>
                <webtob-home>c:\WebtoB\</webtob-home>
                    <read-timeout>120000</read-timeout>
                    <reconnect-timeout>60000</reconnect-timeout>
                <webtob-backup>
                    . . .
                </webtob-backup>
            </webtob-listener>
        </webserver-connection>
        . . .
    </context-group>
    . . .
</web-container>


[예 4.14] 웹 컨테이너 B : <<WEBMain.xml>>

<?xml version="1.0"?>
<web-container xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    . . .
    <context-group>
        . . .
        <webserver-connection>
            <webtob-listener> <!--Listener B-->
                <listener-id>WebListenerB</listener-id>
                <port>9900</port>
                <output-buffer-size>16384</output-buffer-size>
                <thread-pool>
                    <min>4</min>
                    <max>15</max>
                    <step>2</step>
                    <max-idle-time>60000</max-idle-time>
                    <max-wait-queue>5</max-wait-queue>
                </thread-pool>
                <postdata-read-timeout>
                    40000
                </postdata-read-timeout>
                <scheme>http</scheme>
                <hth-count>2</hth-count>
                <disable-pipe>false</disable-pipe>
                <webtob-address>
                    111.111.111.111
                </webtob-address>
                <registration-id>MyGroup</registration-id>
                <webtob-home>c:\WebtoB\</webtob-home>
                <read-timeout>120000</read-timeout>
                <reconnect-timeout>60000</reconnect-timeout>
                <webtob-backup>
                    . . .
                </webtob-backup>
            </webtob-listener>
        </webserver-connection>
        . . .
    </context-group>
    . . .
</web-container>


다음은 WebtoB Server A가 [그림 4.5]에 있는 간단한 예와 같이 설정되기 위해 http.m 파일에 어떻게 설정되는지를 보여주는 예이다.

[예 4.15] http.m of WebtoB Server A

*NODE
foo     … 
   HTH = 2,
   JSVPORT = 9900,
   …   
*SVRGROUP
jsvg    NODENAME = foo, SVRTYPE = JSV

*SERVER
MyGroup    SVGNAME = jsvg, MinProc = 6, MaxProc = 25

*URI
uri1  Uri = "/examples/", Svrtype = JSV
uri2  Uri = "/test/", Svrtype = JSV
*EXT
jsp  MimeType = "application/jsp", SvrType = JSV


위의 예에서는 "리스너 A의 포트 = 리스너 B의 포트 = WebtoB Server의 JSVPORT(=9900)"이다. 또한 Context Group A와 B의 이름이 “MyGroup”으로 동일하다. 이 이름은 등록 ID로 사용되고, http.m의 *SERVER 요소에도 존재한다. 또한 두 WEBMain.xml 파일의 <hth-count> 태그의 값은 http.m의 *NODE 하위의 HTH 수와 동일함을 주목한다(값은 모든 파일에서 동일하게 2로 주어졌다).

마지막으로 중요한 것은 min과 max 값 설정이다. 샘플 설정 파일들에서 앞에서 제시한 공식에 따라 다음과 같이 설정되었다.

HTH MinProc = 6 = 2 thread + 4 thread, HTH MaxProc = 25 = 10 thread + 15 thread

그리고 HTH의 수가 2(http.m에서)이고 MinProc/MaxProc의 값이 HTH 당 값이기 때문에 Worker Thread/연결 값은 2배가 됨을 짐작할 수 있다. 그러므로 이 설정의 Worker Thread의 최댓값은 "MaxProc * HTH 수 = 25 * 2 = 50"이 된다. WebtoB 리스너는 자동으로 현재 HTH 프로세스 수에 맞춰진다. 그러므로 WebtoB 리스너를 설정할 때에는 HTH 프로세스가 1개인 것처럼 생각해서 설정한다.

마지막으로, 동일한 설정이 또 다른 Context Grop이 추가될 때마다 적용된다.

복잡한 구성의 경우

다음의 예에서는 4개의 Context Group을 가지고 있고, 8개의 WebtoB 리스너를 가진 4개의 웹 컨테이너가 2개의 WebtoB 서버에 연결된다. 즉, 모든 컨테이너와 Context Group은 모든 WebtoB 서버에 연결된다.

[그림 4.6] 복잡한 WebtoB 웹 서버 구성

복잡한 WebtoB 웹 서버 구성


위 그림에서는 특별한 설정 사항을 볼 수 없다. 각 웹 서버와 각 웹 서버 리스너에서 위의 예와 동일한 방법으로 설정한다.

한 가지 차이점은 각 Context Group이 모든 웹 서버에 연결되어 있고, Session Routing을 이용할 수 있다는 것이다. 이런 환경에서 Session Server를 사용하는 것은 선택 사항이다. 하지만, 높은 안정성을 위해서는 Session Routing과 함께 Session Server의 사용을 권장한다.

Session Server는 웹 컨테이너의 장애가 발생하는 경우 기본적으로 세션 데이터를 복구하는 기능을 가진다.

4.5. TCP 리스너 사용

TCP 리스너는 내부 HTTP 리스너가 동작하는 것과 같은 방식으로 동작한다. 그러나, 주목해야 할 점은 TCP 리스너는 HTTP프로토콜을 지원하지 않는다는 것이다. 대신에, 개발자는 표준 HTTP 프로토콜 대신에 맞춤 통신 프로토콜을 구현할 수 있다.

“TCP”라는 개념은 이 리스너가 통신 프로토콜 구조의 낮은 레벨에서 작업할 수 있다는 것을 강조하게 된다. 즉, 이것은 애플리케이션 레벨에서 보다는 전송 레벨에서 동작한다고 볼 수 있다(HTTP 리스너는 애플리케이션 레벨의 리스너이다). 그러므로, TCP 리스너가 동작하기 위해서는 애플리케이션 레벨의 통신 프로토콜의 구현이 요구된다.

본 절에서는 이러한 맞춤 프로토콜 제공자를 구현하는 방법과 그것을 어떻게 TCP 리스너와 함께 등록하는지, 그리고 TCP 리스너에 접속하는 클라이언트를 작성하고 맞춤 프로토콜을 사용하여 통신하는 방법에 대하여 알아본다.

TCP 리스너는 HTTP 또는 HTTPS 프로토콜을 가지고 사용할 수 없는 통신 요구사항이 있을 경우에만 사용한다.

본 절에서는 다음의 과정과 절차에 대해서 설명한다.

  1. 맞춤 통신 프로토콜 정의

  2. dispatcher 설정 클래스 구현(프로토콜 설정 정보)

  3. TCP handler Servlet 구현(프로토콜 구현)

  4. 맞춤 프로토콜 구현을 위한 TCP 리스너 설정

  5. TCP 클라이언트 구현

  6. TCP 클라이언트 컴파일과 구동

참고

TCP 리스너 API는 "JEUS_HOME\docs\api\jeusapi\index.html"에서 "Webcontainer TCP Listener API Reference"에도 설명되어 있다.

다음은 본 절에서 사용할 용어에 대한 설명이다.

파일설명
프로토콜(통신 프로토콜)TCP 클라이언트와 TCP Handler 사이에서(TCP 리스너가 중간역할) 교환되는 메시지의 구조와 내용을 정의한다.
메시지TCP 클라이언트와 TCP Handler 사이에서 교환되는 데이터이다. 프로토콜에서 정의한 구조에 맞아야 한다.
TCP 클라이언트TCP 리스너와 교신하며 메시지를 주고받는 외부의 애플리케이션(TCP Handler에 의해 처리된다)이다. 메시지를 주고받는 것은 표준 소켓을 사용한다.
TCP Handler

TCP 리스너를 통하여 메시지를 받고 구현된 방법대로 메시지를 처리한다.

TCP Handler는 jeus.servlet.tcp.TCPServlet의 하위 클래스의 형태로 구현되고 웹 컨테이너에 보통 서블릿처럼 등록된다. TCP Handler는 TCP 프로토콜 위에 존재하는 맞춤 프로토콜의 “제공자” 또는 “구현체”처럼 동작한다고 말할 수 있다.

TCP dispatcher configuration class

TCP 설정 dispatcher 클래스는 jeus.servlet.tcp.TCPDispatcherConfig를 확장한 클래스이다. 이 클래스는 맞춤 프로토콜에 대한 정보를 TCP 리스너로 전달하여 이 non-HTTP 메시지를 해석하여 처리하도록 한다.

이 클래스는 JEUS_HOME\lib\application 디렉터리 아래에 놓아두고, WEBMain.xml의 TCP 리스너 설정 항목 중 <dispatcher-config-class> 태그에 설정한다(“4.3. 리스너 설정” 참조).

TCP 리스너

맞춤 메시지에 대한 해석과 라우팅 인프라를 제공한다. TCP 클라이언트와 TCP Handler 사이에서 non-HTTP 중간 매개체로 역할을 한다.

이것은 다른 HTTP 리스너와 마찬가지로 웹 컨테이너 내에 존재한다.

4.5.1. 맞춤 통신 프로토콜 정의

TCP 리스너는 TCP 클라이언트와 TCP Handler 간에 통신되는 모든 메시지들을 두 부분으로 나누는데, 헤더와 바디가 그것이다. 일반적으로 헤더는 기본적이면서도 표준 정보를 전달하고 크기도 정해져 있다. 바디는 전송될 임의의 사용자 데이터가 포함된다(예: HTTP 응답의 HTML 코드).

TCP 리스너가 어떻게 사용되는지 설명하기 위하여 여기에서 간단한 프로토콜(메시지 구조)을 정의한다. 이것은 나중에 사용할 것이다.

맞춤 통신 프로토콜(맞춤 메시지 구조)은 다음과 같은 내용을 가진다.

  • 헤더

    1. 헤더는 4Bytes의 magic 수로 시작한다. 이는 사용되는 프로토콜을 식별한다. “7777”로 설정한다.

    2. 1 Byte의 type 필드가 magic 수 다음에 쓰인다. '0'은 요청을 '1'은 응답을 의미한다.

    3. 세 번째 항목은 4Bytes의 body length이다. 이 항목은 메시지의 바디 부분의 Byte 수를 기록한다. 여기의 예에는 크기가 128Bytes로 고정되어 있다.

    4. 마지막 네 번째의 항목은 32Bytes의 string으로 service name을 기록한다. 여기서는 그 값으로 요청을 처리하는 TCPServlet의 이름을 넣는다.

  • 바디

    메시지의 바디 부분은 128Bytes 길이를 가진 문자 데이터를 넣으면 된다. 여기서는 이 128Byte 블록을 2개의 임의의 64Bytes 텍스트 string으로 넣는다.

이제부터 어떻게 TCP 리스너를 통하여 맞춤 프로토콜을 적용하는지에 대하여 살펴본다. 먼저, 설정 dispatcher 클래스를 구현하고 TCP Handler 클래스(TCPServlet의 하위 클래스)를 구현한다.

4.5.2. Dispatcher 설정 클래스 구현

Dispatcher 설정 클래스는 jeus.servlet.tcp.TCPDispatcherConfig 클래스의 하위 클래스이다. 이 클래스의 abstract 메소드는 TCP 리스너가 알맞은 TCP Handler에게 메시지를 전달하기 위해 필요한 여러 가지 정보들을 전달되도록 구현되어야 한다.

참고

이 메소드들은 "JEUS_HOME\docs\api\jeusapi\index.html"에서 "Webcontainer TCP Listener API Reference"에 설명되어 있다.

다음의 코드(SampleConfig.java)는 전 절에서 정의된 프로토콜에 기반하여 어떻게 구현하였는지를 보여준다. 특정 메소드들이 어떻게 사용되었는지는 주석을 참고한다.

[예 4.16] <<SampleConfig.java>>

package sample.config;

import javax.servlet.*;
import javax.servlet.http.*;
import jeus.servlet.tcp.*;

/*
  This class extends the abstract class jeus.servlet.tcp.
  TCPDispatcherConfig class. This implementation provides
  routing and handling information to the TCP listener
  according to our defined communications protocol.
*/
public class SampleConfig extends TCPDispatcherConfig {
    /*
      Any init code goes into this method. This method will be    
      Called once after starting the TCP listener.
      We leave this method empty for our simple example.
    */
    public void init() {}

    /*
      This method returns the fixed-length of the header so                  
      that the TCP listener knows when to stop                                   
      reading the header. The header length for 
      our example is 41 (bytes): 4 (magic) + 1 
      (type) + 4 (body length) + 32 (service name).
    */
    public int getHeaderLength() {
        return 41; 
    }
    
    /*
      This method must return the length of the body.
      For our example, this length is represented as an 
      “int” in the header, starting at position “5” 
      (see the protocol definition above).
    */
    public int getBodyLength(byte[] header) {
        return getInt(header, 5);
    }

    /*
      This method must return the context path so that the
      request can be routed by the TCP listener to the context
      that contains the TCP handler (TCPServlet implementation).
      For our example, we always use the context path “/tcptest”.
    */
    public String getContextPath(byte[] header) {
        return "/tcptest";
    }

    /*
      This method must return the name (path) of the TCP 
      handler(TCPServlet) relative to the context path. 
      For our example, we fetch this name from the 9th position
      in the header.
    */
    public String getServletPath(byte[] header) {
        return "/" + getString(header, 9, 32);
    }

    /*
      This method returns some path info from the header.
      It is not used in our example and thus returns “null”.
    */
    public String getPathInfo(byte[] header) {
        return null;
    }

    /*
      This method returns any session ID embedded in the header.
      It is not used in our example and thus returns “null”.
    */
    public String getSessionId(byte[] header) {
        return null;
    }

    /*
      This method determines whether the TCP listener 
      should keep the socket connection open after the TCP handler
      has delivered its response. If it returns “false”, the
      connection will be dropped like in HTTP communications.
      If it returns “true” the connection will be kept open
      like in the Telnet or FTP protocols. For our example,
      we choose to make it persistent (connection not closed
      by the TCP listener).
    */
    public boolean isPersistentConnection() {
        return true;
    }
}


4.5.3. TCP Handler 구현

TCP Handler는 항상 jeus.servlet.tcp.TCPServlet의 하위 클래스이다. 여기에는 항상 overridden되는 abstract void service(TCPServletRequest req, TCPServletResponse, res) 메소드가 존재한다.이 서비스 메소드는 맞춤 프로토콜에 준하는 메시지를 처리할 수 있도록 구현되어야 한다. 메시지는 TCPServletRequest 객체에 전달되고 TCP Handler로부터 전달된 응답은 TCPServletResponse 객체의 output stream으로 출력된다.

다음 예제는 맞춤 프로토콜에 준하는 메시지를 처리하는 TCP Handler의 구현 코드이다. 이는 다소 직설적인 구현으로 TCPServletRequest에서 헤더와 바디가 꺼내어져 오고 System.out에 헤더와 바디의 각 항목들이 출력된다. 그 후에 service() 메소드는 2개의 응답 메시지를 생성하고 TCPServletResponse 객체의 output stream에 출력한다.

[예 4.17] <<SampleServlet.java>>

package sample.servlet;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import jeus.servlet.tcp.*;

/**
 * Sample TCPServlet implementation
 *
 * Protocol scheme:
 * 
 * common header (for request and response) : total 41 byte
 * 
 *   magic field: length = 4 byte, value = 7777
 *   type field : length = 1 byte, 0 : request, 1:response
 *   body length field : length = 4, value = 128
 *   service name field : length = 32
 * 
 * request and response body
 *  
 *   message1 field : length = 64
 *   message2 field : length = 64
 * 
 */

public class SampleServlet extends TCPServlet {

    public void init(ServletConfig config) throws ServletException {
    }

    public void service(
            TCPServletRequest req, TCPServletResponse res)
        throws ServletException, IOException {
        byte[] header = req.getHeader();
        byte[] body = req.getBody();

        String encoding = res.getCharacterEncoding();
        if (encoding == null)
            encoding = "euc-kr";

        DataInputStream in = new DataInputStream(
            new ByteArrayInputStream(header));
        int magic = in.readInt();
        System.out.println(
            "[SampleServlet] received magic = " + magic);

        byte type = (byte)in.read();
        System.out.println(
            "[SampleServlet] received type = " + type);

        int len = in.readInt();
        System.out.println(
            "[SampleServlet] received body length = " + len);

        byte[] svcname = new byte[32];
        in.readFully(svcname);
        System.out.println(
            "[SampleServlet] received service name = " +
            (new String(svcname)).trim());

        String rcvmsg = null;
        rcvmsg = (new String(body, 0, 64)).trim();
        System.out.println(
            "[SampleServlet] received msg1 = " + rcvmsg);
        //rcvmsg = (new String(body, 64)).trim();
        try {
            rcvmsg = (new String(body, 64, 64, encoding)).trim();
        } catch (Exception e) {}

        System.out.println(
            "[SampleServlet] received msg2 = " + rcvmsg);

        String msg1 = "test response";
        String msg2 = "test response2";

        byte[] result1 = null;
        byte[] result2 = null;
        if (encoding != null) {
            try {
                result1 = msg1.getBytes(encoding);
                result2 = msg2.getBytes(encoding);
            } catch (UnsupportedEncodingException uee) {
                result1 = msg1.getBytes();
                result2 = msg2.getBytes();
            }
        } else {
            result1 = msg1.getBytes();
            result2 = msg2.getBytes();
        }

        header[4] = (byte)1; // mark as response

        ServletOutputStream out = res.getOutputStream();
        out.write(header);

        byte[] buf1 = new byte[64];
        System.arraycopy(result1, 0, buf1, 0, result1.length);
        out.write(buf1);

        byte[] buf2 = new byte[64];
        System.arraycopy(result2, 0, buf2, 0, result2.length);
        out.write(buf2);

        out.flush();
        //out.close();
    }

    public void destroy() {
    }
}


4.5.4. 맞춤 프로토콜 코드를 위한 TCP 리스너 설정

위에서 구현한 코드(SampleConfig.java와 SampleServlet.java)를 기반으로 하여 TCP 리스너를 설정한다.

  1. SampleConfig.java 와 SampleServlet.java를 컴파일한다.

    C:\tcptest>javac -classpath d:\jeus\lib\system\jeus.jar Sam
    pleConfig.java SampleServlet.java
  2. SampleConfig.class 파일을 다음의 디렉터리에 위치시킨다.

    JEUS_HOME\lib\application\sample\config
  3. SampleServlet.class를 다음 디렉터리에 위치시킨다(“tcpcontext” 디렉터리 이름을 알맞은 것으로 대체한다).

    JEUS_HOME\webhome\app_home\tcpcontext\WEB-INF\classes\sample\servlet

    Context Group에 대한 설명은 “제3장 Context Group”을 Context 등록은 “제6장 Web Context(웹 애플리케이션)”를 참고한다.

  4. 간단한 web.xml을 생성하고 SampleServlet에 대한 정보를 다음과 같이 설정한다.

    [예 4.18] <<web.xml>>

    <?xml version="1.0"?>
    <web-app xmlns=”http://www.tmaxsoft.com/xml/ns/jeus”>
        <display-name>TCPHandlerApp</display-name>
        <distributable/>
        <servlet>
            <servlet-name>sample</servlet-name>
            <servlet-class>
                sample.servlet.SampleServlet
            </servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>sample</servlet-name>
            <url-pattern>/sample</url-pattern>
        </servlet-mapping>
    </web-app>


    위의 web.xml 파일에서 TCP Handler인 sample.servlet.SampleServlet을 간략하게 'sample'이라는 서블릿 이름과 URL 경로인 '/sample'로 어떻게 매핑하는지를 확인한다. 이 맞춤 프로토콜 구현 코드를 이용하려는 TCP 클라이언트는 헤더에 'sample'이라는 서비스 이름을 명시한다. “4.5.1. 맞춤 통신 프로토콜 정의”를 참고한다.

    서비스 이름은 TCP 리스너에 의해 호출되는 SampleConfig 클래스의 getServletPath()에 의해 추출된다. 여기서는 클라이언트가 헤더에 'sample'이라고 지정해 주었고, TCP 리스너는 'sample' 서비스 이름을 web.xml에 정의된 sample.servlet.SampleServlet TCPServlet 클래스를 통하여 매핑하게 된다.

  5. web.xml 파일을 다음의 디렉터리에 위치시킨다.

    JEUS_HOME\webhome\app_home\tcpcontext\WEB-INF
  6. JEUS_HOME\config\<node-name>\<node-name>_servlet_<engine-name> 디렉터리의 WEBMain.xml을 수정한다. WEBMain.xml 파일은 새로운 Context Group(TCPGroup) 그리고 TCP 리스너에 대한 설정 정보를 포함해야한다.

    다음은 예제가 동작하기 위한 WEBMain.xml의 설정 정보이다.

    [예 4.19] <<WEBMain.xml>>

    <?xml version="1.0"?>
    <web-container xmlns=”http://www.tmaxsoft.com/xml/ns/jeus”>
        . . .
        <context-group>
            <group-name>TCPGroup</group-name>
            <webserver-connection>
                <tcp-listener>
                    <listener-id>TCPListener1</listener-id>
                    <port>5555</port>
                    <thread-pool>
                        <min>25</min>
                        <max>30</max>
                        <step>2</step>
                        <max-idle-time>1000</max-idle-time>
                    </thread-pool>
                    <dispatcher-config-class>
                        sample.config.SampleConfig
                    </dispatcher-config-class>
                </tcp-listener>
            </webserver-connection>
        </context-group>
        . . .
    </web-container>


    TCP 리스너 포트(5555), sample.config.SampleConfig을 사용하기 위한 <dispatcher class>의 설정, 앞에서 추가한 새로운 Context Group의 이름(“tcpcontextgroup”)을 주의 깊게 확인한다.

  7. JEUS_HOME\config\<node-name> 디렉터리의 JEUSMain.xml을 수정한다.

    JEUSMain.xml 파일은 새로운 Context(“TCPSample”) 정보들을 포함해야 한다. JEUS_HOME은 "c:\jeus6"로 가정한다.

    [예 4.20] <<JEUSMain.xml>>

    <?xml version="1.0"?>
    <jeus-system xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
        <node>
            <name>tmax1</name>
            . . .
        </node>
        <application>
            <name>TCPSample</name>
            <path>
                c:\jeus6\webhome\app_home\tcpcontext
            </path>
            <deployment-target>
                <target>
                    <engine-container-name>
                        tmax1_container1
                    </engine-container-name>
                    <web-context-group>
                        <name>MyGroup</name>
                    </web-context-group>
                </target>
            </deployment-target>
            <deployment-type>COMPONENT</deployment-type>
            <web-component/>
        </application>
    </jeus-system>


    TCP Context의 공식적인 이름도 “TCPSample”이라고 설정했다. 다음 과정에서는 이 이름의 용도를 잘 살펴보자.

  8. 마지막으로, 이 TCP Context를 위해 deployment descriptor를 생성해야 한다.

    “jeus-web-dd.xml”이라는 파일을 생성하고 다음의 디렉터리에 위치시킨다.

    JEUS_HOME\webhome\app_home\tcpcontext\WEB-INF

    [예 4.21] <<jeus-web-dd.xml>>

    <?xml version="1.0"?>
    <jeus-web-dd xmlns=”http://www.tmaxsoft.com/xml/ns/jeus”>
        <context-path>/tcptest</context-path>
        <docbase>tcpcontext</docbase>
    </jeus-web-dd>


    위의 예에서 어떻게 Context 경로를 SampleConfig.java의 getContextPath() 메소드에 의해 반환되는 값과 일치하도록 설정하는지 유의하여 살펴보기 바란다(“/tcptest”).

    참고

    3부터 7의 과정은 Web Context와 디플로이에 대한 내용을 다루는 “제6장 Web Context(웹 애플리케이션)”에서 자세히 설명한다.

  9. 위의 과정이 모두 완료되면 모든 파일들을 저장하고 “제2장 웹 컨테이너”에서 설명한 것과 같이 웹 컨테이너를 재시작한다.

  10. TCPGroup Context Group, TCPSample Context와 “sample” TCPServlet의 디플로이를 확인하기 위하여 위와 같이 설정을 하면, TCP 리스너에 연결되는 TCP 클라이언트의 구현과 구동에 대하여 알아보고, SampleServlet TCP Handler의 service() 메소드를 호출한다.

4.5.5. TCP 클라이언트 구현

여기의 TCP 클라이언트는 아주 간단 명료하다. TCP 리스너와 소켓 연결을 맺고 이 연결을 통하여 메시지를 전송한다. 이 메시지는 “4.5.1. 맞춤 통신 프로토콜 정의”에서 정의한 맞춤 통신 프로토콜에 준하는 바이트 스트림이다.

설정된 TCP 리스너는 메시지를 받을 것이고 SampleConfig 클래스에 dispatch 정보를 물을 것이다. 이 정보는 SampleServlet 구현 코드의 service() 메소드의 호출을 암시하고 있다. SampleServlet은 클라이언트로부터 전달된 데이터를 출력하고 응답을 생성하여 전송한다. 이 응답은 클라이언트가 받아 System.out으로 출력한다.

다음은 그에 대한 예이다.

[예 4.22] <<Client.java>>

package sample.client;

import java.io.*;
import java.net.*;

public class Client {
    private String address;
    private int port;

    private int magic = 7777;
    private byte type = 0;
    private int bodyLength = 128;
    private byte[] serviceName="sample".getBytes();

    public Client(String host, int port) {
        this.address = host;
        this.port = port;
    }

    public void test()
        throws IOException, UnsupportedEncodingException {
        Socket socket = new Socket(address, port);
        DataOutputStream out = new DataOutputStream(
            new BufferedOutputStream(socket.getOutputStream()));
        DataInputStream in = new DataInputStream(
            new BufferedInputStream(socket.getInputStream()));

        out.writeInt(7777);
        out.write(type);
        out.writeInt(bodyLength);
        byte[] buf = new byte[32];
        System.arraycopy(serviceName, 0, buf, 0, serviceName.length);
        out.write(buf);
        byte[] msg1 = "test request".getBytes();
        byte[] msg2 = "test request2".getBytes();
        buf = new byte[64];
        System.arraycopy(msg1, 0, buf, 0, msg1.length);
        out.write(buf);
        buf = new byte[64];
        System.arraycopy(msg2, 0, buf, 0, msg2.length);
        out.write(buf);

        out.flush();

        // rx msg
        int magic = in.readInt();
        System.out.println("[Client] received magic = " + magic);

        byte type = (byte)in.read();
        System.out.println("[Client] received type = " + type);

        int len = in.readInt();
        System.out.println("[Client] received body length = " + len);

        byte[] svcname = new byte[32];
        in.readFully(svcname);
        System.out.println("[Client] received service name = " +
                           (new String(svcname)).trim());

        byte[] body = new byte[128];
        in.readFully(body);
        String rcvmsg = null;
        rcvmsg = (new String(body, 0, 64)).trim();
        System.out.println("[Client] received msg1 = " + rcvmsg);
        rcvmsg = (new String(body, 64, 64, "euc-kr")).trim();
        System.out.println("[Client] received msg2 = " + rcvmsg);

        out.close();
        in.close();
        socket.close();
    }

    public static void main(String[] argv) throws Exception {
        Client client = new Client("localhost", 5555);
        client.test();
    }
}


위의 클라이언트 코드에서 프로토콜에 필요한 다양한 헤더의 필드들을 어떻게 설정하는지 확인한다.

magic수는 '7777'로, type은 '0(요청)', body length는 '128Bytes(고정 길이)', service name은 'sample'로 되어 있다(SampleServlet의 이름은 web.xml에 설정되어 있다). 그리고 2개의 메시지들을 생성하여 헤더 정보를 전송한 후에 TCP 리스너에게 그들을 전송한다. 마지막으로 hostname을 'localhost'로 포트 번호는 '5555'로 설정하였다.

4.5.6. TCP 클라이언트 컴파일과 실행

본 절에서는 어떻게 이 클라이언트를 컴파일하고 실행하는지에 대해 설명한다.

TCP 리스너가 설정되어 “localhost”에 5555 포트를 이용하여 운영되고 있다고 가정하고 이런 환경에서 다음 과정을 거쳐 TCP 클라이언트를 컴파일하고 실행시켜보자.

  1. Client.java를 컴파일한다.

    C:\tcptest>javac -classpath d:\jeus\lib\system\jeus.jar Client.java
  2. Client.class 파일을 현재 작업 디렉터리(c:\tcptest)의 “sample\client” 디렉터리 하위로 이동한다.

  3. Client.class를 다음과 같이 실행한다(모두 한 줄에).

    C:\tcptest>java  -classpath d:\jeus\lib\system\jeus.jar;.sample.client.Client
  4. JEUS 관리자의 콘솔에 다음과 같이 TCP Handler의 결과값을 보여줘야 한다(SampleServlet 클래스).

    [SampleServlet] received magic = 7777
    [SampleServlet] received type = 0
    [SampleServlet] received body length = 128
    [SampleServlet] received service name = sample
    [SampleServlet] received msg1 = test request
    [SampleServlet] received msg2 = test request2

    참고

    만약 WEBMain.xml의 <redirect-stdout> 태그가 'true'로 설정되어 있으면 “JEUS_HOME\logs\<node-name>_servlet_<engine-name>\stdout_<date>.log”로 결과가 남겨진다.

  5. 다음은 클라이언트의 실행 윈도우에 남겨질 것이다.

    [Client] received magic = 7777
    [Client] received type = 1
    [Client] received body length = 128
    [Client] received service name = sample
    [Client] received msg1 = test response
    [Client] received msg2 = test response2

4.6. 보안(SSL) 리스너 사용

본 절에서는 JEUS 웹 컨테이너에서 보안 리스너를 설정하는 방법에 대해 설명한다.

또한 WEBMain.xml에 기본 리스너를 설정하는 방법과 필수 keystore, truststore 파일을 설정하고 보안 리스너에게 위치나 이 파일들의 암호를 알려주는 방법, 마지막으로 보안된 SSL 연결을 통하여 HTML 파일을 가져오도록 보안 리스너를 시작하고 연결하는 방법에 대하여 설명한다.

4.6.1. 개요

JEUS 웹 컨테이너의 보안 리스너는 직접적인 클라이언트 대 컨테이너의 HTTPS 요청을 지원한다. HTTPS 프로토콜은 SSL(Secure Socket Layer) 기술을 사용하여 인증, 메시지 기밀, 메시지 무결성 서비스를 제공한다. 최근에는, 민감한 정보(신용카드 번호)가 교환되는 대부분의 온라인 서비스들은 SSL을 사용한다.

JEUS 보안 리스너는 JSSE(Java Secure Socket Extensions) API 기반의 HTTPS/SSL 연결을 지원한다. 이 기능은 사용은 가능하지만, 실제 많은 운영 환경이 웹 컨테이너 보다는 웹 서버에서 지원하는 SSL 기능에 의존한다. http://java.sun.com/products/jsse에 SSL과 JSSE에 대한 상세한 정보를 참고한다.

시작하기 전에, 다음과 같은 사항들이 설정되어 있다고 가정한다.

  • WEBMain.xml에 서블릿 엔진이 설정되어 시스템에 추가되어 있다. 자세한 내용은 “제2장 웹 컨테이너”를 참조한다.

  • WEBMain.xml에 'MyGroup'이라는 Context Group이 추가되어 있다. 자세한 내용은 “제3장 Context Group”을 참조한다.

  • MyGroup은 'TestContext'라는 Context를 가지고 '/Test'라는 context path를 가진다.

    MyContext 디렉터리는 'hello.html'을 가지고 이 파일은 우리가 추가할 보안 리스너를 통하여 접근될 것이다. “MyContext\WEB-INF” 디렉터리에는 내용이 없는 web.xml파일이 존재한다. 자세한 내용은 “제6장 Web Context(웹 애플리케이션)”를 참조한다.

4.6.2. 디렉터리 구조

다음은 보안 리스너의 초기 디렉터리 구조이다.

[그림 4.7] 보안 리스너의 초기 디렉터리

보안 리스너의 초기 디렉터리


파일에는 다음과 같은 항목들이 존재한다.

[예 4.23] JEUS_HOME\config\<node-name>\<node-name>_servlet_<engine-name>\WEBMain.xml

<?xml version="1.0"?>
<web-container xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    . . .
    <context-group>
        <group-name>MyGroup</group-name>
        . . .
    </context-group>
    . . .
</web-container>


[예 4.24] JEUS_HOME\config\<node-name>\JEUSMain.xml

<?xml version="1.0"?>
<jeus-system xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    <node>
        <name>tmax1</name>
        . . .
    </node>
    <application>
        <name>TestContext</name>
        <path>
            c:\jeus6\webhome\app_home\MyContext
        </path>
        <deployment-target>
            <target>
                <engine-container-name>
                    tmax1_container1
                </engine-container-name>
                <web-context-group>
                    <name>MyGroup</name>
                </web-context-group>
            </target>
        </deployment-target>
        <deployment-type>COMPONENT</deployment-type>
        <web-component/>
    </application>
</jeus-system>


[예 4.25] JEUS_HOME\webhome\app_home\webapps\MyContext\WEB-INF\jeus-web-dd.xml

<?xml version="1.0"?>
<jeus-web-dd  xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    <context>
        <context-path>/Test</context-path>
    </context>
</jeus-web-dd>


[예 4.26] JEUS_HOME\webhome\app_home\webapps\MyContext\WEB-INF\web.xml

<?xml version="1.0"?>
<web-app xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    <description>A test module.</description>
    <display-name>Test Module</display-name>
    . . .
</web-app>


[예 4.27] JEUS_HOME\webhome\app_home\webapps\MyContext\hello.html

<html>
<head>
<title>Hello</title>
</head>
<body>
<h4><b>Hello, World</b>
(This is hello.html file)</h4>
</body>
</html>


참고

위의 모든 파일과 디렉터리를 전부 설명하지 않았다. 더 자세한 정보는 Context와 관련 파일들에 대한 설명이 있는 “제6장 Web Context(웹 애플리케이션)”(jeus-web-dd.xml과 web.xml)를 참고한다.

Context에 대해서 알지 못하면 여기의 내용을 모두 이해하지 못할 수도 있다. 본 절에서는 설명을 목적으로 할 뿐이며, 제시된 예제를 실행하기 위해서는 위에서 제시한 설정이 필요하다.

4.6.3. 보안 리스너 설정

다음은 <enable-secure>를 true로 설정하여 모든 보안 설정 정보를 기본값으로 사용하고 있는 예이다.

[예 4.28] 보안 리스너 설정 : <<WEBMain.xml>>

<?xml version="1.0"?>
<web-container xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    . . .
    <context-group>
        <group-name>MyGroup</group-name>
        <webserver-connection>
            <http-listener>
                <listener-id>SecureListener1</listener-id>
                <port>443</port>
                <output-buffer-size>8192</output-buffer-size>
                <thread-pool>
                    <min>2</min>
                    <step>1</step>
                    <max-idle-time>300000</max-idle-time>
                    <max-wait-queue>4</max-wait-queue>
                    <max-queue>-1</max-queue>
                </thread-pool>
                <postdata-read-timeout>
                    30000</postdata-read-timeout>
                <back-log>50</back-log>
                <server-access-control>
                    false</server-access-control>
                 <scheme>https</scheme>
                 <ssl-config>
                    <enable-secure>true</enable-secure>
                 </ssl-config>
            </http-listener>
        </webserver-connection>
        <group-docbase>webapps</group-docbase>
        . . .
    </context-group>
    . . .
</web-container>


보안 리스너 설정과 관련된 요소는 다음과 같다.

  • <enable-secure>

    • SSL(Secure Socket Layer) 통신 지원 여부를 결정한다.

    • true이면 설정된 값에 따라 SSL 통신을 지원하고, false이면 지원하지 않는다. (기본값: false)

  • <client-auth>

    • 클라이언트의 인증 여부를 설정한다.

      설정값설명
      true서버가 클라이언트에게 인증서를 요청하여 클라이언트에 대한 인증을 수행한다.
      false클라이언트에 대한 인증 과정을 수행하지 않는다. 일반적으로 B2B를 제외한 보통의 경우에는 클라이언트 인증을 수행하지 않는다. (기본값: false)
  • <ssl-protocol>

    • 암호화/복호화하는데 사용되는 SSL 프로토콜을 지정한다.

    • Sun JVM을 사용한다면 TLS, IBM JVM을 사용할 때는 SSL이 기본값으로 설정된다.

  • <cipher-suite>

    • SSL handshaking 후에 실제 데이터를 전송할 때 사용하는 암호 suite들을 지정한다.

    • 일반적으로 JDK에서 제공하는 cipher-suite를 사용하며, 디폴트로 제공되지 않는 ipher-suite를 사용할 때 사용한다.

  • <keystore-file>

    • key와 인증서를 저장하고 있는 파일을 지정한다. 절대 경로, 상대 경로 모두 허용된다. 만일 상대 경로가 사용된다면 JEUS_HOME\config\<node-name> 아래에서 해당 파일을 찾는다.

    • JVM 인자로 jeus.ssl.keystore과 javax.net.ssl.keyStore을 이용해서 설정할 수 있다.

    • 우선순위는 다음과 같다.

      1. webmain.xml의 <keystore-file> 설정

      2. -Djeus.ssl.keystore 옵션

      3. -Djavax.net.ssl.keyStore 옵션

    • 기본값은 JEUS_HOME\config\<node-name>\keystore 이다.

  • <keystore-pass>

    • keystore 파일을 열기 위한 패스워드이다.

    • JVM 인자로 jeus.ssl.keypass과 javax.net.ssl.keyStorePassword를 이용해서 설정할 수 있다.

    • 우선순위는 다음과 같다.

      1. webmain.xml의 <keystore-pass> 설정

      2. -Djeus.ssl.keypass 옵션

      3. -Djavax.net.ssl.keyStorePassword 옵션

    • 기본값은 jeuskeypass이다. 암호화해서 설정할 수 있다(ex. {AES}i0syYrTxIq+N4RTw3=).

  • <keystore-keypassword>

    • keystore 파일의 key들을 사용하기 위한 패스워드이다. 일반적으로 keystore-password와 똑같게 설정해서 사용하기 때문에 아무런 설정이 없을 경우 keystore password와 동일하게 설정된다.

    • keystore password와 keystore keypassword의 값이 다를 경우 항상 이 값을 설정해야 한다. 암호화해서 설정할 수 있다(ex. {AES}i0syYrTxIq+N4RTw3=).

  • <keystore-type>

    • keystore의 타입을 지정한다. Sun의 keytool에 의해서 keystore를 생성한다면 JKS(Java's Key Store)를, OpenSSL이나 Microsoft KeyManager로 keystore를 생성한다면 PKCS12를 사용해야 된다.

    • JVM 인자로 javax.net.ssl.keyStoreType을 이용해서 설정할 수 있다.

    • 우선순위는 다음과 같다.

      1. webmain.xml의 <keystore-type> 설정

      2. -Djavax.net.ssl.keyStoreType 옵션

    • 기본값은 JKS이다.

  • <key-management-algorithm>

    • keystore에 저장할 key에 대한 관리 알고리즘을 설정한다.

    • JVM인자로 ssl.KeyManagerFactory.algorithm을 이용해서 설정할 수 있다.

    • 우선순위는 다음과 같다.

      1. webmain.xml의 key-management-algorithm 설정

      2. -Dssl.KeyManagerFactory.algorithm 옵션

    • 기본값은 Sun JVM을 사용할 경우 SunX509를, IBM JVM을 사용할 경우에는 IbmX509로 설정된다.

  • <truststore-file>

    • 클라이언트 인증서를 저장하고 있는 파일을 지정한다. 절대 경로, 상대 경로 모두 허용된다. 만일 상대 경로가 사용된다면 JEUS_HOME\config\<node-name> 아래에서 해당 파일을 찾는다.

    • JVM 인자로 jeus.ssl.truststore과 javax.net.ssl.trustStore을 이용해서 설정할 수 있다.

    • 우선순위는 다음과 같다.

      1. webmain.xml의 <truststore-file> 설정

      2. -Djeus.ssl.truststore옵션

      3. -Djavax.net.ssl.trustStore옵션

    • 기본값은 JEUS_HOME\config\<node-name>\truststore 이다.

  • <truststore-pass>

    • truststore 파일을 열기 위한 패스워드이다.

    • JVM 인자로 jeus.ssl.trustpass과 javax.net.ssl.trustStorePassword를 이용해서 설정할 수 있다.

    • 우선순위는 다음과 같다.

      1. webmain.xml의 <truststore-pass> 설정

      2. -Djeus.ssl.trustpass 옵션

      3. -Djavax.net.ssl.trustStorePassword 옵션

    • 기본값은 jeustrustpass 이다. 암호화해서 설정할 수 있다. (예: {AES}i0syYrTxIq+N4RTw3=)

  • <truststore-type>

    • truststore의 타입을 지정한다. Sun의 keytool에 의해서 keystore를 생성한다면 JKS(Java's Key Store)를, OpenSSL이나 Microsoft KeyManager로 keystore를 생성한다면 PKCS12를 사용해야 한다.

    • JVM 인자로 javax.net.ssl.trustStoreType을 이용해서 설정할 수 있다.

    • 우선순위는 다음과 같다.

      1. webmain.xml의 <truststore-type> 설정

      2. -Djavax.net.ssl.trustStoreType 옵션

    • 기본값은 JKS 이다.

  • <trust-management-algorithm>

    • truststore에 저장할 trust에 대한 관리 알고리즘을 설정한다.

    • JVM 인자로 ssl.TrustManagerFactory.algorithm을 이용해서 설정할 수 있다.

    • 우선순위는 다음과 같다.

      1. webmain.xml의 <trust-management-algorithm> 설정

      2. -Dssl.TrustManagerFactory.algorithm 옵션

    • 기본값은 Sun JVM을 사용할 경우 SunX509를, IBM JVM을 사용할 경우에는 IbmX509로 설정된다.

추가된 부분은 <scheme>, <ssl-config> 태그이다. 이 태그에 대한 자세한 설명은 “4.3. 리스너 설정”을 참고한다. 특히, 리스너의 포트를 "443"으로 설정하는 것을 주목한다. 이 포트 번호는 웹 브라우저에서 기본적으로 사용하는 HTTPS/SSL 포트 번호이다. <scheme> 부분을 “https”로 설정하는 것도 주목한다.

참고

“443” 포트의 사용이 허락되지 않을 수도 있다. 이런 경우에는 “3456”과 같은 다른 포트를 선택한다. Windows에서는 “netstat –an”으로 현재 사용 중인 포트의 목록을 볼 수 있다.

4.6.4. SSL Keystore 설정

SSL 프로토콜은 최소한 클라이언트가 서버를 인증해야만 한다. 이는 서버 측의 보안 리스너는 클라이언트에게 X.509 인증서를 제공할 수 있어야 한다는 것을 의미한다.

JSSE는 J2SDK 1.4에서 제공하는 “JKS” keystore를 사용하여 서버 인증서를 저장한다. 더불어, 보안 리스너는 “JEUS_HOME\config\<node-name>\sslkeystore”로 저장된 JKS keystore를 기본으로 사용한다.

그러므로 sslkeystore 파일을 자체적으로 생성할 필요가 있다. J2SDK 1.4에서 제공하는 keytool을 사용하여 다음과 같이 생성한다(굵은 글씨는 사용자가 입력해야 할 내용들이다).

C:\>keytool -genkey -alias jeusssl -keyalg RSA -validity 7 -keyst
ore d:\jeus\config\johan\keystore
Enter keystore password:  jeus123
What is your first and last name?
  [Unknown]:  localhost
What is the name of your organizational unit?
  [Unknown]:  RND
What is the name of your organization?
  [Unknown]:  TmaxSoft
What is the name of your City or Locality?
  [Unknown]:  Seoul
What is the name of your State or Province?
  [Unknown]:  seoul
What is the two-letter country code for this unit?
  [Unknown]:  KR
Is <CN=localhost, OU=RND, O=TmaxSoft, L=Seoul, ST=Kyoungi, C=KR> cor
rect?
  [no]:  yes

Enter key password for <jeusssl>
        (RETURN if same as keystore password):

C:\>

위의 과정은 JEUS_HOME\config\johan\keystore에 keystore를 생성한다(여기서 '<node-name>'은 'johan'이고 로컬 머신의 이름과 동일하다).

개인 키와 인증서(자가 사인한 공개 키와 다른 정보들)는 생성되어 이 파일에 저장된다. 이 인증서는 보안 리스너(실제로는 JSSE를 구현한 프로그램)에 의해 사용되어 클라이언트에게 자신을 인증하게 된다.

참고

“first and last name”에 “localhost”를 포함한 이유는 인증서는 개인보다는 머신(Server)을 대상으로 발급되기 때문이다. 실제 환경에서는 IP 주소나 DNS 이름을 넣는다. keystore의 위치는 바꿀 수 있다. 이에 대한 자세한 내용은 “4.6.5. SSL Truststore 설정”에서 설명한다.

4.6.5. SSL Truststore 설정

SSL 상호(클라이언트) 인증이 발생할 때에는 클라이언트가 서버에게 자신의 인증서를 보내줘야 한다. 이 인증서는 서버의 신뢰된 인증서 목록에 등록되어 신뢰가 이루어진다. 이 인증서들은 “truststore”라는 곳에 저장된다.

JEUS에서 보안 리스너에 의해 사용되는 truststore는 JEUS_HOME\config\<node-name>\truststore에 위치한다.

일반적으로 이 파일은 Verisign과 같은 공인 인증기관(CA)에서 발급된 인증서들을 포함한다. J2SDK 1.4와 함께 제공되는 JRE_HOME\lib\security\cacerts은 널리 알려진 CA들로부터 발급된 신뢰된 인증서들의 목록이다.

실제 개발 환경에서는 이 파일을 JEUS_HOME\config\<node-name>\truststore에 복사한다. 그 다음에 클라이언트를 위한 인증서들을 생성, 서명하여 이 truststore에 포함시킨다. 이렇게 생성된 상호 인증서를 클라이언트들에게 배포하여 JEUS SSL 리스너와 함께 인증 과정을 거치도록 한다.

여기의 간단한 예에서는 truststore를 처음부터 생성하여 자체적으로 서명한 서버 인증서(keystore로부터)에 포함시킨다. 이것은 직접 생성한 인증서를 신뢰하겠다는 의미이다. 그러기 위해서는 다음의 명령어들을 실행시켜야 한다.

  1. 먼저, keystore로부터 자제적으로 서명한 인증서를 'jeusssl.cer'라는 파일에 export한다.

    C:\>keytool -export -alias jeusssl -keystore d:\jeus\config\johan
    \keystore -rfc -file jeusssl.cer
    Enter keystore password:  jeus123
    Certificate stored in file <jeusssl.cer>
  2. 그 인증서를 JEUS_HOME\config\<node-name>\truststore의 JEUS SSL truststore로 import한다.

    C:\>keytool -import -alias jeussslcert -file c:\jeusssl.cer -keys
    tore d:\jeus\config\johan\truststore
    Enter keystore password:  jeus123
    Owner: CN=localhost, OU=RND, O=TmaxSoft, L=Seoul, ST=Kyoungi, C=KR
    Issuer: CN=localhost, OU=RND, O=TmaxSoft, L=Seoul, ST=Kyoungi, C=KR
    Serial number: 3e447270
    Valid from: Sat Feb 08 11:58:56 KST 2004 until: Sat Feb 15 11:58:56 KST 2004
    Certificate fingerprints:
             MD5:  B4:53:FE:B6:00:EB:FB:0F:04:7F:D2:F6:FA:9A:A0:3B
             SHA1: DE:C8:26:5F:D0:06:9B:3C:F8:E2:7E:3A:26:B7:78:83:93:2D:5E:1C
    Trust this certificate? [no]:  yes
    Certificate was added to keystore
    
    C:\>
  3. 위의 과정이 완료되면 새로운 JEUS_HOME\config\<node-name>\truststore 파일이 생성된다. 이것은 1개의 신뢰된 인증서를 가지고 있고 클라이언트 인증서가 상호 인증 과정에서 신뢰될 수 있을 것인지를 결정할 때 사용된다.

위와 같이 설정하면 실제 상황에서는 직접 생성한 인증서를 제출하는 클라이언트는 자동적으로 신뢰되지 않을 것이다.

4.6.6. 보안 리스너 속성 설정

보안 리스너를 시작하기 전에 SSL keystore와 truststore 파일의 위치, 그리고 이 파일에 접근할 때 사용하는 암호들에 대한 정보를 제공해야 한다.

다음은 –D 옵션을 사용하여 설정할 수 있는 JEUS 특정 속성들에 대한 설명이다.

  • jeus.ssl.keystore

    • SSL keystore를 포함하고 있는 파일의 경로이다.

    • 불필요. 설정되지 않으면 기본으로 JEUS_HOME\config\<node-name>\keystore가 설정된다.

  • jeus.ssl.keypass

    • SSL keystore의 암호이다.

    • 설정되지 않으면 기본으로 “jeuskeypass”가 설정된다.

  • jeus.ssl.truststore

    • SSL truststore를 포함하고 있는 파일의 경로이다.

    • 불필요. 설정되지 않으면 기본으로 JEUS_HOME\config\<node-name>\truststore가 설정된다.

  • jeus.ssl.trustpass

    • SSL truststore의 암호이다.

    • 설정되지 않으면 기본으로 “jeustrustpass”가 설정된다.

위의 속성들은 JEUSMain.xml의 <engine-container><command-option> 태그에 지정된다.

[예 4.29] <<JEUS_HOME\config\<node-name>\JEUSMain.xml>>

<?xml version="1.0"?>
<jeus-system xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
    <node>
        <name>[node name]</name>
        . . .
        <engine-container>
            <name>mycontainer</name>
            <command-option>
                -Djeus.ssl.keypass=jeus123 
                -Djeus.ssl.trustpass=jeus123
            </command-option>
            <engine-command>
                <type>servlet</type>
                <name>engine1</name>
            </engine-command>
            <!-- Other engine start commands go here -->
            . . .


<command-option> 값은 한 줄에 설정 하는 것에 주의하고, JEUSMain.xml의 편집이 끝나면 저장한다.

4.6.7. 보안 리스너 시작하기

위의 모든 과정을 거친 후에 JEUS 노드를 재시작한다(down을 하고 boot을 한다). 이렇게 하면 보안 리스너가 시작된다. JEUS 노드의 재시작 과정은 "JEUS Server 안내서"를 참고한다.

4.6.8. 보안 리스너에 연결하기

다음은 보안 리스너에 연결하는 과정에 대한 설명이다.

  1. 로컬 호스트의 웹 브라우저를 열고 다음의 URL을 요청한다.

    https://localhost/Test/hello.html

    만약에 443 외의 다른 포트를 설정했다면 https://localhost:<Port>/Test/hello.html와 같이 요청해야 한다. 또한 “https”의 “s”를 유의한다.

  2. 브라우저에서는 다음 그림과 같이 Security Alert 대화창이 나타난다. 이 대화창은 SSL 연결은 시도되었으나 보안 리스너에서 제공되는 인증서가 신뢰되지 않았다는 것을 알려준다. 브라우저가 그 자체의 truststore에 우리가 자체적으로 서명한 인증서를 포함하고 있지 않기 때문이다.

    [Yes] 버튼을 클릭하여 진행하고 연결과 인증서를 수용한다

    [그림 4.8] Internet Explorer에서의 인증서 보안 경고창

    Internet Explorer에서의 인증서 보안 경고창

  3. 보안SSL 연결을 사용하는 보안 리스너를 통하여 'hello.html'을 받을 수 있다.

    다음 그림의 작은 자물쇠 모양은 SSL 보안 연결이 수행되고 있다는 것을 의미한다.

    [그림 4.9] 보안된 SSL 연결

    보안된 SSL 연결

주의

[그림 4.9]에서는 '1567' 포트를 이용하여 보안 리스너에 접근하고 있다. 일반적으로 '443' 포트가 사용됨을 유의한다.

4.7. 리스너 튜닝

최적의 성능을 위하여 리스너를 설정할 때, 다음의 사항을 고려한다.

  • 시스템 자원을 많이 사용하거나 대기시간을 길게하여 출력 버퍼의 크기를 증가시킨다.

  • 일반적으로 Worker Thread Pool의 min, max, step 값을 크게 부여하면 웹 컨테이너에 많은 클라이언트가 접근할 때 Pool이 좋은 성능을 가지게 된다. 시스템 메모리를 적게 사용하기 위해서는 이들의 값을 낮게 설정한다.

  • Server Access Control 옵션을 비활성화 하면 성능 개선을 기대할 수 있다.

  • WebtoB 리스너에서는 앞에서 언급했던 공식에 따라 WEBMain.xml과 http.m의 값들을 동일하게 설정한다. 이렇게 해야 가장 좋은 성능을 기대할 수 있다.

  • WebtoB와 웹 컨테이너 사이의 통신은 항상 Pipe 통신을 이용하는 것을 권장한다.

    WebtoB 서버와 웹 컨테이너가 동일 머신에 존재하면 이와 관련된 옵션을 기본값인 “false”로 설정한다. 다른 머신에 존재하면 Pipe 통신을 할 수 없으므로, <disable-pipe> 태그를 “true”로 설정한다. 웹 컨테이너에 의해 필요한 설정이 자동적으로 설정된다. 그러므로 설정이 반드시 필요하지는 않다.