Apache Proxy

open study 2010. 12. 3. 15:46
http://www.apacheweek.com/features/reverseproxies  참조 번역


Web Proxies



Proxy server는 크게 웹에 대한 사용자의 gateway이다. 사용자가 브라우저 세팅에서 proxy를 설정하면 모든 HTTP request는 proxy를 통해 route된다.

일반적으로 proxy는 다음과 같은 역할에 사용된다.



  *  caching된 페이지를 얻음으로써, 빠른 웹 access.

  *  firewall 뒤의 사용자에게 특정 웹 접근을 가능하게 control

  * web content 의 filter 나 transform





Apache의 mod_proxy는 Forward Proxy와 Reverse Proxy를 지원하는데, Reverse Proxy는caching을 통한 웹성능 향상외에  웹으로부터 방화벽뒤에있는 서버로의 controlled access를 가능하게 하기 위해대부분 사용된다.



- Forward Proxy : 서비스 서버의 앞에 서서 요청을 얻어옴. 방화벽을 뚫기위해 많이 설정함.

- Reverse Proxy : 역시 서비스 서버의 앞에 서나 대행을 위해 사용됨.





Proxing with Apache



Standard Apache module인 mod_proxy는 HTTP/1.0 과 HTTP/1.1 두 protocolversion을 지원하는데, HTTP/1.1이 rich new cache control mechanisims을 가진다는 점에서큰 차이가 있다.





The Apache Proxy Modules



Apache modular arcitecture를 유지하기 위해서는, mod_proxy는 modular 자체이고, 전형적인proxy server는 가능한 여러 모듈을 필요로 할 것이다. 이장에서는 proxing과 관련한 이런 모듈에 대한 설명을 주로할 것이고,  caching 관련해서는 다루지 않는다.



mod_proxy: The core module deals with proxy infrastructure and configuration and managing a proxy request.
mod_proxy_http: This handles fetching documents with HTTP and HTTPS.
mod_proxy_ftp: This handles fetching documents with FTP.
mod_proxy_connect: This handles the CONNECT method for secure (SSL) tunneling.
mod_cache, mod_disk_cache, mod_mem_cache: These deal with managing adocument cache. To enable caching requires mod_cache and one or both ofdisk_cache and mem_cache.
mod_proxy_html: This rewrites HTML links into a proxy's address space.
mod_headers: This modifies HTTP request and response headers.
mod_deflate: Negotiates compression with clients and backends.




Building Apache for Proxing ( Apache 2.0 기준)



mod_proxy_html을 제외하고, 위 모든 모듈이 core Apache distribution에 들어가있으므로, 쉽게 사용가능하다. (mod_proxy_html 설치는 위 사이트 참조하십시여 ㅡㅡ;)



Apache의 configure에서

# ./configure --prefix=/env/apache2 --enable-proxy=shared
# make
# make install


간단히

./configure --enable-proxy --enable-proxy-http



기존 installation에 추가하기 위해서는 apxs를 써야한다.

# apxs -c -i [module-name].c
noting that mod_proxy itself is in two source files
(mod_proxy.c and proxy_util.c).






A Reverse Proxy Scenario



example.com이라는 회사가 internet을 통해 접근가능한 public IP address와 DNS entry를 가진 www.example.com이라는 website를 가지고 있다고 하자.

이 회사는 또한 방화벽 안에 private IP address와 등록되지 않은 DNS entry를 가진 여러application server를 가지고 있다. 이런 해당 network안의 applicationserver로internal1.example.com"과 "internal2.example.com" 이 있다고 하자. 이 서버들은public DNS entry를 가지지 않으므로, 회사외부로 부터 internal1.example.com으로 접근이 안되고"no such host" 에러가 날 것이다.



그런데, 이 application server로 web access를 해야된다고 결론이 난 것이다.  당연히 internet을 통해 직접적으로 노출하지는 말아야되고 해당 웹서버로 통합되긴 해야하므로,  내부적으로 http://www.example.com/app1/any-path-herehttp://internal1.example.com/any-path-here로 매핑하고 http;//www.example.com/app2/other-path-here를 http://internal2.example.com/other-path-here로 매핑하는 것이다.



Configuring the Proxy



이 모듈을 사용하기 위해서 우선 httpd.conf에 이것들을 loading 하라고 위 사이트에는 적혀있으나,실제로  mod_proxy는 아파치와 static으로 link되기 때문에 LoadModule을 하지 않아도 되므로 여기선생략하겠다.


proxy를 구성하기 전에, 우선 아래 중요한 securiy warning을 확인하자.

"ProxyRequests On" 으로 setting하지 말아라.

ProxyRequests 를 On으로 setting하면, 당신의 서버가 Open Proxy로 변해버린다.

web을 open proxy로 scan해버리는 인간들이 있어서, 이 서버를 불법적인 곳에 access하기위해 route하려고사용하거나  심하게는 이 proxy를 통해 email spam을 route할 수도 있다.  얼마안가서  정상적인 traffic은막혀버리고  family filter 같은 것에 의해 block당해버린 서버들을 발견하게 될 것이다.

물론 적절한 security measure로 forward proxy를 사용할 수 있으나, 여기선 다루지 않겠다.



reverse proxy를 설치하는 기본적인 configuration directive는 ProxyPass 이다.

해당 application server의 proxy rules를 set up하기 위해 아래와 같이 사용하면 된다.



ProxyPass /app1/ http://internal1.example.com/
ProxyPass /app2/ http://internal2.example.com/


그리고 apache를 재시작하면  http://www.example.com/app1/some-path-mapshttp://internal1.example.com/some-path 로 매핑되게 된다.
Posted by 복태아빠
,
잘되어 있는 사이트를 포스팅 해왔다.

Tomcat Web application 만들기 

먼저, 웹 루트를 어디로 정할 것인가?
(아파치나 톰캣 자체가 static html을 바라보는 웹 웹루트)

 

톰켓이 기본적으로 보고 있는 루트 컨텍스트는 webapps/ROOT 이다.

server.xml 내용을 보면

  1. 96:  <Host name="localhost"  appBase="webapps"
                unpackWARs="true" autoDeploy="true"
                xmlValidation="false" xmlNamespaceAware="false">

와 같이 appBase는 ${catalina-home} 밑의 상대경로를 인자로 받으며,기본적으로 보안과 context의 통일적인 적용을 이유로 컨텍스트의 루트는 ROOT 디렉토리 밑이 됩니다.

따라서, 톰켓이 설치가 되면 웹루트는 ${catalina-home}/webapps/ROOT 가 된다.

(위의 과정대로 설치했다면 우리 시스템에서는 /usr/local/tomcat/webapps/ROOT 가 된다)

 이를 3가지 형태로 변경해 사용할 수 있다.

1. webapp 자체를 웹루트 디렉토리로 만들고 싶을 때,

단지, <Host> 태그 내에 아래와 같은 컨텍스트를 추가하면 됩니다.

    <Context path="" docBase="." reloadable="true"/>

   사실 컨텍스트는 더 많은 옵션이 있으나 여기는 최소한의 사항만 적었습니다. 실서버에 적용할 때는 log 부분도 신경을 써주셔야 합니다. 더 자세한 사항은 웹을 검색해 보시기 바랍니다.

2. webapp/test/ROOT를 웹루트 디렉토리로 만들고 싶을 때,

  1. 96:  <Host name="localhost"  appBase="webapps/test"
                unpackWARs="true" autoDeploy="true"
                xmlValidation="false" xmlNamespaceAware="false">

