LoginSignup
0

More than 5 years have passed since last update.

Apacheのmod_proxy_balancerのコードを読んでみる

Posted at

はじめに

mod_proxy、mod_proxy_wstunnelに続き、mod_proxyの後段のモジュールであるmod_proxy_balancerについて読んでみたので記事にします。

実行環境や実装のバージョンは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を選定するところです。

前準備

  1. URLがmod_proxy_balancer用であるか('balancer://'で始まるか)を確認します。バランサがすでに提供されている場合は、DECLINEDで終了(フェールオーバーの試行であるため)

  2. ロードバランサをロックします。

  3. workerを強制的にリカバリーして、balancerのメンバーリストを更新します。

  4. そのセッションのルートを見つけます。

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

現時点で他のモジュールを読む予定は有りませんが、参考になる方がいれば幸いです。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0