Redis2¶
Name¶
ngx_redis2 - Redis 2.0プロトコルのためのNGINX upstreamモジュール。
注意
このモジュールはNGIXのソースと一緒に配布されません。 インストレーションの説明を見てください。
状態¶
このモジュールは既にプロダクションの準備ができています。
概要¶
location = /foo {
set $value 'first';
redis2_query set one $value;
redis2_pass 127.0.0.1:6379;
}
# GET /get?key=some_key
location = /get {
set_unescape_uri $key $arg_key; # this requires ngx_set_misc
redis2_query get $key;
redis2_pass foo.com:6379;
}
# GET /set?key=one&val=first%20value
location = /set {
set_unescape_uri $key $arg_key; # this requires ngx_set_misc
set_unescape_uri $val $arg_val; # this requires ngx_set_misc
redis2_query set $key $val;
redis2_pass foo.com:6379;
}
# multiple pipelined queries
location = /foo {
set $value 'first';
redis2_query set one $value;
redis2_query get one;
redis2_query set one two;
redis2_query get one;
redis2_pass 127.0.0.1:6379;
}
location = /bar {
# $ is not special here...
redis2_literal_raw_query '*1\r\n$4\r\nping\r\n';
redis2_pass 127.0.0.1:6379;
}
location = /bar {
# variables can be used below and $ is special
redis2_raw_query 'get one\r\n';
redis2_pass 127.0.0.1:6379;
}
# GET /baz?get%20foo%0d%0a
location = /baz {
set_unescape_uri $query $query_string; # this requires the ngx_set_misc module
redis2_raw_query $query;
redis2_pass 127.0.0.1:6379;
}
location = /init {
redis2_query del key1;
redis2_query lpush key1 C;
redis2_query lpush key1 B;
redis2_query lpush key1 A;
redis2_pass 127.0.0.1:6379;
}
location = /get {
redis2_query lrange key1 0 -1;
redis2_pass 127.0.0.1:6379;
}
説明¶
これはNGINXにRedis 2.x サーバにブロッキング無しで通信させるNGINXアップストリームモジュールです。完全なRedis 2.0統合プロトコルはRedisパイプラインサポートを含めて実装されています。
Luaと接続するために使われる場合は、lua-resty-redis <openresty/lua-resty-redis>ライブラリが柔軟でメモリが効果的なため、このモジュールの代わりに使うことをお勧めします。
get
redis コマンドを使いたいだけの場合は、HTTP Redisを試してみることができます。実装するには get
だけが必要なため、Redis応答のパースされたコンテント部分を返します。
他の選択はクライアント側で自分自身でredis応答をパースすることです。
ディレクティブ¶
redis2_query¶
構文: | redis2_query <cmd> [arg1] [arg2]... |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
redis-cli
ユーティリティと似たやり方で個々の引数(Redisコマンド名自身も含む)を指定することがで、Redisコマンドを指定する。
一つのlocationにこのディレクティブの複数のインスタンスを置くことができ、これらのクエリはパイプラインされるでしょう。例えば:
location = /pipelined {
redis2_query set hello world;
redis2_query get hello;
redis2_pass 127.0.0.1:$TEST_NGINX_REDIS_PORT;
}
そして、GET /pipelined
は連続する2つの生のRedis応答をもたらすでしょう。
+OK
$5
world
while newlines here are actually CR LF
(\r\n
).
redis2_raw_query¶
構文: | redis2_raw_query QUERY |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
Specify raw Redis queries and NGINX variables are recognized in the QUERY
argument.
QUERY
引数には 1つだけの Redis コマンドが許されます。そうでなければエラーを受け取るでしょう。一つのクエリ内で複数のパイプラインされたコマンドを指定したい倍アは、代わりにredis2_raw_queries ディレクティブを使ってください。
redis2_raw_queries¶
構文: | redis2_raw_queries N QUERIES |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
QUERIES
引数にN
コマンドを指定します。N
および QUERIES
引数はNGINX変数を取ることができます。
幾つかの例です:
location = /pipelined {
redis2_raw_queries 3 "flushall\r\nget key1\r\nget key2\r\n";
redis2_pass 127.0.0.1:6379;
}
# GET /pipelined2?n=2&cmds=flushall%0D%0Aget%20key%0D%0A
location = /pipelined2 {
set_unescape_uri $n $arg_n;
set_unescape_uri $cmds $arg_cmds;
redis2_raw_queries $n $cmds;
redis2_pass 127.0.0.1:6379;
}
注意
上の2つ目の例の中で、set_unescape_uri
ディレクティブはSet Miscによって提供されました。
redis2_literal_raw_query¶
構文: | redis2_literal_raw_query QUERY |
---|---|
デフォルト: | none |
コンテキスト: | location, location if |
生のRedisクエリを指定しますが、その中のNGINX変数は理解されないでしょう。別の言い方をすると、QUERY
引数の中で自由にドル記号文字($
) を使っても構いません。
QUERY
引数には、1つのredisコマンドだけが許されます。
redis2_pass¶
構文: | redis2_pass <upstream_name> |
---|---|
構文: | redis2_pass <host>:<port> |
デフォルト: | none |
コンテキスト: | location, location if |
Phase: | content |
Redisサーバのバックエンドを指定します。
redis2_connect_timeout¶
構文: | redis2_connect_timeout <time> |
---|---|
デフォルト: | 60s |
コンテキスト: | http, server, location |
Redisサーバへの接続のタイムアウト。デフォルトでは秒数です。
混乱を避けるために常に明示的に時間単位を設定するのが懸命です。時間単位は、s
(seconds), ms
(milliseconds), y
(years), M
(months), w
(weeks), d
(days), h
(hours) および m
(minutes) です。
この時間は597時間未満に設定すべきです。
redis2_send_timeout¶
構文: | redis2_send_timeout <time> |
---|---|
デフォルト: | 60s |
コンテキスト: | http, server, location |
RedisサーバにTCPリクエストを送信するためのタイムアウト。デフォルトでは秒数。
混乱を避けるために常に明示的に時間単位を設定するのが懸命です。時間単位は、s
(seconds), ms
(milliseconds), y
(years), M
(months), w
(weeks), d
(days), h
(hours) および m
(minutes) です。
redis2_read_timeout¶
構文: | redis2_read_timeout <time> |
---|---|
デフォルト: | 60s |
コンテキスト: | http, server, location |
redisサーバからTCP応答を受信するためのデフォルトで秒のタイムアウト。
混乱を避けるために常に明示的に時間単位を設定するのが懸命です。時間単位は、s
(seconds), ms
(milliseconds), y
(years), M
(months), w
(weeks), d
(days), h
(hours) および m
(minutes) です。
redis2_buffer_size¶
構文: | redis2_buffer_size <size> |
---|---|
デフォルト: | 4k/8k |
コンテキスト: | http, server, location |
このバッファサイズはRedis応答を読み込むために使われますが、Redisが応答できる最大のサイズにする必要はありません。
デフォルトのサイズはページサイズで、おそらく4kあるいは8kです。
redis2_next_upstream¶
構文: | redis2_next_upstream [ error | timeout | invalid_response | off ] |
---|---|
デフォルト: | error timeout |
コンテキスト: | http, server, location |
どの失敗条件が他のupstreamサーバへの転送へのリクエストを発生させるべきかを指定します。redis2_passの値が2つ以上のサーバのupstreamの時のみ適用されます。
恣意的な例は以下のようになります:
upstream redis_cluster {
server 127.0.0.1:6379;
server 127.0.0.1:6380;
}
server {
location = /redis {
redis2_next_upstream error timeout invalid_response;
redis2_query get foo;
redis2_pass redis_cluster;
}
}
接続プール¶
RedisのためのTCPコネクションプールを提供するためにこのモジュールを使って洗練されたkeepaliveを使うことができます。
例の設定の断片は以下のように見えます:
http {
upstream backend {
server 127.0.0.1:6379;
# a pool with at most 1024 connections
# and do not distinguish the servers:
keepalive 1024;
}
server {
...
location = /redis {
set_unescape_uri $query $arg_query;
redis2_query $query;
redis2_pass backend;
}
}
}
Luaの相互運用性¶
このモジュールは Luaのための非ブロッキングredis2クライアントとして使うことができます (しかし、今では代わりにlua-resty-redisライブラリを使うことをお勧めします。これはほとんどの場合において使いやすく効果的です)。以下はGETサブリクエストを使う例です:
location = /redis {
internal;
# set_unescape_uri is provided by ngx_set_misc
set_unescape_uri $query $arg_query;
redis2_raw_query $query;
redis2_pass 127.0.0.1:6379;
}
location = /main {
content_by_lua '
local res = ngx.location.capture("/redis",
{ args = { query = "ping\\r\\n" } }
)
ngx.print("[" .. res.body .. "]")
';
}
そして/main
にアクセスすると以下を得ます
[+PONG\r\n]
ここで\r\n
は CRLF
です。つまり、このモジュールは遠隔のredisサーバから生の TCP 応答を返します。
インラインのLuaコードを外部の.lua
ファイルに移動する場合、エスケープシーケンス\r\n
を直接使用することが重要です。NGINXの文字列リテラルに入れる場合にLuaコード自身をクォートする必要があるため、上では\\r\\n
を使いました。
生のRedisリクエストをリクエストボディを使って転送するためにPOST/PUTサブリクエストを使うこともできます。これはURIのエスケープおよびアンエスケープを必要としないため、幾分CPUサイクルを節約します。以下はそのような例です:
location = /redis {
internal;
# $echo_request_body is provided by the ngx_echo module
redis2_raw_query $echo_request_body;
redis2_pass 127.0.0.1:6379;
}
location = /main {
content_by_lua '
local res = ngx.location.capture("/redis",
{ method = ngx.HTTP_PUT,
body = "ping\\r\\n" }
)
ngx.print("[" .. res.body .. "]")
';
}
これは、以前(GET)の例のように完全に同じ出力をもたらすでしょう。
幾分複雑なハッシュルールに基づいて具体的なRedisバックエンドをピックアップするようにLuaを使うこともできます。例えば、
upstream redis-a {
server foo.bar.com:6379;
}
upstream redis-b {
server bar.baz.com:6379;
}
upstream redis-c {
server blah.blah.org:6379;
}
server {
...
location = /redis {
set_unescape_uri $query $arg_query;
redis2_query $query;
redis2_pass $arg_backend;
}
location = /foo {
content_by_lua "
-- pick up a server randomly
local servers = {'redis-a', 'redis-b', 'redis-c'}
local i = ngx.time() % #servers + 1;
local srv = servers[i]
local res = ngx.location.capture('/redis',
{ args = {
query = '...',
backend = srv
}
}
)
ngx.say(res.body)
";
}
}
Luaによってパイプライン化されたRedisリクエスト¶
このNGINXモジュールを使って複数のパイプライン化されたRedisのリクエストを発行するためにLuaを使用する方法の完全な実証例です。
まずは、以下をnginx.conf
ファイルを含みます:
location = /redis2 {
internal;
redis2_raw_queries $args $echo_request_body;
redis2_pass 127.0.0.1:6379;
}
location = /test {
content_by_lua_file conf/test.lua;
}
Basically we use URI query args to pass the number of Redis requests and request body to pass the pipelined Redis request string.
そして、以下のLuaコードを含むために、conf/test.lua
ファイルを作成します (このパスはNGINXのサーバrootへの相対です):
-- conf/test.lua
local parser = require "redis.parser"
local reqs = {
{"set", "foo", "hello world"},
{"get", "foo"}
}
local raw_reqs = {}
for i, req in ipairs(reqs) do
table.insert(raw_reqs, parser.build_query(req))
end
local res = ngx.location.capture("/redis2?" .. #reqs,
{ body = table.concat(raw_reqs, "") })
if res.status ~= 200 or not res.body then
ngx.log(ngx.ERR, "failed to query redis")
ngx.exit(500)
end
local replies = parser.parse_replies(res.body, #reqs)
for i, reply in ipairs(replies) do
ngx.say(reply[1])
end
ここで、Redisサーバがlocalhostのデフォルトのポート(6379)でlistenしていると仮定します。
curl
のようなHTTPクライアントを使って/test
location にアクセスすると、以下の出力を得ます。
OK
hello world
もっと現実的な設定はRedisバックエンドのための適切なupstream定義を使うことで、その中でkeepalive ディレクティブを使ってTCP接続プールを有効にします。
Redis Publish/Subscribe サポート¶
このモジュールはRedis publish/subscribe 機能のための制限されたサポートを持ちます。RESTおよびHTTPモデルのステートレス特性により、完全にはサポートされません。
次の例を考えます:
location = /redis {
redis2_raw_queries 2 "subscribe /foo/bar\r\n";
redis2_pass 127.0.0.1:6379;
}
そして、redis-cli
コマンドライン内の /foo/bar
キーのためのメッセージをpublishします。そして、/redis
locationから2つのmulti-bulk応答を受け取るでしょう。
Redis Publish/Subscribe の制限¶
このモジュールを使ってRedis pub/sub 機能を使いたい場合は、以下の制限に注意する必要があります:
- このRedis upstreamを使ってkeepaliveを使うことができません。短い Redis 接続のみが動作するでしょう。
- There may be some race conditions that produce the harmless
Redis server returned extra bytes
warnings in your NGINX’s error.log. そのような警告は滅多にないですが、それに備えます。 - このモジュールによって提供される redis2_connect_timeout および redis2_read_timeoutのような様々なタイムアウト設定を調整しなければなりません。
それらの制限に耐えられない場合は、Luaのためのlua-resty-redis ライブラリに切り替えることをお勧めします。
パフォーマンス調整¶
- このモジュールを使っている場合は、できる限りTCP接続プール(keepaliveによって提供される)とRedisパイプラインを使うようにしてください。これらの機能はとてもパフォーマンスを改善するでしょう。
- 一つのRedisサーバインスタンスの連続する処理特性のために、マルチコアのマシーン上で複数のRedisサーバのインスタンスを使うこともとても役に立つでしょう。
ab
またはhttp_load
のようなものを使ってパフォーマンスをベンチマークしている場合は、NGINXワーカーがerror.log
にフラッシュするのに多くのサイクルを消費するのを避けるために、エラーログのレベルが(warn
のように)十分高くするようにしてください。error.logは常にバッファされずブロックされ、従って高価なものになります。
インストール
(NGINXコアおよび他の多くの良いものと同じく)このモジュールをngx_openresty bundleを使ってインストールすることをお勧めします。ngx_openrestyをセットアップするためにインストールの説明 を調べてください。
別のやり方として、以下のように標準のNGINXコアを再コンパイルすることでこのモジュールを手動でインストールすることができます:
そして、ngx_redis2の ファイルリストからこのモジュールのリリースtarballの最新バージョンをダウンロードします。
そして、最後にこのモジュールを使ってソースをビルドします:
wget 'http://nginx.org/download/nginx-1.7.4.tar.gz' tar -xzvf nginx-1.7.4.tar.gz cd nginx-1.7.4/ # Here we assume you would install you nginx under /opt/nginx/. ./configure --prefix=/opt/nginx \ --add-module=/path/to/redis2-nginx-module make -j2 make install
互換性¶
- Redis 2.0, 2.2, 2.4 以上は何も問題無く、このモジュールで動作するでしょう。Alchemy Databaseもそうです。
- (aka redisql in its early days).
NGINXの以下のバージョンがこのモジュールで動作するはずです:
- 1.7.x (最後のテスト: 1.7.4)
- 1.6.x
- 1.5.x (最後のテスト: 1.5.12)
- 1.4.x (最後のテスト: 1.4.3)
- 1.3.x (最後のテスト: 1.3.7)
- 1.2.x (最後のテスト: 1.2.7)
- 1.1.x (最後のテスト: 1.1.5)
- 1.0.x (最後のテスト: 1.0.10)
- 0.9.x (最後のテスト: 0.9.4)
- 0.8.x >= 0.8.31 (最後のテスト: 0.8.54)
NGINXの前のバージョンは動作しないでしょう。
0.8.31以上のNGINXの特定のバージョンでこのモジュールが動作しないことを見つけたら、バグの報告を考えてみてください。
コミュニティ¶
英語のメーリングリスト¶
openresty-en メーリングリストは英語を話す人のためのものです。
バグとパッチ¶
バグレポート、欲しいもののリスト、あるいはパッチを下記でサブミットしてください
- GitHub Issue Tracker上でチケットを作成
- あるいは、OpenResty communityにポスト。
ソースリポジトリ¶
github上のopenresty/redis2-nginx-moduleで利用可能です。
TODO¶
- JSONを直接発行できるように
redis2_as_json
ディレクティブを追加します。
Author¶
Yichun “agentzh” Zhang (章亦春) <agentzh@gmail.com>, CloudFlare Inc.
Getting involved¶
Authorにパッチをサブミット、あるいはGitHub上のSource Repositoryに少しコミットするように依頼することをとても歓迎します。
Copyright & License¶
このモジュールはBSDライセンスのもとにライセンスされます。
Copyright (C) 2010-2014, by Yichun “agentzh” Zhang (章亦春) <agentzh@gmail.com>, CloudFlare Inc.
無断転載禁ず
修正の如何に関係なくソースおよびバイナリ形式での再配布および使用は以下の条件に合う限り許可されます:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
以下も見てください¶
- Redisサーバのホームページ。
- The Redis wire protocol: http://redis.io/topics/protocol
- Lua
- ngx_openresty バンドル。
- Lua cosocket API に基づいたlua-resty-redis ライブラリ。