h2o+mruby+redisでproxy cache serverを作る
h2oとは
CPU使用率の小さいユーザに対して、迅速なレスポンスを返答する新世代HTTPサーバ
また、優先コンテンツ配信や、server push機能を含むHTTP2の機能をフルに活用し、webサイト訪問者に傑出した経験を提供する新世代HTTPサーバのようです(意訳)
https://h2o.examp1e.net/
https://github.com/h2o/h2o
mrubyとは
軽量実装されたruby。
h2oでは、組み込み言語として採用されている。nginxでいうluaな立ち位置との認識。nginxでもmrubyは使えるけど。
luaはよくわからないけど、rubyならわかりそう←
https://github.com/mruby/mruby
redisとは
in-memoryタイプのno-sql。メモリにテーブルを持つためデータのR/Wは高速、データ永続化は定期的にdiskに書くことで実施しているらしい...。
https://redis.io/
h2oで単純にmrubyのredisライブラリを使うと、IOをブロックしてしまい、h2oの高速なイベントループが使えなくなるらしい。
故に下に貼ったプレゼンリンク曰く、ノンブロッキングなredisラッパを実装中とのこと。
h2o with mrubyの導入方法(centos7 on docker)
yum -y install ruby bison cmake gcc gcc-c++ git openssl openssl-devel git clone https://github.com/h2o/h2o cd h2o cmake -DWITH_MRUBY=on . make h2o sudo make install
[ 0%] Building C object CMakeFiles/h2o.dir/deps/picotls/deps/cifra/src/sha512.c.o /root/h2o/deps/picotls/deps/cifra/src/sha512.c: In function 'sha512_update_block': /root/h2o/deps/picotls/deps/cifra/src/sha512.c:115:3: error: 'for' loop initial declarations are only allowed in C99 mode for (size_t t = 0; t < 80; t++) ^ /root/h2o/deps/picotls/deps/cifra/src/sha512.c:115:3: note: use option -std=c99 or -std=gnu99 to compile your code make[3]: *** [CMakeFiles/h2o.dir/deps/picotls/deps/cifra/src/sha512.c.o] Error 1 make[2]: *** [CMakeFiles/h2o.dir/all] Error 2 make[1]: *** [CMakeFiles/h2o.dir/rule] Error 2 make: *** [h2o] Error 2
makeで上のように怒られたので下のようにpatch
[root@66a789150ee9 h2o]# git diff diff --git a/deps/picotls/deps/cifra/src/sha512.c b/deps/picotls/deps/cifra/src/sha512.c index 2d1c896..44e3f23 100644 --- a/deps/picotls/deps/cifra/src/sha512.c +++ b/deps/picotls/deps/cifra/src/sha512.c @@ -111,8 +111,8 @@ static void sha512_update_block(void *vctx, const uint8_t *inp) g = ctx->H[6], h = ctx->H[7], Wt; - - for (size_t t = 0; t < 80; t++) + size_t t; + for (t = 0; t < 80; t++) { if (t < 16) {
動作確認
[root@66a789150ee9 h2o]# h2o -v h2o version 2.3.0-DEV@6c7c850 OpenSSL: OpenSSL 1.0.2k-fips 26 Jan 2017 mruby: YES
ok!
redisの導入方法(centos7 on docker):
yum install epel-release yum install redis /usr/local/bin/redis-server #systemctlに登録してサービス化する,何も指定せずに起動すると、6379でlistenする
h2o+mruby+redisでproxy cacheサーバを作る
https://www.slideshare.net/ichitonagata/h2o-x-mruby-72949986
h2oのgithubを見る限り、どうも上のスライドを作った作者がpushしたと思われるmruby-redisというブランチがmasterにマージされており、masterのソースを見る限り、mrubyでredisが使えるようになっている。ということはredisを使ってコンテンツキャッシュできるんじゃなかろうか?と思い、confを書いてみた。
スライドの例のごとくは書けないものの、https://github.com/h2o/h2o/blob/master/share/h2o/mruby/redis.rbのソースを読みながら書いてみた。
hosts: "localhost:8080": listen: port: 8080 host: 0.0.0.0 paths: "/": mruby.handler: | redis = H2O::Redis.new(host: "127.0.0.1", port: "6379") redis.connect Proc.new do |env| extend_resheader = {} cached = redis.call("GET","key").join if cached.nil? then headers = {} env.each do |key, value| if /^HTTP_/.match(key) headers[$'] = value end end cached = http_request( "https://www.hatena.ne.jp", method: env["REQUEST_METHOD"], headers: headers, body: env["rack.input"], ).join[2].join extend_resheader["X-Cache"]="MISS" redis.call("SET", "key", cached).join else extend_resheader["X-Cache"]="HIT" end [200,extend_resheader,[cached]] end
1回目のhttp://localhost:8080/へのリクエストでは、https://www.hatena.ne.jpにhttp_requestでproxyする。
そのレスポンスbodyを"key":(httpレスポンスbody)でredisに保存する。
2回目以降のhttp://localhost:8080/へのリクエストでは、redisからkeyで1回目に保存した結果をgetで取り出し
レスポンスを返す。
extend_resheaderにはproxy先からコンテンツを返したことを示すヘッダ X-Cache:MISS、redisからキャッシュを返した
X-Cache:HITを設定してクライアントへ返す。