2012年12月23日日曜日

TwemproxyからElastiCacheに分散(同じキーは同じElastiCacheへ)してみる

スズキです。

下記とおり、ようやくVPC上にElastiCacheがきてくれましたが、
VPC上のElastiCacheを試してみた
やはり、キャッシュクラスタはAZをまたがることができず、また、ノード毎に
エンドポイントが用意されているので、キーの分散(同じキーは同じサーバへ)は
自分で行う必要があります。

この"キーの分散"をアプリ側でやるのではなく、インフラ(ミドルウェア)で
吸収できないかなー?と調べてみたら、下記のようなプロダクトがありました。
twemproxy (nutcracker)

twemproxy (pronounced "two-em-proxy"), aka nutcracker
is a fast and lightweight proxy for memcached and redis protocol.
It was primarily built to reduce the connection count
on the backend caching servers.

"twemproxy"(トゥーエンプロキシーと発音)は"nutcracker"とも呼ばれ、
"memcached"と"redis"のプロトコルのための高速で軽量なプロキシーです。
バックエンド(キャッシュ)サーバへの接続数を減少させるために作られました。
まあ文字通り、Twitterでも使われているプロダクトのようです。

そして特長の一つに下記が挙がっています。
Supports multiple hashing modes including consistent hashing and distribution.

"Consistent Hashing"に分散を含む多くのハッシュ方法をサポートしています。
"Consistent Hashing"に関しては下記が非常によく解説されており、一読をオススメします。
第4回 memcachedの分散アルゴリズム:memcachedを知り尽くす
要は"twemproxy"を使えばキーの分散(同じキーは同じサーバへ)をミドルウェアレベルで
実現可能となります。

ということで、下記のような環境を構築してみますが、ElastiCacheの構築は最初に
紹介したブログ記事で構築できるので、今回はEC2上に"twemproxy"を
インストールするところから始めます。


Twemproxyをインストールするための準備


Twemproxyをインストールするときに、CentOS(6.3)の"yum"でインストールできる
"autoconf"はバージョンが古いと言われてしまうので、その周辺も含め、最新版を
インストールします。

"autoconf"のインストール
# curl -OL http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz
# tar xvzf autoconf-2.69.tar.gz 
# cd autoconf-2.69
# ./configure 
# make
# make install
"automake"のインストール
# curl -OL http://ftp.gnu.org/gnu/automake/automake-1.12.tar.gz
# tar xvzf automake-1.12.tar.gz 
# cd automake-1.12
# ./configure 
# make
# make install
"libtool"のインストール
# curl -OL http://ftp.jaist.ac.jp/pub/GNU/libtool/libtool-2.4.2.tar.gz
# tar xvzf libtool-2.4.2.tar.gz 
# cd libtool-2.4.2
# ./configure 
# make
# make install

Twemproxyをインストール

# curl -OL https://github.com/twitter/twemproxy/archive/v0.2.2.zip
# unzip v0.2.2.zip
# cd twemproxy-0.2.2/
# /usr/local/bin/autoreconf -fvi
# ./configure
# make
# make install

Twemproxyの設定と起動


まず、下記のような設定ファイルを作成します。

nutcracker.yml
cache:
  listen: 0.0.0.0:11211
  hash: fnv1a_64
  distribution: ketama
  servers:
    - 10.100.64.217:11211:1
    - 10.100.64.190:11211:1
    - 10.100.65.53:11211:1
    - 10.100.65.181:11211:1
    - 10.100.66.110:11211:1
    - 10.100.66.56:11211:1
  • listen
    • IPアドレスとポートを指定します。
  • hash
    • ハッシュ方法を指定します。(何が何かはよくわかりません...)
  • distribution
    • 分散方法を指定します。(ketamaが"Consistent Hashing"です)
  • servers
    • "サーバ:ポート:重み"で指定します。ただし"サーバ"はDNS名が
      利用できなかったのでIPアドレスを引いて指定しています。
