blog.hamadu.net

2017/07/02 - memcachedとC10K問題

Tags: web memcached

調べ物。(途中)

ファイルIOのイベントハンドル機能のラッパーライブラリ、libevent

ファイルディスクリプタを監視する。

memcachedのソースを読む(v1.4.38)

memcached.c

  • main関数 : int main (int argc, char **argv)

    • くっそ長いけど大半はオプションパラメータのパースと初期設定。
    • 6606行目libevent のインスタンス(main_base)を作って、
      • その後event_base_loop(main_base, 0) に渡してイベントループに入る。
        • event_init, event_base_looplibevent のあれ。詳しくはドキュメントを。
    • L6663 ソケット作ってbind。その後、server_sockets(port, tcp_transport, portnumber_file)
  • server_sockets : server_sockets

    • なんかパースして server_socket() を呼んでる(小並感)
  • server_socket : server_socket(interface, port, transport, portnumber_file)

    • sfd(Socket file descriptorの略かな) を new_socket() からつくる。中身は socket(2) を読んでる。
    • その後、その sfd に対して bind(2)
      • UDPの場合、設定値の数だけ sfd を dup(2)。その後、dispatch_conn_new(per_thread_fd, conn_read, ...) を呼ぶ。
      • TCPの場合、conn_new(sfd, conn_listening, EV_READ | EV_PERSIST, 1, transport, main_base) を呼ぶ。main_base を渡していることに注目。
  • conn_new

    • c = conns[sfd]; SFDに対するコネクションを取って、NULLなら初期化。
      • c->rbuf, c->wbuf をmalloc。
    • STATS_LOCK() の中身は pthread_mutex_lock(&stats_lock); 共通のstatを触るときはロックを獲得する。
    • このあたり からイベント周りの設定。
      • event_set(), event_base_set(), event_add()
      • コネクションごとにイベントハンドラ(event_handler())を設定。
  • event_handler

    • drive_machineを呼んでるだけ。c->which = which ってあるけど、which ってなんだろう。
  • drive_machine
    • コネクションのstateごとにいろんなことをする。
    • conn_listening
      • sfdに対して accept
        • なるほど、接続要求が来ると sfd読み込み可能になる。 そのイベントを select なり poll なり epoll (もしくはそれらをラップした libevent) で受け取ってやればいいわけだ。
      • 接続が上限に引っかかってたら、Too many open connnections を sfd に書き込み、closeする。
      • 大丈夫なら、dispatch_conn_new(sfd, conn_new_cmd, ...) を呼ぶ。
    • conn_waiting
      • conn_set_state(c, conn_read)`
    • conn_parse_cmd
      • try_read_command(c) でコマンドをパース?データが全部来ていないなら、conn_waiting に戻す。
    • conn_new_cmd
    • conn_nread
      • read を呼んで c->ritem にデータを貯めようとする。
    • conn_swallow
      • ??
    • conn_mwrite
      • transmit(c) を呼んで c->msglist[c->msgcurr] を書き込み処理?
    • conn_closing
  • try_read_command

    • コマンドをパースして、process_command() または dispatch_bin_command() を呼ぶ。
  • process_command

    • コマンド文字列を受け取って、内容に応じて process_xxx_command(c, tokens, ...) を呼ぶ。
  • process_get_command

    • 実際の仕事は it = limited_get(key, nkey, c); の部分っぽい。
      • と思ったら中では item_get(key, nkey, c, DO_UPDATE) を呼んでた。
    • 正しく取れたら状態を conn_mwrite にして書き込ませる。

一旦 process_get_command に限定して処理を追っていこう。

thread.c, items.c

  • item_get
    • まず、キーと長さからハッシュ値を計算する。
      • hash(key, nkey) が呼ばれてる。中身は hash.c にあり、設定によって jenkinsmurmur3 のどちらかが選ばれる。
    • do_item_get を呼び出してハッシュ値に対応するitemを探す。
  • do_item_get
    • assoc_find() から item を探す。
      • あったら
      • 無かったら

assoc.c

ハッシュテーブルの本体実装。

  • assoc_find
    • hv に対応するバケットから key が memcmp で一致するものを探して返す。