3. /home/dev/appName 을 웹루트 디렉토리로 만들고 싶을 때

  1. 96:  <Host name="localhost"  appBase="/home/dev/appName" unpackWARs="true" autoDeploy="true"
                xmlValidation="false" xmlNamespaceAware="false">
  2.     <Context path="" docBase="." reloadable="true"/>
        </Host>

     또는,

  1. 96:  <Host name="localhost"  appBase="/home/dev"
                unpackWARs="true" autoDeploy="true"
                xmlValidation="false" xmlNamespaceAware="false">

        <Context path="" docBase="mywebappdir" reloadable="true"/>
        </Host>

   Context 를 빼먹으면 ROOT를 자동으로 웹루트로 쓴다.

3번의 설명은 Host의  appBase 경로를 다른 곳으로 돌린 경우에 해당한다.

이러한 경우 다음 항목인 " 별도의 디렉토리를 appBase로 잡을 경우 설정" 내용을 참고하여 진행해야 한다.

우리 시스템(2008년8월 개발서버)은 3번과 같이 별도의 경로를 web, app Base디렉토리로 잡고 진행한다.

 

 

p.s.> 톰켓 5.0부터 추가적인 context는 server.xml에 추가하지 않고,
각 웹어플리케이션 디렉토리 별로 META-INF 밑에 context.xml을 추가하게 됩니다.

웹루트를 appBase와 같이 하려면 이의 설정을 server.xml에서 설정해도 무방한 것 같습니다만,
webapps를 루트로 쓰는 것은 특별한 경우가 아니면 지양하는 게 좋으며,
꼭 필요하다면 apache의 redirect를 쓰는 방법 등으로 해결할 수도 있습니다.

 

별도의 디렉토리를 웹 컨텍스트 루트로 잡을 경우 설정과정

  • 우리 시스템은 다른 하드디스크에 별도로 디렉토리를 생성하였다.

    이는 /media/disk 로 마운트되어 있다.

  • /media/disk/webapps 디렉토리를 생성해 권한을 사용가능한 일반유저 권한으로 설정한다(mkdir, chown 등 사용)
  • 위 디렉토리에 컨텍스트가 될 "test" 디렉토리를 만들고 시험적으로 되는지 확인해 볼 만한 index.jsp 파일 하나를 아래와 같이 대충 만들어 넣는다.
  1. <html>
    <head><title>TEST-INDEX</title>
    <body>
    <h1>INDEX</h1>
    </body>
    </html>

 

톰캣 관리자 화면에서 새로운 어플리케이션이 보여지게 하려면  TOMCAT_HOME /work/Catalina/호스트명(localhost)/{컨텍스트명}test.xml 파일을 아래와 같이 만들어 넣으면

  1. <?xml version='1.0' encoding='utf-8'?>
    <Context path="/test"
             docBase="/media/disk/webapps/test"
             debug="0"
             reloadable="true"
             crossContext="true"
    />

톰캣 관리자 화면에서 확인이 될 수 있다.

즉, 호스트 밑에 컨텍스트명과 동일한 컨텍스트 xml 파일을 넣으면 된다는 것이지만, 런타임에만 적용되므로 환경설정에 완전히 적용하기 위해선 컨텍스트 설정에 관한 자세한 설정은 Tomcat 6 컨텍스트 설정방법 을 참고한다.

TOMCAT_HOME : 톰캣 설치 디렉터리

 

1. 도메인으로 분류하는 방법

 

TOMCAT_HOME\conf\server.xml 을 열면 기본적으로 하나의 Service 엘리먼트가 있고
그 하위에 Engine 엘리먼트가, 또 그 하위에 아래와 같은 하나의 Host 엘리먼트가 있다.

 

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true"
            xmlValidation="false" xmlNamespaceAware="false">
      </Host>

 

아래와 같이 추가하려는 도메인으로 Host 엘리먼트를 하나 더 추가한다.

 

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true"
            xmlValidation="false" xmlNamespaceAware="false">
      </Host>
      <Host name="test.sisinfo.com
appBase="/media/disk/webapps/test"
            unpackWARs="true" autoDeploy="true"
            xmlValidation="false" xmlNamespaceAware="false">
      </Host>

 

appBase는 이 컨텍스트의 물리적 영역이 되겠다.
물론 실제의 물리적인 디렉터리도 아래와 같이 필요하다.

 

/media/disk/webapps/test/ROOT
/media/disk/webapps/test/ROOT/WEB-INF

 

WEB-INF의 web.xml등은 TOMCAT_HOME/webapps/ROOT/WEB-INF에서 복사한다.

/media/disk/webapps/test/ROOT 디렉터리는 이 컨텍스트의 루트 디렉터리로 작동한다.

 

여기서 test.sisinfo.com은 추가하려는 도메인이 되겠으며,
실제로 도메인을 보유하지 않고 개발 PC에서 작업하는 경우,
C:\Windows\system32\drivers\etc\hosts 파일을 열고
마지막 줄에 다음을 추가한다.

 

192.168.10.105      test.sisinfo.com

 

이제 웹브라우저를 열고 [test.sisinfo.com:포트번호]에 접속하면 된다.

 

만일 /media/disk/webapps/test/ROOT 가 아닌 /media/disk/webapps/test 를 룰트 디렉터리로 사용하고자 하는 경우에는
server.xml을 다음과 같이 작성한다.

      <Hostname="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true"
            xmlValidation="false" xmlNamespaceAware="false">
      </Host>
      <Host name="test.sisinfo.com
"  appBase="/media/disk/webapps/test"
            unpackWARs="true" autoDeploy="true"
            xmlValidation="false" xmlNamespaceAware="false">
    <context path="" docBase="/media/disk/webapps/test"></context>
      </Host>

 

2. 포트 번호로 분류하는 방법

 

톰캣에서도 MS의 IIS와 같이 포트 번호에 따라 호스팅하는 것이 가능하다.
TOMCAT_HOME\conf\server.xml 을 열고 아래와 같이 Service 엘리먼트를 추가한다.

 

  <Service name="testdomain">
    <Connector port="8090" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
    <Engine name="testdomain" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
             resourceName="UserDatabase"/>
      <Host name="localhost"  appBase="C:\testdomain"
            unpackWARs="true" autoDeploy="true"
            xmlValidation="false" xmlNamespaceAware="false">
            <context path="" docBase="C:\testdomain"></context>
      </Host>
    </Engine>
  </Service>

 

Service 엘리먼트의 name 속성은 추가하려는 컨텍스트의 이름이 되겠다.
첫 Connector 엘리먼트의 port 속성은 사용하려는 포트 번호를 넣으면 된다.
Host 엘리먼트와 context 엘리먼트, appBase에 대한 물리적 경로 생성은 1항의 설명과 같다.

 

이제 웹브라우저를 열고 [localhost:포트번호]에 접속하면 된다.

 

 

 

 

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

아파치와 연동없이 톰캣만으로도 하나의 IP로 다수의 웹사이트를 운영하는 것이 가능하다.
(아파치와 톰캣을 연동하는 방법에 대해서는 차후에 자세히 올리도록 하겠다)

특히 개발자의 경우 여러개의 프로젝트를 개발하거나 테스트하고자 할 때 웹사이트를 여러개 운영해야한다. 다수의 웹 사이트를 세팅하는 방법은 크게 2가지가 있다.