次に、下記で"Twemproxy"を起動します。
/usr/local/bin/nutcracker -d -i 1000 -c ./nutcracker.yml
  • -d
    • デーモンとして起動します。
  • -i
    • 統計情報を集めるインターバルです。
  • -c
    • 利用する設定ファイルです。
IPアドレスで指定してしまうと変更してしまったときに下記で紹介したような対応が
必要になります。
HAProxyをDNS名で指定したバックエンドのIPアドレスが変わったらリロードする

動作確認


一度保存した値は、取得するときも同じサーバから取得できていることがわかります。
# telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
set suz1 0 0 4
suz1
STORED
get suz1
VALUE suz1 0 4
suz1
END
get suz1
VALUE suz1 0 4
suz1
END
set suz2 0 0 4
suz2
STORED
get suz2
VALUE suz2 0 4
suz2
END
get suz2
VALUE suz2 0 4
suz2
END
また統計情報をみることで、キーによって、いくつかのサーバに分散されていることも
わかります。
# telnet 127.0.0.1 22222
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
{
    "cache": {
        "10.100.64.190:11211:1": {
            "in_queue": 0, 
            "in_queue_bytes": 0, 
            "out_queue": 0, 
            "out_queue_bytes": 0, 
            "request_bytes": 23, 
            "requests": 4, 
            "response_bytes": 10, 
            "responses": 4, 
            "server_connections": 1, 
            "server_eof": 0, 
            "server_err": 0, 
            "server_timedout": 0
        }, 
        "10.100.64.217:11211:1": {
            "in_queue": 0, 
            "in_queue_bytes": 0, 
            "out_queue": 0, 
            "out_queue_bytes": 0, 
            "request_bytes": 51, 
            "requests": 3, 
            "response_bytes": 69, 
            "responses": 3, 
            "server_connections": 1, 
            "server_eof": 0, 
            "server_err": 0, 
            "server_timedout": 0
        }, 
        "10.100.65.181:11211:1": {
            "in_queue": 0, 
            "in_queue_bytes": 0, 
            "out_queue": 0, 
            "out_queue_bytes": 0, 
            "request_bytes": 0, 
            "requests": 0, 
            "response_bytes": 0, 
            "responses": 0, 
            "server_connections": 0, 
            "server_eof": 0, 
            "server_err": 0, 
            "server_timedout": 0
        }, 
        "10.100.65.53:11211:1": {
            "in_queue": 0, 
            "in_queue_bytes": 0, 
            "out_queue": 0, 
            "out_queue_bytes": 0, 
            "request_bytes": 0, 
            "requests": 0, 
            "response_bytes": 0, 
            "responses": 0, 
            "server_connections": 0, 
            "server_eof": 0, 
            "server_err": 0, 
            "server_timedout": 0
        }, 
        "10.100.66.110:11211:1": {
            "in_queue": 0, 
            "in_queue_bytes": 0, 
            "out_queue": 0, 
            "out_queue_bytes": 0, 
            "request_bytes": 96, 
            "requests": 7, 
            "response_bytes": 155, 
            "responses": 7, 
            "server_connections": 1, 
            "server_eof": 0, 
            "server_err": 0, 
            "server_timedout": 0
        }, 
        "10.100.66.56:11211:1": {
            "in_queue": 0, 
            "in_queue_bytes": 0, 
            "out_queue": 0, 
            "out_queue_bytes": 0, 
            "request_bytes": 21, 
            "requests": 2, 
            "response_bytes": 5, 
            "responses": 2, 
            "server_connections": 1, 
            "server_eof": 0, 
            "server_err": 0, 
            "server_timedout": 0
        }, 
        "client_connections": 0, 
        "client_eof": 0, 
        "client_err": 3, 
        "forward_error": 0, 
        "fragments": 3, 
        "server_ejects": 0
    }, 
    "service": "nutcracker", 
    "source": "ip-10-100-0-236", 
    "timestamp": 1356201522, 
    "uptime": 17075, 
    "version": "0.2.2"
}
Connection closed by foreign host.

次はELB(internal)と絡めてみよう。
--------
http://www.suz-lab.com

0 コメント: