このgistは Cloud Foundry Advent Calendar 2013 の13日目の記事です。大幅に遅れてすみません..。
今日は,「Gorouter と Ruby Router v2 の性能比較」の2回目として,Gorouterと Ruby Router v2 の改造を行います。
が,その前に,前回の記事で作ったmassregistrarを修正しました。https://gist.github.com/nota-ja/7901644/revisions の3f9cdbf
のところの差分を見てもらうとわかりますが,主な修正点は,1) バックエンドのダミーアプリのURLの数もコマンドライン・オプションで指定するようにしたこと, 2) IPアドレスごとにポートとURLの対応付けを行う処理を1回しか行わないようにしたこと, の2点です。後者の修正で,前回の記事で書いた問題は解消しています。
さて,今回の本題に戻ります。前回説明したように,Cloud Foundry のRouterの主な機能はプロキシー動作です。
- クライアントからアプリへのリクエストを受け付け,
- それを適切なアプリに転送し,
- アプリのレスポンスを受け取って,
- それをクライアントに転送する
ですから,単純に性能を測ろうとすると,転送先のアプリが必要になりますし,そのアプリの性能に影響を受けることになります。それはいろいろと面倒なので,2番目と3番目の動作を省略し,Router内で処理を折り返して,あたかも HTTP 200 OK がアプリから返ってきたように,クライアントにレスポンスを返すよう改造するということが今回の記事の趣旨です。
性能測定を行うのに,測定対象を改造するというのは禁じ手の一つではあるんですが,本質的には影響がない(というより,より本質的な性能を測れるようにする)ということで,その辺には目をつぶって先に進みたいと思います。
まずGorouterから。結局こう改造しました。ベースとしたのは,記事を書いている時点での最新版です。
diff --git a/proxy/request_handler.go b/proxy/request_handler.go
index c54ffa7..68f3df5 100644
--- a/proxy/request_handler.go
+++ b/proxy/request_handler.go
@@ -101,21 +101,14 @@ func (h *RequestHandler) HandleWebSocketRequest(endpoint *route.En
}
func (h *RequestHandler) HandleHttpRequest(transport *http.Transport, endpoint *route.E
- h.transport = transport
-
- h.setupRequest(endpoint)
- h.setupConnection()
-
- endpointResponse, err := transport.RoundTrip(h.request)
- if err != nil {
- return endpointResponse, err
- }
+ endpointResponse := new(http.Response)
+ endpointResponse.StatusCode = 200
h.forwardResponseHeaders(endpointResponse)
h.setupStickySession(endpointResponse, endpoint)
- return endpointResponse, err
+ return endpointResponse, nil
}
func (h *RequestHandler) SetTraceHeaders(routerIp, addr string) {
endpoint
への接続周りを削除して,StatusCode
が200のendpointResponse
を返すとともに,errorとしてnil
を返すようにしています。
試します。まずGorouterを起動します。
sudo bin/router -c src/github.com/cloudfoundry/gorouter/example_config/1213.yml
{"timestamp":1387007312.428715706,"process_id":8967,"source":"common.logger","log_level":"info","message":"Component Router registered successfully","data":null}
設定ファイルの中身はこちら。
status:
port: ***
user: ***
pass: ***
nats:
- host: 192.168.14.111
port: 4222
user: ***
pass: ***
logging:
file: /tmp/gorouter.log
syslog:
level: debug
access_log: /tmp/gorouter-access.log
port: 80
index: 0
go_max_procs: 8
publish_start_message_interval: 30
prune_stale_droplets_interval: 30
droplet_stale_threshold: 120
publish_active_apps_interval: 0 # 0 means disabled
公に配布されている設定ファイルのサンプルとの違いは,natsの接続情報やvarz/healthzのエンドポイントといった非本質的な差分を除けば,ログをファイルに出力するようにしたことくらいです。あとGorouterが待ち受けているポートを Ruby Router v2 のnginxに合わせて80にしたので,起動にルート権限が必要になってます。
次に,massregistrarでダミーアプリのGorouterへの登録を開始します。今回は検証なので,インスタンスを1つにします。
massregistrar -natsAddresses=192.168.14.111:4222 -natsPassword=nats -natsUsername=nats numDea=1 -insPerDea=1 -numUrl=1
2013/12/14 17:01:52 configuring nats server: 192.168.14.111:4222
Gorouterのログから,このダミーアプリのURLがu0.vcap.me
であることがわかります。
{"timestamp":1387008272.888099432,"process_id":8967,"source":"router.global","log_level":"debug","message":"router.register: Received message","data":{"message":{"host":"127.0.0.1","port":30000,"uris":["u0.vcap.me"],"tags":null,"app":"","private_instance_id":""}}}
{"timestamp":1387008272.938806295,"process_id":8967,"source":"router.global","log_level":"debug","message":"Got router.register: &{127.0.0.1 30000 [u0.vcap.me] map[] }","data":null}
curlでアクセスしてみます。
curl -w "%{http_code}" http://u0.vcap.me
200
200が返ってきました。
Gorouterのアクセスログにも,普通に記録されています。
u0.vcap.me - [14/12/2013:17:07:37 +0900] "GET / HTTP/1.1" 200 0 "-" "curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3" 127.0.0.1:54670 response_time:0.000095717 app_id:
response time は約1万分の1秒(100ns)になっています。
ということで,うまくいっているようです。
次は Ruby Router v2 です。
Ruby Router v2 は,前回紹介したこの記事にもあるように,プロキシー動作はnginxが行っています。従って,手を入れるのはnginxの設定ファイルになります。このnginx上のプロキシー処理周りには,luaが入っているのですが,luaを触るのはたぶん1年近くぶりで,しかも前回はちょっと触っただけなので,今回の作業はけっこう苦労しました(これが記事が遅れた原因です,というのは言い訳にしかなりませんが)。
最終的に,改造箇所はこうなりました。
diff --git a/nginx_router.conf b/nginx_router.conf
index cb789e4..ef38365 100644
--- a/nginx_router.conf
+++ b/nginx_router.conf
@@ -142,15 +142,7 @@ http {
)
uls.post_process_subrequest(ngx, res)
- ';
-
- proxy_pass http://$backend_addr;
-
- # Handling response from backend servers
- header_filter_by_lua '
- local uls = require ("uls")
-
- uls.post_process_response(ngx)
+ ngx.status = 200
';
}
}
proxy_pass
がアプリにリクエストを転送する部分で,それ以後の処理を削除し,post_process_subrequest
した直後にngx.status
を200に設定し,プロキシー処理を抜けるようにしています。
Gorouter同様,ベースにしたコードは,記事を書いている時点での最新版です。とはいえ,GitHub上のHistoryを見ればわかるように,最終更新は1年以上前なのですが。
※注:上記URLがrouter(の最新版)ではなくvcap(の最新版)を指している理由は,nginxの設定ファイルがvcap/dev_setup/下のChefレシピによってデプロイされるためです。なお,上記の改造は,デプロイ前のテンプレート・ファイルではなく,デプロイ後の実際に生成された設定ファイルに対して行ったものです。
では,Gorouter同様,起動して試してみます。
まずrouterを起動。
vcap/dev_setup/bin/vcap_dev -d . start router
Targeting default deployment "devbox"
Using cloudfoundry config from .deployments/devbox/config
Executing .deployments/devbox/deploy/rubies/ruby-1.9.2-p180/bin/ruby vcap/dev_setup/bin/vcap start router -c .deployments/devbox/config -v vcap -l .deployments/devbox/log
router : RUNNING
nginxで折り返すのにrouterの起動が必要な理由は,このrouterがNATSの'router.register'メッセージを受信して,アプリのURLへのリクエストがあった時に,それを元にアプリが待ち受けているホスト:ポートを探し出す役割を持っているためです。
routerの設定はこちら。
# Unix domain socket for connection between nginx and router
sock: /tmp/router.sock
# NATS message bus URI
mbus: nats://***:***@192.168.14.111:4222/
logging:
# level: info
level: debug
syslog: RT
pid: /var/vcap/sys/run/router.pid
# IP address and port on which the router is listening
port: 2222
inet: 0.0.0.0
# Used for /healthz and /vars endpoints. If not provided random
# values will be generated on component start. Uncomment to use
# static values.
status:
port: ***
user: ***
password: ***
# Uncomment and set true to show requests-per-second stats for all aps not only top 10. If not specified or set false only 10 apps are shown
expose_all_apps: true
enable_nonprod_apps: true
flush_apps_interval: 30
次にnginxを起動します。
sudo service nginx_router start
* Starting: nginx_router...
massregistrarは停止していないので,そのまま使います。
Gorouter同様,curlでアクセスしてみます。
curl -w "%{http_code}" http://u0.vcap.me
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body bgcolor="white" text="black">
<center><h1>Welcome to nginx!</h1></center>
</body>
</html>
200
nginxのデフォルト・メッセージとともに,200が返ってきました。
routerのログを見ると,こんなログが残っていました。
Dec 14 22:05:18 nsnt001 RT[12479]: [2013-12-14 22:05:18.675443] router - pid=12479 tid=e16c fid=dcdb DEBUG -- Request body: {"host":"u0.vcap.me"}
Dec 14 22:05:18 nsnt001 RT[12479]: [2013-12-14 22:05:18.681628] router - pid=12479 tid=e16c fid=dcdb DEBUG -- Request body: {"host":"u0.vcap.me"}
nginxのアクセスログはどうでしょう。
u0.vcap.me - [14/Dec/2013:22:05:18 +0900] "GET / HTTP/1.1" 200 384 "-" "curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3" 127.0.0.1 response_time:- app_id:0
こちらも問題なく動作しているようです。
ということで,今回はここまで。