가상호스트를 이용하는 방법은 도메인을 이용하여 실제로 서비스를 운영하는 경우가 아니면 개발자에겐 별 의미가 없다. 여기서는 두번째 방법인 IP 어드레스의 포트를 이용하는 방법에 대해서 설명하겠다. (바로가기 : 톰캣에서 가상 호스트를 이용하는 방법)

우선 설치 환경은 다음과 같다.

  • O/S : Windows XP (난 아직 리눅스를 잘 모른다. 비슷하겠지만 테스트해보지 않았다)
  • Tomcat 6.0 (정확히는 6.0.10) : 다운로드

설명의 편의를 위해 톰캣의 설치 디렉토리는 'TOMCAT_HOME' 으로 표기할 것이다. 참고로 내 경우는 C:\Server\Tomcat6.0 이다.

설정하는 방법은 간단하다. /TOMCAT_HOME/conf/에 있는 server.xml 파일만 수정하면 끝이다. server.xml의 쓸데없는 주석부분을 다 없애고 핵심부분만 남겨놓으면 아래와 같다.


<Service name="Catalina">
  <Connector port="8080" protocol="HTTP/1.1"
   maxThreads="150" connectionTimeout="20000"
   redirectPort="8443" />
  <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

  <Engine name="Catalina" defaultHost="localhost">
   <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
    resourceName="UserDatabase"/>

   <Host name="localhost"  appBase="webapps"
    unpackWARs="true" autoDeploy="true"
    xmlValidation="false" xmlNamespaceAware="false">
   </Host>

  </Engine>
 </Service>



우선 빨간색으로 표시한 부분만 이해하고 넘어가도 상관없다.
Connector port="8080"은 HTTP로 넘어오는 포트를 지정하는 것이다. 톰캣의 기본 포트가 8080인 이유가 여기에 있다. 따라서 8080 대신 기본 80포트를 사용하고 싶다면? 바로 이 부분을 port="80"으로 바꾸어주면 된다.

다음, Host 지시어의 appBase="webapps" 는 웹어플리케이션(웹사이트)의 위치를 나타낸다. appBase="./webapps"와 같은 의미다. 실제 위치는 TOMCAT_HOME/webapps이다. 물론 "d:/weapps/myweb1" 과 같이 절대경로로 지정하는 것도 가능하다.

그럼 웹사이트를 하다 더 추가하고 싶다면? 위의 <Service>...</Service>를 하나 더 만들어 버리면 된다. 위의 코드를 복사한 다음 server.xml 에 추가한다. 그리고 빨간색으로 표시한 부분만 수정하자.

<Service name="Catalina2">
  <Connector port="9090" protocol="HTTP/1.1"
     maxThreads="150" connectionTimeout="20000"
     redirectPort="8443" />
  <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
 
  <Engine name="Catalina" defaultHost="localhost">
   <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
     resourceName="UserDatabase"/>
 
   <Host name="localhost"  appBase="d:/webapps/myweb2"
    unpackWARs="true" autoDeploy="true"
    xmlValidation="false" xmlNamespaceAware="false">
   </Host>
  </Engine>
 </Service>


다른 웹어플리케이션을 돌리기 위해 서비스를 하나 더 추가한 것이다.
port="9090" 은 새로 추가하고 싶은 포트이다.
appBase="d:/webapps/myweb2"는 9090 포트에서 돌아갈 웹사이트 위치이다.

이제 server.xml 설정은 끝난 것이다.
마지막으로 웹사이트의 ROOT 디렉토리를 지정해주자. 아래의 폴더를 생성한다.

