はじめに
mod_proxy、mod_proxy_wstunnelに続き、mod_proxyの後段のモジュールであるmod_proxy_balancerについて読んでみたので記事にします。
- mod_proxyの記事:Apacheのmod_proxyのコードを読んでみる(3)
- mod_proxy_wstunnelの記事:Apacheのmod_proxy_wstunnelのコードを読んでみる
実行環境や実装のバージョンはmod_proxyの時と同様にApache v2.4.23です。
いつものように我流で読み進めているので、理解内容に間違いなどありましたらご指摘ください。
mod_proxy_balancerの概要
AP_DECLARE_MODULE(proxy_balancer)
apache標準のhook定義関数です。
mod_proxy_wstunnelと同様に基本的に前段のmod_proxyで登録されているため、hook関数の登録しかしていません。
AP_DECLARE_MODULE(proxy_balancer) = {
STANDARD20_MODULE_STUFF,
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
NULL, /* command apr_table_t */
ap_proxy_balancer_register_hook /* register hooks */
};
register_hooks
hook登録関数で、AP_DECLARE_MODULEの最後に指定されている関数です。
static void ap_proxy_balancer_register_hook(apr_pool_t *p)
{
/* Only the mpm_winnt has child init hook handler.
* make sure that we are called after the mpm
* initializes
*/
static const char *const aszPred[] = { "mpm_winnt.c", "mod_slotmem_shm.c", NULL};
static const char *const aszPred2[] = { "mod_proxy.c", NULL};
/* manager handler */
ap_hook_post_config(balancer_post_config, aszPred2, NULL, APR_HOOK_MIDDLE);
ap_hook_pre_config(balancer_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_handler(balancer_handler, NULL, NULL, APR_HOOK_FIRST);
ap_hook_child_init(balancer_child_init, aszPred, NULL, APR_HOOK_MIDDLE);
proxy_hook_pre_request(proxy_balancer_pre_request, NULL, NULL, APR_HOOK_FIRST);
proxy_hook_post_request(proxy_balancer_post_request, NULL, NULL, APR_HOOK_FIRST);
proxy_hook_canon_handler(proxy_balancer_canon, NULL, NULL, APR_HOOK_FIRST);
}
Apache標準のhookを4個とmod_proxyから呼ばれるhookを3個登録しています。
この中でap_hook_post_configに関しては、第2引数でaszPred2としてmod_proxyが指定されているので、mod_proxyのhookが呼ばれた後のmod_proxy_balancerのhookが呼ばれることになります。
mod_proxyから呼ばれるhookの呼び出し元は次のようになっています。
- proxy_hook_pre_request、proxy_hook_post_request:mod_proxyのproxy_handlerからフックされます
- proxy_hook_canon_handler:mod_proxyのproxy_fixupからフックされます
mod_proxy_balancerの詳細
balancer_pre_config
ap_hook_pre_configのhandlerです。
confファイルから必要な情報を取得するぐらいしかやっていません。
balancer_post_config
ap_hook_post_configのhandlerです。
/* balancer_post_config() will be called twice during startup. So, don't
* set up the static data the 1st time through. */
if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) {
return OK;
}
参考情報を見ていただければ分かるのですが、ap_hook_pre_configとap_hook_post_configは2回呼び出されます。
2回目の呼び出された時に不整合が起こらないように、1回目の呼び出しではOKをreturnしています。
2回目の呼び出しでは、最初にメモリを確保します。(slotmemへshmをprovider機構で設定し各workerごとにshmを確保する)
/*
* Get slotmem setups
*/
storage = ap_lookup_provider(AP_SLOTMEM_PROVIDER_GROUP, "shm",
AP_SLOTMEM_PROVIDER_VERSION);
その後、whileループに入り処理をします。
while文の中身はそれほど読めていませんが、関数名の通りの処理で、mod_proxyが管理する構造体proxy_server_confへ値の設定などをする実装になっていると思います。
参考情報
balancer_child_init
ap_hook_child_initのhandlerです。
内部変数の初期化、共有メモリの取得などをしています。
proxy_balancer_canon
フックされた時点でurlに"balancer"を含んでいないとDECLINEDになり終了です
リクエストに問題がなければ、request_rec構造体のfilenameメンバへ整形したurlを設定し、path_infoへpath情報を設定してOKを返します。
r->filename = apr_pstrcat(r->pool, "proxy:" BALANCER_PREFIX, host,
"/", path, (search) ? "?" : "", (search) ? search : "", NULL);
r->path_info = apr_pstrcat(r->pool, "/", path, NULL);
return OK;
proxy_balancer_pre_request
proxy_hook_pre_requestのhandlerです。
mod_proxyのproxy_handlerからフックされます。
mod_proxy_balancerの肝とも言えるworkerを選定するところです。
前準備
URLがmod_proxy_balancer用であるか('balancer://'で始まるか)を確認します。バランサがすでに提供されている場合は、DECLINEDで終了(フェールオーバーの試行であるため)
ロードバランサをロックします。
workerを強制的にリカバリーして、balancerのメンバーリストを更新します。
そのセッションのルートを見つけます。
find_session_route(セッションと紐づくルートの探索)
static proxy_worker *find_session_route(proxy_balancer *balancer,
request_rec *r,
char **route,
const char **sticky_used,
char **url)
stickysessionはURLまたはcookieで指定することができます。
stickysessionが未指定だった場合は、セッションとrouteを紐づける必要がないためNULLを返します。
stickysessionが指定されていた場合、最初にURLの内容を確認し、未指定であればcookieの内容を確認します。
/* Try to find the sticky route inside url */
*route = get_path_param(r->pool, *url, balancer->s->sticky_path, balancer->s->scolonsep);
if (*route) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01159)
"Found value %s for stickysession %s",
*route, balancer->s->sticky_path);
*sticky_used = balancer->s->sticky_path;
}
else {
*route = get_cookie_param(r, balancer->s->sticky);
if (*route) {
*sticky_used = balancer->s->sticky;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01160)
"Found value %s for stickysession %s",
*route, balancer->s->sticky);
}
}
普通に考えるとcookieを先に見た方が良さそうですが、最初にURLを確認しています。
理由を想像すると、携帯電話などでcookieを使用できないものからアクセスされたケースに備えるためだと思います。
stickysessionに該当する値が取得できた場合、対応するrouteを指定したworkerを返します。
find_session_route(セッションと紐づくルートの探索)結果による分岐
セッションに紐づくworkerが存在する場合
ロードバランサのステータス(lbstatus)を更新します。routeが設定済み、かつsticky_forceが指定されている場合
バランサ名(balancer://の名前)と一致しないルートが用意されています。
ルートが同じバランサのメンバであるかどうかを確認し、該当する場合は503を返して終了です。(エラーケース)セッションに紐づくworkerが存在しない場合
find_best_worker関数を呼び出し、lbmethodのfinderが、その時点でベストなworkerを選定します。
後処理
次の処理をしてstatusを返して終了です。
- urlの書き換え
- 変更前:"balancer://url"
- 変更後:"worker_scheme://worker_hostname[:worker_port]/url"
- request_rec構造体に値の設定
proxy_balancer_post_request
proxy_hook_post_requestのhandlerです。
mod_proxyのproxy_handlerからフックされます。
request後のエラー処理(errorstatusやfailontimeoutの内容による処理)を実行します。
balancer_handler
balancer-manager(ブラウザでロードバランス設定を変更する)の機能性を実現しています。
mod_proxy_balancerのバランシング機能について調べていたので、この関数はほとんど読んでいないです。
おわりに
これまでにmod_proxyに関係する3つのApacheモジュールについて読んだ内容を整理してみました。
- mod_proxy
- mod_proxy_wstunnel
- mod_proxy_balancer
現時点で他のモジュールを読む予定は有りませんが、参考になる方がいれば幸いです。