2017年12月19日火曜日

MySQLジャンキーにngx_mrubyを与えた結果

この記事は 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 Labsは「α前のステータスのプロダクトを取り敢えず出してみる場」らしいので(要出典)、 GAになってから1度もメジャーバージョンアップできずに最短ライフタイムサイクルで消えた ナントカ とは違い、まあそういうものだし当時は俺も「誰得wwww」とか言ってたんですが、
俺が遊び始めて「あー面白いかも」と思った途端に消える
という
いつもの流れになって寂しく思っていたところです。
「そこで ngx_mruby ですよ」
誰かの声が聞こえた気がしたので試してみました。 やってること自体は単なるHello, Worldです。

ngx_mrubyのインストールには nginxにngx_mrubyをインストールする - Qiita を大変参考にさせていただきました。 2014年のアドベントカレンダーの記事が3年経ってMySQLジャンキーを救う(?)、インターネッツですね。
あとはペパボの中の人にパッケージをビルドするヤーツも教えてもらいました。最終的にはこれで作ったrpmをインストールしています。便利!
ngx_mruby/build_config.rbconf.gem :github => 'mattn/mruby-mysql' だけ追加しました。
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的な文脈では)何に使えるだろう。。

0 件のコメント :

コメントを投稿