スティルハウスの書庫の書庫

はてなダイアリーで書いてた「スティルハウスの書庫」を移転してきました。

今度はServletContext+UUIDで負荷分散状況を調べてみた #appengine

@higayasuoさんのつぶやき:

#appengine でリクエストを処理するスレッドは1インスタンスあたり1つという仮定は正しい。ただし、インスタンスの特定にRuntimeのhashCodeを使うのは間違いでFilterなどで起動時にServletContextにUUIDなどを突っ込んで調べるのが正しい
#appengine でJavaのRuntimeは多くのアプリケーションで共有されていて同じアプリケーションの複数インスタンスで共有されることもある

なるほど、、そこで今度は、Runtime.getRuntime().hashCode()の代わりに、ServletContextにUUIDを入れて識別する方法でTask Queueのコンテナインスタンス単位の負荷分散状況を調べてみました。こんなコードをサーブレットに書いて、ランダムIDを取得します。

final ServletContext sc = super.getServletContext();
String instId = (String) sc.getAttribute("instId");
if (instId == null) {
	instId = UUID.randomUUID().toString();
	sc.setAttribute("instId", instId);
}

これに対してTask Queueで69個のタスクを実行し、得られたログからidを集計した結果がこちら:

[091f09e0-5255-4208-a6d7-4df64bd70f2a] 6
[22920a1f-17ec-4d8b-be3f-4a5881518903] 6
[26171bd4-8f66-46cd-a754-758b93bd71b3] 6
[32a23443-8c65-411a-b984-714fbccc958e] 3
[3753cb1d-da36-4765-b320-97d585f804f8] 5
[3c29e854-f25c-4816-b0f6-1c23f4f366e7] 4
[586ed664-3696-48a7-9fdb-10661309c7c9] 5
[5925465f-33c7-4f43-92ee-c34bc15c44b5] 3
[81e017c5-ea84-46d3-9f8e-1db420cc26bd] 3
[83a78921-7d7c-4939-b8d9-a6fa6f8a844c] 4
[a5cc6e5e-94ea-4c04-b7ad-6c7941baedf1] 1
[ac1f62b7-053f-4f7e-8ba7-c0eca0f21f02] 5
[ad3981bc-c7cb-4eb9-bee6-a6c077bd68c5] 7
[af789ef5-aecc-408f-a265-07847c4d2919] 1
[c42db46a-427a-4ec5-b5a3-30e37cbd7888] 4
[dcb34116-7d10-4302-b1be-8a52aed21bed] 6

総合計	69

今回も前回と同様に、16個のコンテナインスタンスに対して負荷分散してる状況がわかります。すると1JVM=1コンテナインスタンスってことなのかなぁ。。

追記

ひがさんのコメントでご指摘いただいた点(識別の対象がコンテナではなくアプリケーションのインスタンスである点)を修正しました。ついでに、RuntimeのハッシュコードとServletContextのUUIDのペアでも集計してみました(r=がRuntime、i=がインスタンス

[r=10292103:i=ca08a349-6bed-4ccc-9951-288e08b6bc1b] 4
[r=1119993:i=90228109-e8e6-496e-a1d2-884544b701b7] 6
[r=1122818:i=84ae4f57-135f-4ac2-ba3c-0745cd222190] 2
[r=13620718:i=56dce314-e413-4c4f-9164-33b8e17afd0e] 6
[r=14624872:i=5ef591f9-f411-4fa2-9e92-e0eb268dbd81] 7
[r=1496906:i=a60606ab-c065-4f3d-baa3-e18ba34532ad] 6
[r=17350388:i=1563cf15-59da-4150-8f05-0743fd337b3d] 6
[r=21924150:i=571a0e16-cd07-4031-8097-281ba9d5e584] 9
[r=30299896:i=2ad9a29c-d2b9-4bdc-9441-97ff92c0ea78] 6
[r=30652173:i=905cae70-56b7-4c99-8c4e-ec51d082c0ab] 4
[r=33245819:i=f2859bbb-fc04-4061-befb-13dc994e5082] 3
[r=3598249:i=985ae732-d0cc-457c-869a-26eabe096032] 5
[r=3872307:i=51f066a5-7b95-4682-a101-73e1ae0be752] 4
[r=5141948:i=60be17e8-61f0-4420-a9e9-b9edd1c800bf] 1
総合計	69

このデータを見る限りは、

という対応付けになっているようです。一方、ひがさんが指摘されてたように、appengineのすべてのアプリでリクエストのThread IDは「11」に統一されてます。

#appengine どのアプリのThreadのidも11だしhashCodeも28409161っておかしくない? ThreadのidとhashCodeみんなも調べてみて

これは私の環境でも確認できました。どのアプリでもThreadのidは11で、hashCodeは28409161で固定されています。そのため、

#appengine でリクエストを処理するスレッドは1インスタンスあたり1つという仮定が正しいとする根拠は、Threadのidが同じVMでかぶることはないから

よって、

ということになります。てことは、たしか@WdWeaverさんが推測されてたように、個々のリクエストを個別のJVMに割り振ってるのかな〜〜という気がしてきました。

さらに追記

ひがさんからのコメントをふまえて、ここまでの理解をメモっておきます:

  • RuntimeのhashCodeはJVMプロセスを識別しており、上記例では14のJVMプロセスに負荷分散していると考えてよいだろう
  • JVMプロセス内部では複数のスレッドが存在するが、個々のアプリのインスタンスのリクエストはそのいずれか1つで処理される。
    • アプリからみてどのスレッドもid=11に見えるのは、処理順序がいつも決まってるためで、おそらく作為的なものではないだろう