この記事は mod_mruby ngx_mruby Advent Calendar 2017 の18日目の記事です。
時間オーバーしてますごめんなさい。
時間オーバーしてますごめんなさい。
本題に入る前に、この記事をご覧の方の中に “MySQL HTTP Plugin” をご存知の方はいらっしゃるでしょうか? (MySQL Casualな方々はこの辺でオチの予想がついたはず)
MySQL HTTP Pluginは2014年ころに MySQL Labs で公開されていた「実験室版」として配布されていて、 MySQLがHTTPをしゃべるようになるプラグイン です。
何言ってるのかよくわからないとか、誰得? とか思うかも知れませんが、そんな細かいことを気にしてはいけません。とにかく、MySQL(mysqld)がHTTPをしゃべったんです。
かつての面影(?)はこのあたりの記事とか資料に見て取ることができます。
- MySQL HTTP Plugin確認 – variable.jp
- 日々の覚書: MYSQL HTTP PLUGIN確認 の補足
- HTTP Plugin for MySQL!
- Handlerさんコンニチワ
MySQL Labsは「α前のステータスのプロダクトを取り敢えず出してみる場」らしいので(要出典)、 GAになってから1度もメジャーバージョンアップできずに最短ライフタイムサイクルで消えた ナントカ とは違い、まあそういうものだし当時は俺も「誰得wwww」とか言ってたんですが、
俺が遊び始めて「あー面白いかも」と思った途端に消える
という
いつもの流れになって寂しく思っていたところです。
「そこで ngx_mruby ですよ」
誰かの声が聞こえた気がしたので試してみました。 やってること自体は単なるHello, Worldです。
ngx_mrubyのインストールには nginxにngx_mrubyをインストールする - Qiita を大変参考にさせていただきました。 2014年のアドベントカレンダーの記事が3年経ってMySQLジャンキーを救う(?)、インターネッツですね。
あとはペパボの中の人にパッケージをビルドするヤーツも教えてもらいました。最終的にはこれで作ったrpmをインストールしています。便利!
ngx_mruby/build_config.rb
に conf.gem :github => 'mattn/mruby-mysql'
だけ追加しました。 /etc/nginx/conf.d/default.conf
によしなにこんな記述を追加。 /query
をエンドポイントにして後ろに渡されたクエリーをそのままローカルのmysqldに受け渡してJSONで戻す(えっ) location /query {
mruby_content_handler_code '
req = Nginx::Request.new
sql = req.uri.gsub(/\/query\//, "")
conn = MySQL::Database.new("127.0.0.1", "root", "", "world", 64057)
ret= []
conn.execute(sql) do |row, field|
one_row= []
for n in 0..field.count - 1 do
one_row.push({field[n] => row[n]})
end
ret.push(one_row)
end
req.content_type = "application/json"
Nginx.rputs JSON::stringify(ret)
';
}
こんな感じ。
たったこれだけのものを書くのに結構長い時間がかかった。プログラム力の衰えを感じる。。
たったこれだけのものを書くのに結構長い時間がかかった。プログラム力の衰えを感じる。。
で、出来上がったMySQL HTTP Pluginごっこはこんな感じ。
$ http localhost/query/SELECT%20%2A%20FROM%20city%20LIMIT%203
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 287
Content-Type: application/json
Date: Tue, 19 Dec 2017 06:24:38 GMT
Server: nginx/1.13.7
[
[
{
"ID": 1
},
{
"Name": "Kabul"
},
{
"CountryCode": "AFG"
},
{
"District": "Kabol"
},
{
"Population": 1780000
}
],
[
{
"ID": 2
},
{
"Name": "Qandahar"
},
{
"CountryCode": "AFG"
},
{
"District": "Qandahar"
},
{
"Population": 237500
}
],
[
{
"ID": 3
},
{
"Name": "Herat"
},
{
"CountryCode": "AFG"
},
{
"District": "Herat"
},
{
"Population": 186800
}
]
]
おおなんかMySQL HTTP Pluginっぽい! ちなみに本家MySQL HTTP Pluginの出力結果はこんな感じでした。結構違う。
ってか実データとメタデータが分離されてた。それでよかったのか。。
$ http http://a:b@localhost:8080/sql/world/SELECT+%2A+FROM+city+LIMIT+3
HTTP/1.1 200 OK
Cache-control: must-revalidate
Connection: Keep-Alive
Content-Length: 1078
Content-Type: application/json
Pragma: no-cache
Server: MyHTTP 1.0.0-alpha
[
{
"data": [
[
"1",
"Kabul",
"AFG",
"Kabol",
"1780000"
],
[
"2",
"Qandahar",
"AFG",
"Qandahar",
"237500"
],
[
"3",
"Herat",
"AFG",
"Herat",
"186800"
]
],
"meta": [
{
"catalog": "def",
"charset": 63,
"column": "ID",
"database": "world",
"decimals": 0,
"flags": 16899,
"length": 11,
"org_column": "ID",
"org_table": "city",
"table": "city",
"type": 3
},
{
"catalog": "def",
"charset": 33,
"column": "Name",
"database": "world",
"decimals": 0,
"flags": 1,
"length": 105,
"org_column": "Name",
"org_table": "city",
"table": "city",
"type": 254
},
{
"catalog": "def",
"charset": 33,
"column": "CountryCode",
"database": "world",
"decimals": 0,
"flags": 16393,
"length": 9,
"org_column": "CountryCode",
"org_table": "city",
"table": "city",
"type": 254
},
{
"catalog": "def",
"charset": 33,
"column": "District",
"database": "world",
"decimals": 0,
"flags": 1,
"length": 60,
"org_column": "District",
"org_table": "city",
"table": "city",
"type": 254
},
{
"catalog": "def",
"charset": 63,
"column": "Population",
"database": "world",
"decimals": 0,
"flags": 1,
"length": 11,
"org_column": "Population",
"org_table": "city",
"table": "city",
"type": 3
}
],
"status": [
{
"server_status": 34,
"warning_count": 0
}
]
}
]
まあなんかよしなに楽しんでみました。実際のところ(MySQL的な文脈では)何に使えるだろう。。