AND OR  

このページでは主に Apache Module 関連のメモを書いています。

環境構築

CentOS 5.8 (x86_64) 上で開発します。
yum で httpd (2.2.x系) と httpd-devel をインストールしておきます。
僕の場合は以下のような環境になっています。

$ httpd -V
Server version: Apache/2.2.17 (Unix)
Server built:   Dec 17 2010 11:58:46
Server's Module Magic Number: 20051115:25
Server loaded:  APR 1.3.12, APR-Util 1.3.9
Compiled using: APR 1.3.12, APR-Util 1.3.9
Architecture:   64-bit
Server MPM:     Prefork
  threaded:     no
    forked:     yes (variable process count)
Server compiled with....
 -D APACHE_MPM_DIR="server/mpm/prefork"
 -D APR_HAS_SENDFILE
 -D APR_HAS_MMAP
 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
 -D APR_USE_SYSVSEM_SERIALIZE
 -D APR_USE_PTHREAD_SERIALIZE
 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
 -D APR_HAS_OTHER_CHILD
 -D AP_HAVE_RELIABLE_PIPED_LOGS
 -D DYNAMIC_MODULE_LIMIT=128
 -D HTTPD_ROOT="/etc/httpd"
 -D SUEXEC_BIN="/usr/sbin/suexec"
 -D DEFAULT_PIDLOG="logs/httpd.pid"
 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
 -D DEFAULT_LOCKFILE="logs/accept.lock"
 -D DEFAULT_ERRORLOG="logs/error_log"
 -D AP_TYPES_CONFIG_FILE="conf/mime.types"
 -D SERVER_CONFIG_FILE="conf/httpd.conf"
$ httpd -l
Compiled in modules:
  core.c
  prefork.c
  http_core.c
  mod_so.c

Apache Module の作成

hello_module の作成手順。

$ apxs -g -n hello
Creating [DIR]  hello
Creating [FILE] hello/Makefile
Creating [FILE] hello/modules.mk
Creating [FILE] hello/mod_hello.c
Creating [FILE] hello/.deps
$ cd hello
$ make
$ sudo make install

mod_hello.conf の作成

LoadModule hello_module modules/mod_hello.so
<Location /hello>
  SetHandler hello
</Location>

動作確認

作成した mod_hello.conf をApacheのconfディレクトリにコピー後、Apacheを再起動。

$ sudo cp ./mod_hello.conf /etc/httpd/conf.d/
$ sudo apachectl configtest
$ sudo apachectl restart

/hello にアクセスして

The sample page from mod_hello.c

と表示されればOKです。

C++でのモジュール開発

C++でApacheモジュールを開発する際に気をつける点のひとつに、生成したインスタンスのリソース解放処理があります。
基本的に以下のような手順でリソース解放処理を記述します。

拡張モジュールについて

mod_deflate

コンテンツを圧縮してクライアントに返すモジュール。Apache モジュール mod_deflate
圧縮するためCPU資源は使いますが、レスポンスのサイズはかなり小さくなります。
rest-term.com に設定していたディレクティブは以下のとおり (現在はApacheからNginxに移行しています)。

# DEFLATEフィルタをセット
SetOutputFilter DEFLATE

# 一部のブラウザで圧縮の制限を行う
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html

# 画像は元々圧縮されているのでCPU資源節約のため圧縮しない
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|ico)$ no-gzip dont-vary

# DEFLATEフィルタをかける MIME-type を指定
# text/plain text/html ... のように1行で続けて書いてもOK
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/atom_xml
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/x-httpd-php

mod_dbd

データベースへのコネクション管理機能を提供するモジュール。
mod_dbd manages SQL database connections using APR.
Apache Module mod_dbd

mod_dbd はコネクション管理のみを行うモジュールなので公開されているのは以下の関数のみ。
内部で apr_dbd を用いてコネクションプール機能を提供するラッパーモジュールとなっていて、
データベースへの各問い合わせは apr_dbd のAPIを利用します。
DBD routines

構築に際して、古いバージョンのApacheにはMySQL用のドライバ(apr_dbd_mysql.c)が同梱されていないため別途追加が必要です。

ディレクティブ

  • ''DBDExptime''
    データベース接続のアイドル時間
    デフォルト値 300
  • ''DBDKeep''
    データベース接続のオフピーク接続数
    デフォルト値 2
  • ''DBDMax''
    データベース接続の最大接続数
    デフォルト値 10
  • ''DBDMin''
    データベース接続の初期接続数
    デフォルト値 1
  • ''DBDParams''
    データベースの接続パラメータ
    MySQLの場合は、host, port, user, pass, dbname を指定します。
    例) "host=localhost port=3306 user=foo pass=bar dbname=dbd_test"
  • ''DBDPersist''
    データベース接続にコネクションプールを利用するか
    Apache 2.2.2から引数の形式は 1/0 ではなく On/Off で指定
    デフォルト値はないため、明示的にきちんと指定しないと
    "seg fault or similar nasty error detected in the parent process"
    と言われて落ちます。configtest でも怒られないので気付きにくいです。
  • ''DBDPrepareSQL''
    プリペアドステートメントの定義
  • ''DBDriver''
    ドライバの指定
    MySQLなら "mysql", PostgresSQLなら "pgsql" と指定します。

サンプル

mod_dbd を用いたサンプルモジュール(dbdtest_module)を作成します。
対象のテーブル(user)は以下のような簡素なユーザーテーブルです。

''id''''name''''age''
1foo25
2bar26
  1. ap_dbd_acquire 関数を用いてコネクションハンドラを取得
    (ap_dbd_t 構造体の中の apr_dbd_t がコネクションハンドラ)
  2. apr_dbd の各APIを用いてデータベースを操作
    型名が ap_dbd だったり、apr_dbd だったり紛らわしいので注意しましょう。。
    r が付いていないのが mod_dbd が提供するAPIです。
  • 出力結果
    The sample page from mod_dbdtest.c
    DBD driver name is mysql
    id: 1, name: foo, age: 25
    id: 2, name: bar, age: 26
  • 問題点(?
    普通のSQL文を発行する場合は問題なかったけど、ストアドプロシージャをコールすると、
    "Error 1312 (0A000): PROCEDURE '''proc_name''' can't return a result set inn the given context."
    というエラーが発生した。要調査。