nginx-lua-moduleでLuaスクリプト書いてみた

プロダクトでWebサーバ上の現在時刻出したい要件がありました。
これぐらいの処理ならアプリケーション介さずともnginxだけでやりたいですね。
現在時刻ヘッダーにつけるならnginx標準機能で出来そうなんですが、
深淵なる理由からコンテンツとして欲しいとのこと。


今後、Nginx+Lua試してみたいなーという要件もありましたし、
せっかくなのでこの機能をLuaスクリプトで書いてみることにしました。


NginxでLuaを動かす環境

まず環境整えるのが若干大変でした。*1
以下のような手順でインストールするようです。

ライブラリのパスなど気をつけないとハマるかもしれませんね。

nginxレシピ

opscodeのnginxクックブックにはluaをインストールするレシピもありました。
しかし、例のごとくそのままでは動かせなかったので
一部オーバーライドしたクックブックを書いてみました。
AWSの生AMIにこれをそのままプロビジョニングするとNginx+Luaが起動することを確認しています。
https://github.com/toritori0318/chef-nginx-lua-sample

直したところ

recipes/lua.rbにパッチを当てたのと、
recipes/source.rbluaをインストールする処理を差し込んだくらいです。
ちなみにcjsonを入れているのは、今回json形式でレスポンスさせたかったからです。
Luaを動かすだけであればcjsonを入れる必要はありません。

サンプルアプリ仕様

  • /current_timeにアクセスすると、plainテキストでdatetimeが返却される
  • df=epoch が指定されていると、エポック秒が返却される
  • rf=json が指定されていると、json形式で返却される

サンプル実装

/etc/nginx/sites-available/lua_current_time

nginxのコンフィグです。
特に語るところはありません。

# Lua soライブラリのパス指定。今回はcjsonのため(ファイルパターンも指定が必要らしい)
lua_package_cpath "/usr/local/lib/lua/5.1/?.so";

server {
  listen   80;

  access_log  /var/log/nginx/access.log;

  location /current_time {
    # 外部Luaスクリプト読み込み
    content_by_lua_file /etc/nginx/lua/current_time.lua;
  }

  location / {
    default_type 'text/plain';
    # コードもそのまま書ける
    content_by_lua "ngx.say('Hello,lua!')";
  }
}
/etc/nginx/lua/current_time.lua

パラメータを受け取ってゴニョゴニョしています。
特に語るところはありません。

local cjson = require "cjson"
local ctime = ""

local args = ngx.req.get_uri_args()
if args.df == "epoch" then
    ctime = os.date("%s")
else
    ctime = os.date("%Y-%m-%dT%H:%M:%S %z")
end
if args.rf == "json" then
    ngx.header.content_type = "application/json";
    ngx.print(cjson.encode({current_time = ctime}))
else
    ngx.header.content_type = "text/plain";
    ngx.print(ctime)
end


非常に簡単ですね。

ベンチマーク

abしてみました。
コマンドパラメータはこんな感じです。

ab -n 20000 -c 2 http://127.0.0.1/xxxx
/ (Hello Lua)
Server Software:        nginx/1.4.3
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /
Document Length:        11 bytes

Concurrency Level:      2
Time taken for tests:   2.018 seconds
Complete requests:      20000
Failed requests:        0
Write errors:           0
Total transferred:      3060000 bytes
HTML transferred:       220000 bytes
Requests per second:    9912.15 [#/sec] (mean)
Time per request:       0.202 [ms] (mean)
Time per request:       0.101 [ms] (mean, across all concurrent requests)
Transfer rate:          1481.02 [Kbytes/sec] received
/current_time
Server Software:        nginx/1.4.3
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /current_time
Document Length:        25 bytes

Concurrency Level:      2
Time taken for tests:   2.339 seconds
Complete requests:      20000
Failed requests:        0
Write errors:           0
Total transferred:      3340000 bytes
HTML transferred:       500000 bytes
Requests per second:    8549.87 [#/sec] (mean)
Time per request:       0.234 [ms] (mean)
Time per request:       0.117 [ms] (mean, across all concurrent requests)
Transfer rate:          1394.36 [Kbytes/sec] received
/static/index.html(静的ファイル配信。このファイルは手動で作りました)
Server Software:        nginx/1.4.3
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /static/index.html
Document Length:        6 bytes

Concurrency Level:      2
Time taken for tests:   1.942 seconds
Complete requests:      20000
Failed requests:        0
Write errors:           0
Total transferred:      4680000 bytes
HTML transferred:       120000 bytes
Requests per second:    10298.39 [#/sec] (mean)
Time per request:       0.194 [ms] (mean)
Time per request:       0.097 [ms] (mean, across all concurrent requests)
Transfer rate:          2353.34 [Kbytes/sec] received


静的ファイル配信よりわずかに落ちますが十分早い。


Nginx+Lua、環境設定が若干面倒ですが、
上記クックブックを使うと一発で環境作れますし、
Luaスクリプトもお手軽に書けそうなので試してみてはいかがでしょうか〜
(゚Д゚)オLua!オLua!

*1:※追記 openrestyを使うと楽できるようです! http://openresty.org/