d:/webapps/myweb2/ROOT/   (
d:/webapps/myweb2/ROOT/WEB-INF/
(WEB-INF 폴더를 만들고 web.xml 파일을 추가한다. 그냥 /TOMCAT_HOME/webapps/ROOT/WEB-INF/에 있는 web.xml 을 복사하면 된다.

무지 간단하다. 하지만 난 이 간단한 것을 위해서 하루종일 삽질해야만 했다. 검색해 보아도 문서는 많은데 실제 도움이 될만한 것이 별로 없었다.

테스트하기 위해 ROOT/index.html 또는 index.jsp를 만든다.
이제 톰캣을 재시작하고 웹브라우저로 접속해 보자.
http://localhost:8080
http://localhost:9090

 

 

 

 

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

 

톰캣에서 하나의 IP로 여러개의 웹사이트를 운영하는 방법은 2가지가 있다.

  • 가상 호스트(Virtual Host)를 이용하는 방법
  • IP Address의 port를 여러개 사용하는 방법
  • 여기서는 가상 호스트를 이용하여 톰캣에서 여러개의 웹사이트를 운영하는 방법에 대해서 설명하고자 한다.
    설치 환경은 다음과 같다.

    • O/S : Windows XP (Windows Server 동일)
    • Tomcat 6.0.10

    설명의 편의를 위해 톰캣의 설치 디렉토리는 'TOMCAT_HOME' 으로 표기할 것이다. 참고로 내 경우는 C:\Server\Tomcat6.0 이다.

    설정하는 방법은 /TOMCAT_HOME/conf/에 있는 server.xml 파일만 수정하면 된다. server.xml의 쓸데없는 주석부분을 다 없애고 관련 부분만 남겨놓으면 아래와 같다.   

    <Service name="Catalina">
          <Connector port="8080" protocol="HTTP/1.1"
           maxThreads="150" connectionTimeout="20000"
           redirectPort="8443" />
          <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

          <Engine name="Catalina" defaultHost="localhost">
           <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
            resourceName="UserDatabase"/>

           <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true"
            xmlValidation="false" xmlNamespaceAware="false">
           </Host>

          </Engine>
         </Service>

    위의 내용에서 핵심 부분은 <Host></Host> 영역이다.

    1. 우선 <Connector port="8080" protocol="HTTP/1.1" 부분의 port를 80으로 수정한다.
      도메인명이 기본으로 80포트를 사용하기 때문이다.
     
    2. <Host>... </Host> 에 해당하는 부분을 복사하여 2개를 만든다. 그리고 이렇게 수정하자.
          

    <Host name="www.myweb1.com"  appBase="d:/webapps/myweb1"
            unpackWARs="true" autoDeploy="true"
            xmlValidation="false" xmlNamespaceAware="false">
           </Host>

           <Host name="www.myweb2.com"  appBase="d:/webapps/myweb2"
            unpackWARs="true" autoDeploy="true"
            xmlValidation="false" xmlNamespaceAware="false">
           </Host>

      appBase="webapps" 는 톰캣의 기본 웹루트인 TOMCAT_HOME/webapps 디렉토리를 가르킨다. 웹사이트를 원하는 디렉토리에 두고 싶다면 d:/webapps/myweb1 처럼 자기가 지정하고 싶은 곳으로 수정하면 된다.
     
    3. 마지막으로 웹사이트들의 ROOT 디렉토리를 지정해주자. 아래의 폴더를 생성한다.

    d:/webapps/myweb1/ROOT/
    d:/webapps/myweb1/ROOT/WEB-INF/

    d:/webapps/myweb2/ROOT/
    d:/webapps/myweb2/ROOT/WEB-INF/

    그리고 WEB-INF 폴더 밑에 각각 web.xml 파일을 추가한다. 그냥 /TOMCAT_HOME/webapps/ROOT/WEB-INF/에 있는 web.xml 을 복사하면 된다.

    4. 테스트하기 위해 ROOT/index.html 또는 index.jsp를 만든다.

    이제 톰캣을 재시작하고 웹브라우저로 접속해 보자.
    http://www.myweb1.com
    http://www.myweb2.com

    정말 간단하지 않은가?

    Posted by 복태아빠
    ,

    2009/04/07 16:59

    복사 http://blog.naver.com/vijaru777/130045619812

    프로젝트를 하다보면 데이터를 엑셀로 출력해 달라는 요구가 빈번히 발생한다.
    간단하게 CSV 파일로 출력할 수도 있으며 Excel이 HTML 형태의 문서양식도 지원하기에
    HTML로 작성하고 mine type만 살짝 바꿔주어 엑셀로 읽을 수 있도록 하는 방법이 있다.

    물론 CSV로 요구사항을 충분히 만족시킬 수 있을 경우도 있으나 고품질의 엑셀 양식을 요구할 경우 위 두 방법은 웬지 부족함이 있다. 이럴 경우 엑셀 포맷으로 출력할 필요가 있는데 여러가지 솔루션이 있어 어렵지 않게
    해결할 수 있는 부분이지만 매번 어떻게 처리할 지 고민을 한다. 여러가지 방법을 사용해 본 후 가장 나은 방법을 추천해 본다.

    솔루션 찾기

    Java로 엑셀을 다루기 위한 방법은 여러가지 있으며 대표적인 것 3가지만 살펴본다.
    Java Excel API은 개발자가 엑셀 스프레드시트를 동적으로 읽고, 쓰고 수정할 수 있도록 하는 성숙한 오픈소스 java API이다. Java 개발자는 간단한 API를 이용하여 엑셀 스프레드시트를 읽고, 수정하고 쓸 수 있다.

    장점은 이 패키지는 다른 패키지를 필요로 하지 않고 현재버전(2.6.9.1)의 jar 파일의 크기가 709KB로 부담없이 쓸 수 있다는 것이다. 비교적 간단한 AP를 제공하나 엑셀의 차트나 그래프 매크로 정보를 생성할 수는 없으며 시트에 PNG 이미지만 추가할 수 있다.

    POI는 Microsoft의 OLE 2 컴포넌트 문서 포맷을 다루기 위한 프로젝트이다. 따라서 POI에는 엑셀 뿐만 아니라 워드문서를 다루는 API 도 제공하고 몇가지 모듈로 나누어져 있다.
    이중 HSSF 가 엑셀 파일 포맷을 다루기 위한 자바구현체이다. 오래되었고 다른 것 보다 큰 이상을 가지고 출발한 프로젝트라 다양한 API를 제공한다.

    apache 재단에서 진행되는 프로젝트이며 POI 패키지는 여러개의 다른 패키지(commons, log4j 같은..)를 필요로 한다. 풍부한 API를 제공하는 대신에 사용하기 번거로운 점이 있으며 많은 패키지를 필요로 한다는 부담이 있다.

    jXLS은 엑셀파일 포맷의 템플릿을 이용하여 엑셀 파일을 손쉽게 생성하기 위한 패키지이다. 또한 XML 설정 파일을 통해 엑셀파일의 데이터를 Java 객체로 읽는 장치도 제공한다.

    사실 jXLS은 Javarta POI 패키지를 기반으로 동작한다. 따라서 jXLS을 사용하기 위해서는 POI가 사용하는 많은 다른 패키지를 필요로 한다.

    반면 jXLS 자체는 매우 작으며 복잡한 보고서 생성이나 일정한 양식의 엑셀 데이터를 규칙에 따라 읽게 한다는 뚜렷한 목적이 있어 범용성은 약간 떨어지더라도 대부분의 엑셀 관련 작업에 훌륭한 솔루션이 될 수 있다.

    우리는 jXLS을 이용하여 위 문제를 풀어본다.

    jXLS 맛보기

    jXLS은 템플릿을 기반으로 최종 엑셀파일을 생성한다.
    JSP나 Velocity 또는 Freemarker 같이 템플릿을 만들고 출력할 데이터를 템플릿을 이용하여 변환하면 템플릿 모양대로 최종결과물이 생성하는 구조다.
    여기서 jXLS은 템플릿으로 엑셀파일을 그대로 쓰며 따라서 템플릿 작성이 매우 쉽다. 또한 엑셀 파일을 그대로 사용하므로 엑셀의 서식과 차트등 엑셀 파일의 대부분의 기능을 그대로 사용할 수 있다.


    간단한 예제로 설명을 한다.
    Java 객체
    출력할 데이터를 만든다. 아래는 Customer 클래스를 예로 사용했지만 Map도 지원한다. 각 속성에 대한 getter, setter는 존재해야 한다.
    public class Customer {
        private Long no;
        private String name;
        private String cellphone;
        private String email;
        public Long getNo() {
            return no;
        }
        ...
    }


    변환
    출력한 데이터를 만든 후 엑셀템플릿과 출력할 데이터 (Java 빈)을 이용하여 변환한다.
            // 출력할 객체를 만든다.
            List<Customer> customers = new ArrayList<Customer>();
            Customer customer = new Customer();
            customer.setNo(1L);
            ...
            customers.add(customer);
            ...
           
            Map<String, Object> beans = new HashMap<String, Object>();
            beans.put("customers", customers);
            XLSTransformer transformer = new XLSTransformer();
            transformer.transformXLS("엑셀템플릿파일이름.xls", beans, "엑셀결과파일이름.xls");


    엑셀템플릿
    엑셀파일로 다음과 같이 작성한다. 중간에 ${..}로 들어갈 곳은 데이터가 치환되는 부분이다. 위의 경우 "엑셀템플릿파일이름.xls" 파일을 아래와 같이 생성한다.

    이제 프로그램을 구동하면 자바객체를 이용하여 엑셀파일을 생성할 것이다.

    태그 사용하기
    좀 더 세밀한 제어를 위해 jXLS은 여러가지 태그를 제공한다. 위의 예제를 태그로 변경하면 다음과 같다.

    jXLS의 자세한 사용법은 jXLS 홈페이지를 참조한다.


    웹환경 실전에서

    우리는 경우에 따라 고객목록을 HTML로 출력하거나 엑셀파일로 다운로드할 것이다. 즉 동일한 데이터가 경우에 따라 표현하는 방법 만 달리하는 경우에 해당한다.
    이런 요구사항은 흔히 발생하고 이런 경우 MVC 모델을 응용한 아키텍처를 많이 사용한다.
    MVC(Model View Controller) 모델은 많이 들어보았을 것이다. 여기서 우리는 Model과 View를 분리하는 작업을 할 것이다. 위의 경우 Model은 Customer 클래스에 해당하고 View는 엑셀파일에 해당할 것이다.



    Struts2 Result 구현
    위 구현을 위해 우리는 Struts2를 사용할 것이다. Struts2는 가장 많이 사용하는 프레임워크로 Struts 1에 비해 구조가 많이 개선되었다. 위의 목적을 달성하기 위해 우리는 데이터를 엑셀로 만드는 Result Type만 구현하여 Struts2 프레임워크에 붙여(플러그인)주기만 하면 된다.


    Excel Result Type 만들기
    Struts2에 새로운 Result Type을 만들기 위해서는 com.opensymphony.xwork2.Result를 구현하면 된다.

    엑셀로 변환하기 위해 필요한 정보는 엑셀 템플릿, 엑셀에 출력할 객체들 그리고 다운로드받을 파일이름 정도일 것이다. 이것들을 이용하여 변환을 한다.

    JXLSResult.java
    아래는 본인이 사용하는 Struts2 엑셀 result tyup 클래스이다.
    (아래 소스의 저작권은 넥스트리소프트에 있습니다. 참조하여 사용하는 것은 문제가 없으나 저작권은 반드시 명시하여 주시기 바랍니다.)

    public class JXLSResult implements Result {
        /** 엑셀 템플릿 */
        private String template;
        /** 엑셀에 출력할 객체들 */
        private String beans;
        /** 파일이름을 얻어올 키값 */
        private String filenameKey = "filename";

        public void execute(ActionInvocation invocation) throws Exception {
            ActionContext actionContext = invocation.getInvocationContext();
            ServletContext context
                = (ServletContext) actionContext.get(StrutsStatics.SERVLET_CONTEXT);
            HttpServletResponse response
                = (HttpServletResponse) actionContext.get(StrutsStatics.HTTP_RESPONSE);
            // 출력할 bean들을 만든다.
            Map<String, Object> beanParams = new HashMap<String, Object>();
            String[] beanNames = splitBeans();
            for (String beanName : beanNames) {
                beanParams.put(beanName, invocation.getStack().findValue(beanName));
            }
            XLSTransformer transformer = new XLSTransformer();
            InputStream is = null;
            HSSFWorkbook workbook;
            String finalTemplate = TextParseUtil.translateVariables(this.template, invocation.getStack());
            try {
                is = readTemplate(finalTemplate, context);
                workbook = transformer.transformXLS(is, beanParams);
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        // 무시
                    }
                }
            }
            String filename = invocation.getStack().findString(filenameKey);
            if (filename == null)
                filename = "기본파일이름";
            writeWorkbook(filename, response, workbook);
        }
        /** 엑셀에 출력할 객체이름(key)들을 분리한다. */
        private String[] splitBeans() {
            return this.beans.split(",");
        }
        /** 엑셀 결과를 출력한다. */
        private void writeWorkbook(
            String filename, HttpServletResponse response, HSSFWorkbook workbook)
            throws IOException {
            response.setHeader(
                "Content-disposition", "attachment;filename=" + encodeFileName(filename + ".xls"));
            response.setContentType(FileServerUtil.getContentTypeOfFile("xls"));
            workbook.write(response.getOutputStream());
        }
        /** 파일이름 인코딩 */
        private String encodeFileName(String filename) {
            try {
                return URLEncoder.encode(filename, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e.getMessage, e);
            }
        }
        /** 엑셀 템플릿을 읽는다. */
        private InputStream readTemplate(
            String finalTemplate, ServletContext context) throws FileNotFoundException {
            String templateFilePath = context.getRealPath(finalTemplate);
            return new FileInputStream(templateFilePath);
        }
        /**
         * @param template 엑셀 템플릿
         */
        public void setTemplate(String template) {
            this.template = template;
        }
        /**
         * @param beans 엑셀에 출력할 객체들
         */
        public void setBeans(String beans) {
            this.beans = beans;
        }
        /**
         * @param filenameKey 파일이름을 얻어올 키값
         */
        public void setFilenameKey(String filenameKey) {
            this.filenameKey = filenameKey;
        }
    }



    Struts2에 Result Type 추가
    struts.xml 에 구현한 result typ을 추가한다. 상세한 result type 구현 및 추가방법은 struts 사이트를 참조한다.

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">
    <struts>
     ...
     <package name="my-default" extends="struts-default">
      <result-types>
       <result-type name="excel" class="com.nextree.fw.commonweb.struts.result.JXLSResult" />
      </result-types>
     ...
     </package>
    </struts>



    Controller 작성
    Struts2의 Action을 controller로 사용하겠다. 아래와 같이 Action을 만든다. 참고로 struts2는 POJO를 그대로 Action으로 사용할 수 있다. 자세한 Struts2 Action 작성방법은 Struts2 site를 참조한다.
    public class CustomerListController {
        /** 반환할 값 */
        private List<Customer> customers;

        public String execute() {
            ....
            this.customers = ....;   // 여기서 출력해야 할 데이터를 조회한다.
            return Action.SUCCESS;
        }
        /* 출력할 값의 getter 메소드를 반드시 제공한다. */
        public List<Customer> getCustomers() {
            return new JSONResult(this.zipCodeList);
        }
        /* 위 Excel Result type의 경우 file이름을 controller에서 가져오게 되어 있다. */
        public String getFilename() {
            return "고객목록";
        }
    }


    Action 정의
    Struts2 의 struts.xml에 action을 정의한다. 우리는 excel로 다운로드하기에 앞에 설치한 excel result type을 사용한다.
     
    ...
    <package name="mypackage" namespace="/mypackage" extends="my-default">
      <action name="customers" class="test.CustomerListController" method="execute">
       <result type="excel">
        <param name="template">/customer/CustomerList.xls</param>
        <param name="beans">customers</param>
        <param name="filenameKey">filename</param>
       </result>
      </action>
    ...

    이제 http://hostname:port/context/mypackage/customers.do 로 접속하면 Excel 파일을 다운로드 할 것이다.

    결론
    위의 방법을 사용하면 그냥 JSP 작성하듯이 엑셀양식을 작성하여 엑셀을 다운로드하는 기능을 구현하기가 매우 쉽다.
    엑셀을 그대로 사용하기 때문에 엑셀에 화려한 서식뿐만 아니라 차트나 수식등을 넣을 수도 있어 고품질의 엑셀파일을 만들 수 있다.
     
    - 출처 : http://greatkim91.tistory.com/tag/Excel

    Posted by 복태아빠
    ,


     public void sendMail(MailEty mailEty){
      
      try{
       
    //   create some properties and get the default Session
       Properties props = new Properties();
       props.put("mail.smtp.host", mailEty.getSmtphost());

       Session sess = Session.getDefaultInstance(props, null);
       
    //   create a message
       Message msg = new MimeMessage(sess);
       
       msg.setFrom(new InternetAddress(mailEty.getFrom()));
       InternetAddress[] address = {new InternetAddress(mailEty.getTo())};
       msg.setRecipients(Message.RecipientType.TO, address);
       msg.setSubject(mailEty.getMailSubject());
       msg.setSentDate(new Date());
       
       
        //
                // This HTML mail have to 2 part, the BODY and the embedded image
                //
                MimeMultipart multipart = new MimeMultipart("related");

                // first part  (the html)
                BodyPart messageBodyPart = new MimeBodyPart();
                  
       String htmlText = "<HTML>" +
        "<HEAD><TITLE></TITLE></HEAD>" +
        "<BODY>" +
         "<table><tr><td>"+
         "<br>"+
         "Dear "+mailEty.getName()+",<br>"+
         " <br>"+
         " </td>"+
         "</tr>"+
         "<tr>"+
         " <td width=100%><img src=\"cid:starhublogoimg\"></img></td>"+
         "</tr>"+
         "</table>"+
         "</BODY>" +
        "</HTML>";
       
        messageBodyPart.setContent(htmlText, "text/html");
       
        //add it
              multipart.addBodyPart(messageBodyPart);

             
              // second part (the image)
                 messageBodyPart = new MimeBodyPart();
                
                 String filePath = mailEty.getPath()+"/images/starhub_mail_logo.gif";
                
                 DataSource fds = new FileDataSource(filePath);
                 messageBodyPart.setDataHandler(new DataHandler(fds));
                 messageBodyPart.setHeader("Content-ID","<starhublogoimg>");

                 // add it
                 multipart.addBodyPart(messageBodyPart);
       
       //msg.setContent(htmlText,"text/html; charset=euc-kr"); // HTML 형식
             msg.setContent(multipart); // HTML 형식
    //   msg.setText(msgText); // TEXT 형식
       Transport.send(msg);
       logger.info(mailEty.getTo()+" mail send!");
                
       
      }catch(MessagingException mex){
       logger.error(mex.getMessage()+"<br>");
       logger.error(mailEty.getSmtphost()+" connected error! \n mail send fail!");
      }
      
      
     }

    Posted by 복태아빠
    ,

    1 Apache 2.0

    http://httpd.apache.org 에서 : httpd-2.0.63.tar.gz 받아서 푼다.

     

    $ tar xvfz : httpd-2.0.63.tar.gz

     

    INSTALL 파일을 참고하여 apache를 설치하고 실행한다.

     

    $ ./configure --prefix=PREFIX

    $ make

    $ make install

    $ PREFIX/bin/apachectl start

    여기서 PREFIX apache가 설치될 디렉토리이다. --prefix=PREFIX를 생략하면 /usr/local/apache2에 설치된다.

     

    이하의 설명에서 PREFIX는 아파치가 설치된 디렉토리를 뜻한다.


    브라우저를 열고 http://<설치된 IP>/ 입력하였을 때, apache web server가 설치되었다는 메시지가 나오면 성공.

     

    아파치 서버를 끝내려면

     

    $ PREFIX/bin/apachectl stop

     

    2 mod_jk2.so

     

    http://jakarta.apache.org 에서 jakarta-tomcat-connectors-jk2-2.0.4-src.tar.gz 를 받아서 푼다.

     

    jk/native2/INSTALL.txt를 참고하여 mod_jk2.so를 만든다.

    $ ./configure --with-apxs2=PREFIX/bin/apxs

    $ make

    $ cd ../build/jk2/apache2

    $ PREFIX/bin/apxs -n jk2 -i mod_jk2.so

     

    [edit]

    3 httpd.conf

    위에서 만든 mod_jk2.so PREFIX/modules/에 복사한 다음, PREFIX/conf/httpd.conf 에서

    #LoadModule foo_module modules/mod_foo.so

    와 같은 형식으로 되어 있는 곳을 찾아 그 아래에

    LoadModule jk2_module modules/mod_jk2.so

    를 추가한다.

     

    DocumentRoot "localhost:80"

    4 workers2.properties

    PREFIX/conf/ workers2.properties를 아래의 내용으로 만들어 저장한다. ( jk/native2/INSTALL.txt 참고 )

    [channel.socket:<tomcat_ip>:8009]

    port=8009

    host=<tomcat_ip>

     

    [ajp13: <tomcat_ip>:8009]

    channel=channel.socket: <tomcat_ip>:8009

     

    [uri:user/*]

    worker=ajp13: <tomcat_ip>:8009

     

    [uri:/mgt/*]

    worker=ajp13: <tomcat_ip>:8009

    Posted by 복태아빠
    ,

    성능개선

    open study 2009. 5. 7. 12:49
    Posted by 복태아빠
    ,

    1. Windows : catalina.bat

    set CATALINA_OPTS=-Xms512m -Xmx512m

    2. ksh/bash : catalina.sh
    export CATALINA_OPTS="-Xms512m -Xmx512m"

    3. tcsh/csh : catalina.sh
    setenv CATALINA_OPTS="-Xms512m -Xmx512m"


    Tomcat을 사용하다가 java application 실행 시
    java.lang.OutOfMemoryError: Java heap space 에러 생겼을 때 해결방법.


    생기는 경우의 수가 많이 있을 것 같지만, 이번에 아래의 스크립트로 해겼되었으므로 적어놔야 겠다.

    ----

    이번 경우는 Confluence Wiki 설치 중, 어느 부분에서 페이지가 넘어가지 못하고 먹통이 되었다가 결국 아래의 에러가 발생했다. 

    java.lang.OutOfMemoryError: Java heap space

    Confluence Wiki와 같은 Java 어플리케이션은 JVM에서 구동된다. 따라서 시작 시 JVM에서 일정량의 메모리를 할당하여 어플리케이션에서 사용된다. 기본적으로 Java 가상머신은 서버에 얼마나 많은 물리적인 메모리가 설치되있더라도 오직 64MB의 메모리만을 할당된다고 한다.
    따라서 Confluence Wiki같은 중간규모 이상의 어플리케이션은 늘리필요가 있다고 한다.

    늘리는 방법을 찾느냐 조금 시간이 걸렸다.
    Linux, Java, Tomcat에 능통한 사람이면 금방 찾을 수 있지만, 전혀 찾을 수 없었다.
    그래서 여기저기 해결방법을 짜집기 하다가 힌트를 얻었고 스크립트를 작성하였다.

    ** tomcat 5.5 기준임 **

    /usr/local/tomcat/bin 에 tomcat.sh를 하나 만들자. (/usr/local/ 는 사람마다 다를 수 있슴)

    > cd  /usr/local/tomcat/bin
    > vi tomcat5.sh

    tomcat5.sh 파일내용

    #!/bin/sh
    # file:tomcat.sh
    CLASSPATH=/usr/java/jdk1.5.0_15/lib
    JAVA_HOME=/usr/java/jdk1.5.0_15
    CATALINA_OPTS=-Djava.awt.headless=true
    JAVA_OPTS=-Xms512m -Xmx512m

    export CLASSPATH
    export JAVA_HOME
    export CATALINA_OPTS
    export JAVA_OPTS

    case "$1" in
    start)
    echo "Starting tomcat5..."
    /usr/local/tomcat/bin/startup.sh
    ;;
    stop)
    echo "Stopping tomcat5..."
    /usr/local/tomcat/bin/shutdown.sh
    ;;
    *)
    echo "Usage tomcat.sh start/stop"
    exit 1;;
    esac


    tomcat 시작

    > tomcat.sh stop
    > tomcat.sh start 



    flex 와 같은 어플리케이션을 로드 하는 경우에는 permSize 를 설정하는 것이 좋다.
    Xms-> 최소 메모리값 , Xmx-> 최대 메모리값 , permSize-> Permanent generation  영역은 객체나, 함수가 차지하는 영역 이고 Xmx 로 설정하는 heap 메모리영역에 포함되지 않는다
    export JAVA_OPTS="-Xms256m -Xmx1024m -XX:PermSize=64m -XX:MaxPermSize=128m"
     

    Posted by 복태아빠
    ,
    Ajax, Comet and Jetty 정리

     

    jetty의 성능에 대해 알아야 할 필요성이 있어 자료를 찾다가 아래를 발견
    http://www.webtide.com/downloads/whitePaperAjaxJetty.html
    (webtide에 register, login 후 볼 수 있음)

    그래서 정리함.

    introduction
    web 2.0 나오면서 ajax& comet의 조합이 많이 나오는데,
    저 조합을 채택하면 확장성 있는 시스템 만들기가 꽤 어렵다.
    그래서 jetty 6 서버에서는 저런 것들을 어떻게 처리하는지 이 paper에서 살펴보자.

    Servlet Threading
    Thread per connection

    커넥션 하나당 쓰레드 하나를 할당하는 모델에 대해 본자.
    HTTP 1.0에서 1.1으로 넘어오면서 keepalive를 사용하여 커넥션을 처리하는 패턴을 변경할 수 있다.
    그래서 high rates request는 잘 처리할 수 있으나, 동시에 처리할 수 있는 유저수가 떨어진다.

    Thread per request
    request 하나당 쓰레드 하나 모델에 대해 보자.
    NIO가 나온이후에 커낵션 하나에 쓰레드 하나를 계속 할당할 필요가 없이,
    커낵션에 데이터가 왔다갔다 할 때만 select에서 event를 받아서 처리하면 된다.

    그래서 처리 효율이 올라가고 확장성도 늘어났다. 커낵션 하나당 쓰레드 하나를
    할당할 필요가 없어서 더 많은 동시유저를 처리할 수 있게 되었다.

    The Ajax/Comet Problem
    브라우저가 서버에게 request만 날리는 상황에서는 thread per request가 잘 맞아떨어지는데, comet에서는 사용자의 사용행태가 high rates request가 예상되므로
    thread per request과 comet의 궁합이 거시기 하다.

    그리고 comet에서는 서버가 long standing request를 유지해야 하므로 thread-per-request
    모델과는 커낵션 사용행태에 대한 가정 자체가 완전 다르다.

    이 단락에 있는 표는 일반적인 java servlet(synchronous servlet)에서 comet을 구현할 경우
    x된다는 사실을 예상하게 하는 예상 리소스 사용량이다.

    그러나 comet도 아무생각 없이 일반 java servlet server에서 처리될 수 있으면 확장성이 좋아질 거 같다.
    이렇게 되려면 기존의 thread-per-request 모델이 thread-per-active-request 모델로 변경되면 가능해진다.

    The Problem of waiting servlets.
    위와 같은 connection 처리 모델에서 발생하는 문제 외에도,
    서버내에 병목현상을 유발하는 리소스가 있으면 servlet이 block될 수 있다.
    (이 내용이 왜 있는지는 모르겠으나, jetty에서 이런 것도 해결해 준다는 걸 설명하기 위함인듯....)

    Solutions implemented in jetty 6
    NIO Connectors

    jetty는 NIO를 사용한다. 그래서 active-request는 thread-per-request 모델로 해결한다.

    Continuations
    long standing request의 경우을 위해 Continuation 매커니즘을 jetty에서 제시하고 있다.
    WaitingContinuation는 wait()로 중지될 수 있고, notify()로 재게될 수 있어서
    long standing request의 경우 thread-per-request처럼 thread에 할당하지 않고
    continuation으로 처리한다.

    continuation은 wait()으로 처리가 중지될 경우 특별한 종류의 RuntimeException을 발생시켜서 long standing reqeust를 Filter/ReqeustDispatcher/Servlet 체인에서 제외시키서 서블릿 컨테이너의 stack에서 제외시켜버린다. 이후에 jetty는 중지된 long standing request를 notify()에 의해 제게되거나, timeout이 발생할 때까지 가지고 있는다.

    이런식으로 해서 long standing request가 실행되는 순간에만 resource가 할당되어 실행되고, 아무런 이벤트 없이 기다리는 시간에는 long standing request에 어떠한 resource도
    할당되지 않게 한다.

    이 장에 있는 그림은 comet에 continuation을 적용하였을 때의 리소스 사용량을 추산한 것이다.

    split buffers
    servlet에서 실행될 때에만 buffer를 할당해준다. 그래서 메모리 사용량을 줄여보겠다.
    waiting 하고 있는 얘들한테는 어떠한 resource도 연결 안 시키겠다는 내용인것 같다.

    Delayed Dispatch
    tcp패킷은 나누어져서 올 수 있기 때문에 head만 읽고 body가 예상하여
    즉시 long standing request를 notify()시켜버리면 body가 도착안 한 경우
    상황이 거시기 해질 수 있다.
    그러므로 head와 body를 다 받은 후 long standing request가 받은 내용을 즉시 처리할 수 있을 때에만 에 notify()하겠다.

    Conclusion
    was에서 comet도 부드럽게 처리할 수 있는 아키텍쳐를 수립했다.


    Posted by 복태아빠
    ,

    인터페이스 설계

    open study 2009. 1. 12. 17:39

    인터페이스 설계

    첫 인터페이스 설계를 7년전에 해 본 것 같다.
    그때는 정말 어리버리하게 만들었었다. 지금 보면 살짝 낯 뜨거울 정도로..
    그 놈들이 지금까지도 별 수정없이 사용된다니 믿기지 않기는 하지만...

    오늘도 인터페이스 설계에 대한 회의를 하는데,
    예전에 만들었던 것을 떠 올리게 하는 사건이 있어서 잠시 되돌아보니
    오늘도 포함해서 많은 아쉬운 순간들이 생각난다.

    인터페이스 설계에서 생각해야 하는 점들을 정리해보자.
    그리고 똑같은 실수는 되풀이 하지 말자.

    - 무엇과 무엇을 연결하는가?
    로컬 장비내에서의 통신인가? 리모트 장비와의 통신인가?

    - 인터페이스가 구현될 최상위 통신 프로토콜은 무엇인가?
    FILE인가? PIPE인가? TCP인가? HTTP인가? 혹은 또다른 무엇인가?

    - 통신망은 어떤 상황인가?
    내부망인가? 아니면 이더넷으로 나가야 하는가?
    그리고 내부망에서 L4같은 것들의 설정값 혹은 정책은 어떠한가?

    - request, response의 크기
    size를 줄여서 request, response가 왠만하면 하나의 packet에 가게 할 것인가?
    아니면 그냥 그런 거 고민 안 할 것인가?

    - BINARY 포멧을 사용해야 하는가? TEXT 포멧을 사용해야 할 것인가?
    때에 따라서는 BINARY 포멧을 써야 할 때도 있다.

    - 커넥션과 싱크 문제는?
    항상 연결되어 있을 것인가? 하나 처리하고 끊을 것인가?
    그리고 싱크로 할것인가? 어싱크로 할 것인가?

    - CHARSET은?
    UTF8을 쓸 것인가? 유니코드를 쓸 것인가? 아니면 영문으로만 제약할 것인가?

    - 시간문제는?
    시간이 중요한 문제인가? 몇초차이는 용납이 되나?

    - 사용하는 프로토콜을 적절히 사용하였나?
    예를 들면 TCP 쓰는데 data부분에 받는 측 ip, port값이 나온다거나,
    HTTP request 만들었는데 data부분에 url이 있거나 하지는 않는가?

    - REST 에서 발생하는 여러가지 거시기한 상황들
    만일 다른 설계자가 REST를 이상하게 쓰자고 주장하면 어떻게 할 것인가?

    - 결과 코드, 오류코드의 개념을 어떻게 구현할 것인가?
    아마 사람 머리 수만큼 설계안이 나올 것이다. 그렇지 않다면 누군가 놀고 있다.
    그리고 비즈니스 오류의 개수가 엄청나게 많지 않나? 그럼 어떻게 할것인가?

    - 멀티 request의 개념이 있는가?
    트랜잭션 처리를 해야하나? 아니면 기계적으로 배치작업처럼 처리를 할 건가?
    아니면 BPML처럼 제어코드를 정의할 것인가?

    - 서비스 불가능한 상황에 대해서도 고려되어 있는가?
    마이그레이션 중이라던가, 서비스 접었다거나, 서비스 점검등의 상황에 대비하였나?

    - 확장성은 어떻게 생각하나?
    확장성있게 무엇인가를 더 추가할 것인가?
    아니면 발생할 것 같지도 않은 "그때" 가서 다시 생각해 보겠나?

    - 하위 버전은 생각해 봤나?
    인터페이스가 변경되면 어떻게 처리할래?

    - 설계한 인터페이스에서 과한 부분이 없는가?
    더 뺄 것이 없는가?

    - 인터페이스가 알아먹을만 한가?
    너무 복잡하게 설계되어 있지는 아니한가?
    ...

    이외에도 엄청나게 많은 중요한 사항들이 있을 것이다.
    그러나 가장 중요한 것 한가지는 바로 아래 한줄일 것이다.

    - 설계가 끝난 후 협업한 설계자와 더 친해졌는가?

    Posted by 복태아빠
    ,

    Comet 에 대하여

    open study 2009. 1. 12. 17:31
    http://www.okjsp.pe.kr/seq/131233

    Comet 에 대하여

    Reverse Ajax, Ajax Push, Two-Way-Web, HTTP server PUSH 등등으로 불리던 말이 이제 Comet 이라는 용어로 통일되어 가는 거 같다.  개념이 나온지는 꽤 되었지만 자바 서블릿 컨테이너가 이를 효과적으로 지원하지 않아 그간 관심만 가지고 있었다.  그런데 요즘 여러 서블릿 컨테이너들이 이를 지원하기 시작했고 Servlet API로의 표준화도 논의되고 있는 거 같다.  그래서 Comet 에 관련하여 한번 정리해 보고자 한다.

    Comet 이란 웹 클라이언트(보통 웹 브라우저)의 명시적인 요청이 없어도 서버에서 클라이언트로 데이타를 밀어넣는(Push) 방식으로 동작하는 웹 프로그래밍 모델을 일컫는 말이다.  다만 HTTP 트랜잭션을 웹 서버가 먼저 시작할 수는 없으므로 클라이언트가 먼저 서버에 접속하여 접속을 계속 유지한 상태에서 서버가 데이타를 전송한다.  서버가 먼저 클라이언트로 접속을 열어 데이타를 전송할 수는 없다.

    원래 HTTP라는 것이 웹 브라우저에서 요청을 보내면 서버가 응답을 전송하고 끝마치는 간단한 구조라 서버에서 클라이언트로 데이타를 밀어넣는 것은 구현하기가 힘들다.  이런 어려움을 어떻게든 극복하고자 여러 시도가 있었고 이들이 Comet 이라는 이름으로 정리되어 가는 중인 거다.

    Comet 은 구체적인 구현 방법을 가리키는 말은 아니며 여러 방식들을 통칭하는 말이다.  HTTP server push 를 구현하는 방법은 여러 방식이 있는데 이중의 부분 집합을 Comet 이라 할 수 있다.  HTTP Server push 방법에는 다음과 같은 것들이 있다.

    1. Polling
    일정 간격으로 서버에 요청을 보내어 서버의 이벤트를 받는 방식이다.  요청 간격을 줄이면 거의 실시간으로 서버의 이벤트를 받을 수 있다.  그러나 서버의 자원과 대역을 많이 소모하므로 실제로 쓰기는 어려운 방식이다.

    2. Long Polling
    클라이언트가 서버에 접속을 하면 서버는 계속 접속을 유지하고 있다가 이벤트가 발생하면 클라이언트로 전송하고  HTTP 트랜잭션을 마친다. 클라이언트는 메시지를 받으면 다시 서버에 접속을 하여 위의 과정을 반복한다.

    3. Streaming
    클라이언트와 서버의 접속을 계속 유지한 채 서버에서 이벤트가 발생할 때마다 메시지를 HTTP Chunked 방식으로 보낸다.  가장 이상적인 방법이겠으나 ajax로 구현할 때 ie 에서는 제대로 작동하지 않는다(readyState = 3 이벤트가 발생하지 않는다).  iframe 을 사용하여 <script> 태그로 둘러싼 메시지를 보내는 방식을 사용할 수 있지만 이 방식은 접속 오류를 제대로 처리하기 힘들다.

    이중 Long Polling 과 Streaming 을 Comet 이라고 한다.  Server PUSH 로는 Streaming 이 이상적이겠지만 위의 한계점 때문에 보통 Long Polling 방식을 주로 사용한다.  Streaming 방식을 지원하는 것이 HTML5에서 논의 중인 것으로 보여 앞으로 이것이 구체화되면 Streaming 방식으로 좀더 효율적으로 Comet 을 구현할 수 있을 것이다.

    Comet 모델을 구현하는 데 또 한 가지 걸림돌은 웹 서버의 지원이다.  Long Polling 이든 Streaming 이든 HTTP 커넥션을 계속 유지할 필요가 있는데 대부분의 웹 서버(자바 서블릿 컨테이너도 포함)들은 짧은 HTTP 트랙잭션에 맞춰져 있기 때문에 Comet 을 지원하는데 효율적이지 못하다.  특히 자바 서블릿 컨테이너는 HTTP 요청이 각각 스레드에 할당되어 처리되기 때문에 Comet 을 지원하게 되면 많은 스레드가 생성되게 된다.  스레드는 대부분 단지 커넥션을 유지하고 있을 뿐 거의 동작을 하지 않다가 서버측 이벤트가 있을 경우에만 잠깐 동작할 뿐이다.  이렇게 많은 Idle 스레드가 생기면 스레드 생성 및 컨텍스트 교환에 따른 오버헤드가 무시못하게 된다.  

    Comet 을 효율적으로 지원하기 위해서는 다른 고성능 서버 프로그램처럼 하나의 스레드가 여러 커넥션(이 경우 웹 요청)을 처리할 수 있어야 한다.  현재 서블릿 API에서는 이것이 불가능하다.  따라서 이에 대한 대안이 여럿 등장했다.  Comet 요청을 전용으로 처리하고자 하는 서버도 있었으며 서블릿 컨테이너 중에는 Jetty 가 최초로 Continuation 방식의 API 를 선보였다.  이후 Tomcat 6.0, Resin 3.1 에서도 각기 나름의 API 를 선보였다.  현재 Early Draft Review 상태에 있는 Servlet 3.0 에서 Jetty의 Continuation 과 비슷한 방식으로 지원이 논의되고 있다.

    다음 포스트에서 서블릿 컨테이너별로 Comet 을 지원하는 기능을 살펴보고자 한다.

    by Corund | 2008/07/29 19:32 | 트랙백 | 핑백(3) | 덧글(0)

    트랙백 주소 : http://corund.egloos.com/tb/1907537
    ☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
    Linked at 점프와 쉼없는 나아감 : 자바.. at 2008/07/30 16:52

    ... 이전글: Comet 에 대하여 Jetty 는 버전 6.0에서부터 Continuation 이라는 것을 도입해서 Comet 스타일의 웹 프로그래밍을 효과적으로 구현할 수 있게 되었다. jet ... more

    Linked at 점프와 쉼없는 나아감 : 자바.. at 2008/07/30 19:31

    ... 이전글 Comet 에 대하여 자바 서블릿 컨테이너의 Comet 지원 1 - Jetty Tomcat 에서는 버전 6.0의 Advanced IO 지원을 통해 Comet 모델을 효과적으로 구현할 수 있 ... more

    Linked at 점프와 쉼없는 나아감 : 자바.. at 2008/08/01 09:35

    ... 이전글 Comet 에 대하여 자바 서블릿 컨테이너의 Comet 지원 1 - Jetty자바 서블릿 컨테이너의 Comet 지원 2 - Tomcat Resin 은 버전 3.1에서 Comet 모델을 지원 ... more

    Posted by 복태아빠
    ,