Apache 1.3에서 속도 제한을 제공하는 모듈은 두가지가 있다. Session당 속도 제한은 mod_bandwidth, Site당 일일/월별 최대 전송량 제한은 mod_throttle을 사용했다. (단지 이상한 점은, mod_bandwidth가 Apache 2.0에서 동작한다고 주장한다는 점이다. 실제로 구글로 검색을 하다 보면 이런식으로 적혀진 몇몇 흔적을 찾을 수 있는데, 정확한 내용을 찾을 길이 없다. 제작자의 홈페이지에서도 2007년 3월에 Apache 1.3용 모듈 소스가 배포된 것이 마지막이고, Apache 2.0용 mod_bandwidth의 소스는 찾을 수 없었다. 만일 있다고 하면 Apache 1.3과 2.x의 모듈 구조가 상당히 다른 바, 대부분의 소스가 바꼈을 것 같다. 실제로 Binary로 된, Window용 모듈이 배포되고 있는 것 같은 흔적도 있기는 하다.)
Apache 2.x에서는 mod_cband라는 모듈이 등장했는데, 어떤 놈인지 한번 까뒤집어보자.
두개의 handler를 등록하고, output_filter를 하나, post_config에 hook 하나, pool cleanup에 펑션을 하나 등록했다.
mod_cband_request_handler 이놈은 VirtualHost에 대해 bandwidth 사용 현황을 살펴보다가, 지정된 bandwidth를 넘어서면 지정된 URL로 redirect 시킨다. (Apache 1.3에서의 mod_throttle의 기능인것 같다.)
mod_cband_status_handler 이놈은 cband-status라는 페이지를 호출할때 자신의 상태를 보여주게 하는 handler인것 같다. 이 동작을 위해,
mod_cband_status_handler_style 이라는 문자열 상수와,
mod_cband_status_handler_foot 이라는 문자열 상수,
mod_cband_status_handler_HTML 라는 함수와,
mod_cband_status_handler_XML 라는 함수가 준비되어 있다.
의미는 대략 이름만으로 이해가 간다.
mod_cband_filter 이놈은 session당 Bandwidth 제한을 주는 기능을 수행하는 것 같다. 중간에 간단한 주석이 달려 있긴 하지만 불행히도 독일어이다; (Apache 1.3에서의 mod_bandwidth의 기능인것 같다.)
기본적으로 이 filter handler는 ap_register_output_filter 함수에 의해 hook과 함께 등록되고 있고, mod_cband_status_handler와 mod_cband_request_handler 에서 ap_add_output_filter 함수를 통해 connection과 연결되고 있다.
filter 내부의 동작은 자세히 살펴보지는 않았지만, 기본적인 core_filter(실제로 socket을 통해서 content를 내보내는 filter)보다는 먼저 수행되어야 할 것 같고, (내부적으로 그렇게 동작하는지는 알 수 없다.)
주된 동작은 bucket에 대해서 일어나는데, (미리 Apache의 bucket과 bucket bridage에 대한 정보를 얻은 후에 읽어보는 것이 좋겠다.)
참고 사이트: Guide to writing output filters(공식 사이트 문서이고, 제목 그대로 Output filter를 작성하는 가이드이다. 지금 mod_cband_filter 역시 Output filter이고, 그래서 이 문서는 유용하다. 또한, bucket과 bucket brigade에 대한 설명도 나와있다.)
mod_cband.c: line 3413
apr_bucket *b = APR_BRIGADE_FIRST(bb); (b에는 bucket_brigade의 첫번째 bucket이 들어간다.)
line 3467 (Point B)
while(b != APR_BRIGADE_SENTINEL(bb)) { (bucket_brigade_sentinel이 아닐때까지 루프를 돌면서,)
line 3486 (Point A)
if (apr_bucket_read(b, &buf, &bytes_bucket, APR_NONBLOCK_READ) == APR_SUCCESS) { (bucket에서 버퍼를 읽어들인다.)
line 3492 - 3533 (이 구간이 버퍼에 무언가 들어 있을때 실제로 bandwidth 제한을 가하는 루틴이며, 이 과정과 그 이후 에서 bytes_split가 계산이 된다.)
line 3566 - 3569
apr_bucket_split(b, bytes_split);
APR_BUCKET_REMOVE(b);
APR_BRIGADE_INSERT_TAIL(bbOut, b);
bytes -= bytes_split; (bucket b에서 bytes_split만큼을 분리하고, 분리된 bucket을 기존의 bucket brigade에서 삭제하고 새로운 bucket bridage 뒤에 붙여서,)
line 3572
ap_pass_brigade(f->next, bbOut); (bbOut이라는 새로운 bucket bridage를, 다음 filter로 넘긴다.)
(Point A에서 여기까지의 루프는 bucket이 모두 비워질 때 까지 계속되고)
line 3616 - 3619
APR_BUCKET_REMOVE(b);
APR_BRIGADE_INSERT_TAIL(bbOut, b);
b = APR_BRIGADE_FIRST(bb);
ap_pass_brigade(f->next, bbOut); (그 이후 bucket bridage에서 새로운 bucket을 꺼낸다.)
(Point B에서 여기까지의 루프는 bucket brigade가 모두 비워질 때 까지 계속된다.)
결과적으로, Apache 내부적으로 생성된 bucket이 mod_cband_filter를 타면서 조각조각 잘라져서, core_filter쪽으로 전달된다는 사실을 알 수 있다. (core_filter가 좀 더 많이 불려지리라.)
mod_cband_cleanup1, 2 함수는 register_hook이 불려질 당시에 입력된 pool에 대해서, 이 pool이 삭제되었을 경우 취할 동작을 등록한다. cleanup1은 해당 pool이 삭제될 경우 취할 액션이고, cleanup2는 chlid에서 접근하려고 할때 불려진다고 한다. (확실치는 않다;)
참고 사이트 : Pool Cleanup Functions(apr 관련 문서로써, apr_pool_cleanup_register 함수에 대해서 설명하고 있다.)
마지막으로, mod_cband는 critical section 구현을 위해 System V IPC의 Semaphore를 사용하고 있다. mod_cband.c의 line 88 - 183이 해당 기능을 제공하는 루틴이다.