Eukleides project

from http://d.hatena.ne.jp/u5_h/

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を設定してクライアントへ返す。