- 상황설명:
-
최근
foo.html을bar.html로 변경하고 역호환성을 위해 이전 URL을 계속 제공하고 싶다고 가정하자. 사용자는 이전 URL이 변경되었다는 사실을 눈치채지 못한다. - 해결책:
-
다음 규칙으로 이전 URL을 내부적으로 새로운 URL로 재작성한다:
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html
- 상황설명:
-
다시
foo.html을bar.html로 변경하고 역호환성을 위해 이전 URL을 계속 제공하고 싶다고 가정하자. 그러나 이제는 이전 URL을 사용하면 사용자에게 새로운 URL을 힌트로 알려준다. 즉, 브라우저 주소창이 변한다. - 해결책:
-
새로운 URL로 HTTP 리다이렉션하다. 그러면 브라우저가 새로운 URL를 보이고 변경사실을 사용자가 알게된다:
RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ bar.html [R]
- 상황설명:
-
최소한 중요한 최상위 페이지는 브라우저에 최적화된 내용으로 서비스해야할 경우가 있다. 즉, 최신 Netscape 브라우저에게는 최상의 버전을, Lynx 브라우저에게는 최저 버전을, 나머지 브라우저에는 평균적인 버전을 제공한다.
- 해결책:
-
브라우저가 내용협상을 위해 자신의 종류에 대한 정보를 제공하지 않기때문에 내용협상을 사용할 수 없다. 대신 HTTP "User-Agent" 헤더를 사용한다. 다음 규칙은 HTTP "User-Agent" 헤더가 "Mozilla/3"으로 시작하면
foo.html페이지를foo.NS.html로 재작성하고 재작성을 중단한다. 브라우저가 "Lynx"나 "Mozilla" 버전 1 혹은 2라면 URL은foo.20.html이 된다. 나머지 브라우저는foo.32.html페이지를 받는다. 아래 규칙이 이 작업을 한다:RewriteCond %{HTTP_USER_AGENT} ^Mozilla/3.* RewriteRule ^foo\.html$ foo.NS.html [L] RewriteCond %{HTTP_USER_AGENT} ^Lynx/.* [OR] RewriteCond %{HTTP_USER_AGENT} ^Mozilla/[12].* RewriteRule ^foo\.html$ foo.20.html [L] RewriteRule ^foo\.html$ foo.32.html [L]
- 상황설명:
-
외부 호스트에 우리 사이트로 가져오고 싶은 좋은 웹페이지가 있다고 가정하자. FTP 서버의 경우 직접 외부 자료의 최신복사본을 유지하는
mirror프로그램을 사용할 수 있고, 웹서버라면 HTTP로 비슷한 작업을 하는webcopy프로그램을 사용할 수 있다. 그러나 두 방법 모두 단점이 있다: 복사본은 가끔씩 프로그램을 실행해줄 때만 최신판으로 유지된다. 직접 구성해야하는 정적인 미러가 아니라면 좋겠다. 대신 (외부 호스트에서 자료가 갱신되면) 필요할때 자동으로 자료를 갱신하는 동적 미러가 필요하다. - 해결책:
-
이를 위해 Proxy Throughput 기능을 (플래그
[P]) 사용하여 외부 웹페이지 혹은 외부 웹공간 전체를 우리 이름공간으로 대응한다:RewriteEngine on RewriteBase /~quux/ RewriteRule ^hotsheet/(.*)$ http://www.tstimpreso.com/hotsheet/$1 [P]
RewriteEngine on RewriteBase /~quux/ RewriteRule ^usa-news\.html$ http://www.quux-corp.com/news/index.html [P]
- 상황설명:
- ...
- 해결책:
-
RewriteEngine on RewriteCond /mirror/of/remotesite/$1 -U RewriteRule ^http://www\.remotesite\.com/(.*)$ /mirror/of/remotesite/$1
- 상황설명:
-
실제 자료를 방화벽이 보호하는 (내부) 인트라넷 웹서버에 (
www2.quux-corp.dom) 저장하면서, 기업의 (외부) 인터넷 웹서버를 (www.quux-corp.dom) 실행하는 것처럼 보이게 한다. 외부 웹서버는 요청한 자료를 내부 웹서버에서 가져온다. - 해결책:
-
먼저 방화벽이 내부 웹서버를 보호하고 외부 웹서버만이 내부 웹서버에서 자료를 얻을 수 있게 한다. 다음과 같이 패킷필터링 방화벽을 설정한다:
ALLOW Host www.quux-corp.dom Port >1024 --> Host www2.quux-corp.dom Port 80 DENY Host * Port * --> Host www2.quux-corp.dom Port 80
실제 설정문법에 알맞게 고쳐라. 없는 자료를 내부적으로 proxy throughput 기능을 통해 요청하는
mod_rewrite 규칙을 작성한다:RewriteRule ^/~([^/]+)/?(.*) /home/$1/.www/$2 RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^/home/([^/]+)/.www/?(.*) http://www2.quux-corp.dom/~$1/pub/$2 [P]
- 상황설명:
-
www.foo.com의 통신량을www[0-5].foo.com(총 서버 6대)으로 분산하고 싶다. 어떻게 하는가? - 해결책:
-
매우 다양한 방법으로 이 문제를 해결할 수 있다. 먼저 DNS를 사용한 잘 알려진 방법을 설명하고,
mod_rewrite 를 사용하는 경우를 살펴보자:-
DNS Round-Robin
가장 간단한 로드밸런싱 방법은
BIND의 DNS round-robin 방식을 사용하는 것이다. 다음과 같이 DNS A(address) 레코드에www[0-9].foo.com을 설정한다.www0 IN A 1.2.3.1 www1 IN A 1.2.3.2 www2 IN A 1.2.3.3 www3 IN A 1.2.3.4 www4 IN A 1.2.3.5 www5 IN A 1.2.3.6
그리고 다음 항목을 추가한다:
www IN CNAME www0.foo.com. IN CNAME www1.foo.com. IN CNAME www2.foo.com. IN CNAME www3.foo.com. IN CNAME www4.foo.com. IN CNAME www5.foo.com. IN CNAME www6.foo.com.잘못된 것처럼 보이지만, 실제로
BIND의 의도된 기능이다. 이제www.foo.com을 찾으면,BIND는 매번 순서를 조금씩 바꿔가며www0-www6을 반환한다. 그래서 클라이언트들을 여러 서버로 분산한다. 그러나 DNS 검색 결과가 네트웍의 다른 네임서버에 캐쉬되여www.foo.com을 찾은 결과가 특정wwwN.foo.com이면 클라이언트의 다음 요청들도 같은wwwN.foo.com으로 보내지기때문에 완벽한 로드밸런싱 기법이 아님을 주의하라. 그러나 크게 보면 요청이 여러 웹서버에 분산되므로 효과가 좋다. -
DNS 로드밸런싱
http://www.stanford.edu/~schemers/docs/lbnamed/lbnamed.html에 있는
lbnamed프로그램을 사용하여 정교한 DNS기반 로드밸런싱을 할 수 있다. DNS가 실제 로드밸런싱을 하도록 만드는 여러 도구와 Perl 5 프로그램이다. -
Proxy Throughput Round-Robin
이 방법은
mod_rewrite 와 proxy throughput 기능을 사용한다. 먼저 DNS에 다음 항목을 사용하여www0.foo.com이 실제www.foo.com을 전담하게 한다www IN CNAME www0.foo.com.
그리고
www0.foo.com을 프록시전용 서버로 변경한다. 즉, URL을 받으면 서버는 내부 프록시를 통해 다른 5대 서버중 (www1-www5) 한대로 보내기만 한다. 이를 위해 먼저 모든 URL을 로드밸런싱 스크립트lb.pl로 보내는 규칙을 만든다.RewriteEngine on RewriteMap lb prg:/path/to/lb.pl RewriteRule ^/(.+)$ ${lb:$1} [P,L]lb.pl을 작성한다:#!/path/to/perl ## ## lb.pl -- 로드밸런싱 스크립트 ## $| = 1; $name = "www"; # 기본 호스트명 $first = 1; # 첫번째 서버 (자신이 0이기 때문에, 0을 사용하지 않는다) $last = 5; # round-robin에서 마지막 서버 $domain = "foo.dom"; # 도메인명 $cnt = 0; while (<STDIN>) { $cnt = (($cnt+1) % ($last+1-$first)); $server = sprintf("%s%d.%s", $name, $cnt+$first, $domain); print "http://$server/$_"; } ##EOF##마지막 주의: 왜 이 방법이 유용한가? www0.foo.com에 부담이 가지않는가? 물론, 부담이 된다. 그러나 단순한 proxy throughput 요청만 하기때문에 괜찮다! 모든 SSI, CGI, ePerl 등은 전적으로 다른 서버가 처리한다. 이것이 핵심이다. -
하드웨어/TCP Round-Robin
하드웨어를 사용한 해결책도 있다. Cisco는 TCP/IP 수준에서 로드밸런싱을 하는 LocalDirector라는 괴물을 판다. 실제로는 웹서버군 앞단에 위치하는 일종의 회로수준 게이트웨이다. 자금이 충분하고 고성능 해결책이 필요하다면 이것을 사용하라.
-
DNS Round-Robin
- 상황설명:
-
네트웍에는 멋진 CGI 프로그램들이 많다. 그러나 사용하기 번거러워서 많은 웹관리자가 사용하지 않는다. 아파치의 MIME-type에 따른 Action 핸들러 기능도 CGI 프로그램이 특별한 URL을 (정확히
PATH_INFO와QUERY_STRINGS) 프로그램의 입력으로 사용하지 않을 때만 적절하다. 먼저, 확장자가 (secure CGI를 줄여).scgi인 파일을 유명한cgiwrap프로그램으로 처리하기위해 새로운 type을 설정한다. 문제는 (위에서 본) 일관된 URL 구조를 사용하는 경우 사용자 홈디렉토리가/u/user/foo/bar.scgi같은 URL인 점이다.cgiwrap는/~user/foo/bar.scgi/형식의 URL을 원하기때문이다. 다음 규칙이 문제를 해결한다:RewriteRule ^/[uge]/([^/]+)/\.www/(.+)\.scgi(.*) ... ... /internal/cgi/user/cgiwrap/~$1/$2.scgi$3 [NS,T=application/x-http-cgi]
이제 다른 멋진 프로그램, (URL 하위트리에 대한
access.log를 출력하는)wwwlog와 (URL 하위트리에 Glimpse를 실행하는)wwwidx가 있다고 가정하자. 우리는 프로그램에게 작업할 대상인 URL 영역을 알려줘야 한다. 그러나 요청할때마다 항상 적어줘야 하기때문에 깔끔하지 않다. 즉, 보통/u/user/foo/에 대해swwidx프로그램을 실행한다면 다음과 같은 링크를 사용한다/internal/cgi/user/swwidx?i=/u/user/foo/
깔끔하지 않다. 링크에 영역의 위치와 CGI 위치를 모두 적어야 하기때문이다. 영역을 재구성한다면 여러 하이퍼링크를 수정하는데 많은 시간이 걸릴 것이다.
- 해결책:
-
해결책은 자동으로 적절한 CGI를 실행하는 새로운 특별한 URL 형식을 만드는 것이다. 다음과 같이 설정한다:
RewriteRule ^/([uge])/([^/]+)(/?.*)/\* /internal/cgi/user/wwwidx?i=/$1/$2$3/ RewriteRule ^/([uge])/([^/]+)(/?.*):log /internal/cgi/user/wwwlog?f=/$1/$2$3
이제
/u/user/foo/을 검색하는 링크는 다음과 같다HREF="*" /u/user/foo/* (???)
내부적으로 다음과 같이 자동변환된다
/internal/cgi/user/wwwidx?i=/u/user/foo/
같은 방법으로 링크 뒤에
:log를 사용하여 접근 로그 CGI 프로그램을 실행할 수 있다.
- 상황설명:
-
어떻게 브라우저와 사용자가 모르게 자연스럽게 정적 페이지
foo.html을 동적인foo.cgi로 변경할 수 있나. - 해결책:
-
URL을 CGI 스크립트로 재작성하고, MIME-type을 수정하여 CGI 스크립트로 실행하게 한다. 그래서
/~quux/foo.html를 요청하면 내부적으로/~quux/foo.cgi를 실행하게 된다.RewriteEngine on RewriteBase /~quux/ RewriteRule ^foo\.html$ foo.cgi [T=application/x-httpd-cgi]
- 상황설명:
-
이 방법은 실로 비기이다: 동적으로 페이지를 생성하지만, 정적으로 페이지를 서비스한다. 즉, 페이지는 순수하게 (파일시스템에서 읽은 내용을 그대로) 정적 페이지로 전달되지만, 없을 경우 웹서버가 동적으로 생성한다. 그러면 누가 (혹은 cron 작업이) 정적 컨텐츠를 지우지않는 한 CGI가 생성한 페이지를 정적으로 서비스한다. 컨텐츠를 지우면 내용을 갱신한다.
- 해결책:
-
다음 규칙을 사용한다:
RewriteCond %{REQUEST_FILENAME} !-s RewriteRule ^page\.html$ page.cgi [T=application/x-httpd-cgi,L]여기서
page.html를 요청할때page.html이 없거나 파일크기가 0인 경우 내부적으로page.cgi를 실행한다. 여기서 비결은page.cgi가 일반적인 CGI 스크립트와 같이STDOUT에 출력하고, 추가로 출력을page.html파일에 적는다. 한번 실행한후 서버는page.html의 정보를 보낸다. 웹관리자가 강재로 내용을 갱신하고 싶다면, (보통 cron 작업이)page.html을 지우기만 하면 된다.
- 상황설명:
-
복잡한 웹페이지를 만들때 편집자가 내용을 수정할 때마다 자동으로 페이지를 새로 고침하는 웹브라우저가 있으면 얼마나 좋을까? 불가능한가?
- 해결책:
-
가능하다! MIME multipart 기능과 웹서버 NPH 기능,
mod_rewrite 의 URL 조작 능력을 결합하면 된다. 먼저, 새로운 URL 기능을 만든다: URL에:refresh를 추가하기만 하면 파일시스템에서 수정될 때마다 새로 고침한다.RewriteRule ^(/[uge]/[^/]+/?.*):refresh /internal/cgi/apache/nph-refresh?f=$1
이제 다음 URL에 접근하면
/u/foo/bar/page.html:refresh
다음 URL을 내부적으로 부른다
/internal/cgi/apache/nph-refresh?f=/u/foo/bar/page.html
이제 NPH-CGI 스크립트만 남았다. 보통 "독자에게 연습으로 남겨둠"이라고 말하지만 ;-) 나는 이것도 제공한다.
#!/sw/bin/perl ## ## nph-refresh -- NPH/CGI script for auto refreshing pages ## Copyright (c) 1997 Ralf S. Engelschall, All Rights Reserved. ## $| = 1; # split the QUERY_STRING variable @pairs = split(/&/, $ENV{'QUERY_STRING'}); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $name =~ tr/A-Z/a-z/; $name = 'QS_' . $name; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; eval "\$$name = \"$value\""; } $QS_s = 1 if ($QS_s eq ''); $QS_n = 3600 if ($QS_n eq ''); if ($QS_f eq '') { print "HTTP/1.0 200 OK\n"; print "Content-type: text/html\n\n"; print "<b>ERROR</b>: No file given\n"; exit(0); } if (! -f $QS_f) { print "HTTP/1.0 200 OK\n"; print "Content-type: text/html\n\n"; print "<b>ERROR</b>: File $QS_f not found\n"; exit(0); } sub print_http_headers_multipart_begin { print "HTTP/1.0 200 OK\n"; $bound = "ThisRandomString12345"; print "Content-type: multipart/x-mixed-replace;boundary=$bound\n"; &print_http_headers_multipart_next; } sub print_http_headers_multipart_next { print "\n--$bound\n"; } sub print_http_headers_multipart_end { print "\n--$bound--\n"; } sub displayhtml { local($buffer) = @_; $len = length($buffer); print "Content-type: text/html\n"; print "Content-length: $len\n\n"; print $buffer; } sub readfile { local($file) = @_; local(*FP, $size, $buffer, $bytes); ($x, $x, $x, $x, $x, $x, $x, $size) = stat($file); $size = sprintf("%d", $size); open(FP, "<$file"); $bytes = sysread(FP, $buffer, $size); close(FP); return $buffer; } $buffer = &readfile($QS_f); &print_http_headers_multipart_begin; &displayhtml($buffer); sub mystat { local($file) = $_[0]; local($time); ($x, $x, $x, $x, $x, $x, $x, $x, $x, $mtime) = stat($file); return $mtime; } $mtimeL = &mystat($QS_f); $mtime = $mtime; for ($n = 0; $n < $QS_n; $n++) { while (1) { $mtime = &mystat($QS_f); if ($mtime ne $mtimeL) { $mtimeL = $mtime; sleep(2); $buffer = &readfile($QS_f); &print_http_headers_multipart_next; &displayhtml($buffer); sleep(5); $mtimeL = &mystat($QS_f); last; } sleep($QS_s); } } &print_http_headers_multipart_end; exit(0); ##EOF##
- 상황설명:
-
가상호스트가 몇개만 있다면 아파치의
VirtualHost 기능이 잘 동작한다. 그러나 가상호스트가 수백개 있는 ISP라면 이 기능이 최선은 아니다. - 해결책:
-
이 기능을 제공하려면 Proxy Throughput 기능을 (플래그
[P]) 사용하여 외부 웹페이지 혹은 전체 외부 웹영역을 우리의 이름공간에 대응한다:## ## vhost.map ## www.vhost1.dom:80 /path/to/docroot/vhost1 www.vhost2.dom:80 /path/to/docroot/vhost2 : www.vhostN.dom:80 /path/to/docroot/vhostN## ## httpd.conf ## : # 리다이렉트할때 정규 호스트명을 사용한다. UseCanonicalName on : # 가상호스트를 CLF 형식 앞에 추가한다 CustomLog /path/to/access_log "%{VHOST}e %h %l %u %t \"%r\" %>s %b" : # 주서버에서 재작성 엔진을 사용한다 RewriteEngine on # 두 맵을 정의한다: 하나는 URL을 고치고, # 다른 하나는 가상호스트별 DocumentRoot를 # 정의한다. RewriteMap lowercase int:tolower RewriteMap vhost txt:/path/to/vhost.map # 이제 크고 복잡한 규칙 한개를 사용하여 # 가상호스트로 대응한다. # # 1. 가상호스트들이 같이 사용하는 위치는 대응하지 않는다 RewriteCond %{REQUEST_URL} !^/commonurl1/.* RewriteCond %{REQUEST_URL} !^/commonurl2/.* : RewriteCond %{REQUEST_URL} !^/commonurlN/.* # # 2. 우리가 현재 사용하는 방법이 Host 헤더를 # 가상호스트를 지원하므로 # Host 헤더가 있는지 확인한다 RewriteCond %{HTTP_HOST} !^$ # # 3. 호스트명을 소문자로 만든다 RewriteCond ${lowercase:%{HTTP_HOST}|NONE} ^(.+)$ # # 4. vhost.map에서 호스트명을 찾고 # 경로일때만 기억한다 # (위에서 "NONE"은 아니다) RewriteCond ${vhost:%1} ^(/.*)$ # # 5. 마지막으로 URL을 문서 위치로 대응하고 # 로그에 남기기위해 가상호스트를 기억해 둔다 RewriteRule ^/(.*)$ %1/$1 [E=VHOST:${lowercase:%{HTTP_HOST}}] :