7.mod_dbdのデータベースコネクションプールを使用したApacheモジュール
さて、Apache ModuleによるWebアプリケーション作成の最終ステップです。今度はmod_dbdを利用した形にコードを修正します。現時点では画面表示用の文言をコード内に固定で持っていますが、これをデータベースから取得するように作り変えましょう。
この章では以下の手順で作業を進めます。
- データベースの準備
- mod_dbdの導入確認
- httpd.confの修正
- Apache Moduleの修正
- Apache Moduleの動作確認
テスト用データの作成
まずは、Apache Moduleからアクセスするための試験用データベースを準備しておきましょう。
initdb --encoding=UTF-8 --no-locale postmaster& createdb hoge
データベース作成後、自ホスト以外からもデータベースにアクセスできるようPostgreSQLの設定ファイルを変更します。「pg_hba.conf(74行目付近)」と「postgresql.conf(56行目付近)」にそれぞれ以下の内容で追記してください(※この例では 192.168.0.0/16 のネットワークに接続されている全ての端末からのアクセスを許可しています)。
host all all 192.168.0.0/16 trust
listen_addresses = '*'
設定ファイルを修正し終えたらPostgreSQLの再起動を行います。
pg_ctl stop pg_ctl start
データベースが無事起動したことを確認した後、PostgreSQLのGUIツール等(pgAdmin3)を使用してテーブルの作成とデータの登録を行ってください。
create table addrbook (name varchar(50), telno varchar(50), email varchar(50)); insert into addrbook values('FOO', '090-0000-****','aaaaa@hoge.com'); insert into addrbook values('BAR', '080-0000-****','bbbbb@hoge.com'); insert into addrbook values('HOGE', '070-0000-****','ccccc@hoge.com'); insert into addrbook values('MONA', '060-0000-****','ddddd@hoge.com');
mod_dbdの導入確認
Apache Moduleの作成を行う前に、mod_dbdが導入されていることも確認しておきましょう。下記に示すコマンドで、現在使用しているApache HTTP Serverにmod_dbdが組み込まれているか確認することができます。
httpd -M
コマンド実行後、「dbd_module (shared)」が表示されていればmod_dbdは組み込まれています。表示されていない場合は付録を参考にインストールしなおして下さい。
httpd.confの修正
次にmod_dbdを有効にするために「/usr/local/apache22/conf/httpd.conf」の最終行付近に以下の内容を追記し、Apache HTTP Serverを再起動します(※mod_dbdの詳細は「Apache Module mod_dbd」を参照ください)。
DBDriver pgsql
DBDParams "dbname=hoge user=postgres password=postgres"
DBDMin 5
DBDKeep 5
DBDMax 10
DBDExptime 60
Apache Moduleの修正
ここまで準備が済んだところでApache Moduleの修正を行いましょう。表示用データをPostgreSQLから取得するように変更します。
- P4\test\mod_test.c
- P4\test\mod_test.c(コメント記入版)
1 #define ap_psprintf apr_psprintf 2 3 #include "httpd.h" 4 #include "http_config.h" 5 #include "http_protocol.h" 6 #include "ap_config.h" 7 #include "ClearSilver.h" 8 /*mod_dbdに必要なヘッダファイルの読込*/ 9 #include "apr_dbd.h" 10 #include "mod_dbd.h" 11 #include "apr_strings.h" 12 13 14 static NEOERR *render_cb(void *obj, char *s) 15 { 16 request_rec *r = (request_rec *)obj; 17 ap_rputs(s, r); 18 return STATUS_OK; 19 } 20 21 static void output_handler(request_rec *r) 22 { 23 HDF *hdf; 24 CSPARSE *cs; 25 26 int i=0; 27 hdf_init(&hdf); 28 29 30 /*DBからデータを取得した際の状態を保持する変数*/ 31 apr_status_t rv; /*検索の結果セットを保持する変数*/ 32 apr_dbd_results_t *res = NULL; /*結果セットの行情報を保持する変数*/ 33 apr_dbd_row_t *row = NULL; /*DBに対して実行するSQL文*/ 34 char stmt[] = "select name,telno,email from addrbook"; 35 /*mod_dbdコネクションプールからコネクション情報を取得*/ 36 ap_dbd_t *dbd = ap_dbd_acquire(r); 37 if (dbd == NULL) { /*失敗時は処理を中止*/ 38 return; 39 } 40 /*検索処理の実行*/ 41 if (apr_dbd_select (dbd->driver, r->pool, dbd->handle, &res, stmt,1) != 0) 42 { /*失敗時は処理を中止*/ 43 return; 44 } 45 /*結果のレコード数分ループ*/ 46 for (rv = apr_dbd_get_row (dbd->driver, r->pool, res, &row, -1); 47 rv != -1; 48 rv = apr_dbd_get_row (dbd->driver, r->pool, res, &row, -1)) { 49 /*レコードの取得に失敗した場合は処理を中止*/ 50 if (rv != 0) { 51 return; 52 } /*各カラムの情報を取得するためにAPRを使用してメモリ確保*/ 53 char *name = apr_palloc(r->pool,sizeof(char)); /*カラムの情報をメモリに格納*/ 54 name = apr_dbd_get_entry(dbd->driver, row, 0); 55 56 hdf_set_value(hdf, ap_psprintf( r->pool, "csdata.%d.arrayname", i ), name ); 57 i++; 58 59 } 60 61 cs_init(&cs, hdf); 62 cs_parse_file(cs, "/usr/local/src/modtest/test/test.cs"); 63 cs_render(cs, r, render_cb); 64 cs_destroy(&cs); 65 hdf_destroy(&hdf); 66 } 67 68 69 70 /* The sample content handler */ 71 static int test_handler(request_rec *r) 72 { 73 74 if (strcmp(r->handler, "test")) { 75 return DECLINED; 76 } 77 78 r->content_type = "text/html"; 79 80 if (!r->header_only) 81 output_handler(r); 82 return OK; 83 } 84 85 static void test_register_hooks(apr_pool_t *p) 86 { 87 ap_hook_handler(test_handler, NULL, NULL, APR_HOOK_MIDDLE); 88 } 89 90 /* Dispatch list for API hooks */ 91 module test_module = { 92 STANDARD20_MODULE_STUFF, 93 NULL, /* create per-dir config structures */ 94 NULL, /* merge per-dir config structures */ 95 NULL, /* create per-server config structures */ 96 NULL, /* merge per-server config structures */ 97 NULL, /* table of config file commands */ 98 test_register_hooks /* register hooks */ 99 }; 100
ところで、53行目で確保したメモリの廃棄が、その後行われていないことに気づいたでしょうか?
APRは実行環境の隠蔽を行いますが、これは開発時のメモリ管理に関するコーディングについても当てはまります。今回の場合は、メモリはRequestオブジェクトから取得を行い、このRequestオブジェクトが破棄されるときに同時に開放されます。誤解を恐れずに言うと、Javaで言うところのガベッジコレクションに似たこと(※1)をメモリ開放の際にAPRは行ってくれるので、C言語でよくある「malloc()
した際にはfree()
を忘れないこと」をあまり意識しなくてもよくなります。
The Apache Module Book :3.4.2 APR Pools(61ページ)
井上の日誌 -Apacheの話- :https://www.codeblog.org/blog/inoue/200602.html
ちなみに上記コードには記載していませんが、「http_log.h」をインクルードした上で、54行目の記載を下記のように変更すると、ログに検索結果の件数とカラム数が記録されます(※「ap_log_rerror」のAPIは「http_log.h」を参照のこと)。
int rows = apr_dbd_num_tuples(dbd->driver,res); int cols = apr_dbd_num_cols(dbd->driver,res); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,"row count = %d, col count = %d ",rows,cols);
[debug] mod_test.c(49): [client xxx.xxx.xxx.xxx] row count = 50, col count = 3
Apache Moduleの動作確認
ここでまでの作業が済んだら動作確認を行いましょう。Apache停止、mod_testのコンパイル・インストール、Apache再起動という流れで作業を行います。なお、ここでの目的はデータの取得元の変更のみですから、ClearSilverのファイル「test.cs」の修正は行いません。
apachectl stop make clean make all install apachectl start
ブラウザから「http://(ApahceHTTPサーバが動作しているホスト)/test/」にアクセスし、動作を確認しましょう。結果は以下のようになります。