Jakarta Apache JMeter を使用して、OpenSSOをシングルサインオン性能を計測した。
OpenSSOに対してLogin / Logout の操作を複数のユーザーが同時に行った際の同時実効性能、スループット。及び、セッションが増したときのWeb Container側のメモリの使用率など、他のマシンとの比較対象となる数値を計測する、また、その計測方法のサンプルを作成する。

サーバー: WhiteBox
CPU: AMD Phenom 9350e 2.0GHz Quad-Core
RAM: 2GB
OS: Fedora 12 32bit
JDK 1.6
GlassFish v3 Preview / Sun Web Server 7.0 u6
OpenSSO Enterprise 8.0
Sun Directory Server Enterprise Edition 7.0
OpenSSOのConfig Data Store / User Data Storeには、Sun Directory Server 7.0を使用。Directory Server 7.0はFedoraを正式にサポートしておらず、セットアップには多少のウォークアラウンドを要した。( # mv lib/private/libfreebl3.so lib/private/libfreebl3.so- )
Directory Server 6.xの操作知識があれば、7.0は問題なく操作できた。
ユーザーデータとして、10万件のエントリーを作成。サンプルデータの作成には、OpenDSに付属してくる make-ldif ユーティリティを使用し、LDIFを作成しインポートした。
Web Containerとして、まずは、GlassFish v3 Preview(Preludeでは無く)を使用。特別な設定なしにOpenSSO Enterprise 8.0のコンフィギュレーションが完了する。Sun Web Server 7.0 u6でも同様にテストを行う。Web Server 7.0 u6でもインストール時にDirectory Serverと同様のウォークアラウンドを要した。
JMeter 2.3.4を使用。
テストケースとして次の定義を用いる。
基本的なシナリオ
上記を、同時に数十スレッドから数百スレッド実行し、スループットとサーバー側のCPUやメモリの使用率を測定する。

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="2.1">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="amtest-plan" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="ユーザー定義変数" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="スレッドグループ" enabled="true">
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="ループコントローラ" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<intProp name="LoopController.loops">-1</intProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">1000</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<longProp name="ThreadGroup.start_time">1258881060000</longProp>
<longProp name="ThreadGroup.end_time">1258884000000</longProp>
<boolProp name="ThreadGroup.scheduler">true</boolProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<stringProp name="ThreadGroup.duration">60</stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
</ThreadGroup>
<hashTree>
<ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP リクエスト初期値設定" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="ユーザー定義変数" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">redcube.example.com</stringProp>
<stringProp name="HTTPSampler.port">8080</stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path"></stringProp>
</ConfigTestElement>
<hashTree/>
<CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP クッキーマネージャ" enabled="true">
<collectionProp name="CookieManager.cookies"/>
<boolProp name="CookieManager.clearEachIteration">false</boolProp>
<stringProp name="CookieManager.policy">rfc2965</stringProp>
</CookieManager>
<hashTree/>
<HTTPSampler2 guiclass="HttpTestSampleGui2" testclass="HTTPSampler2" testname="login" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="ユーザー定義変数" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="IDToken1" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">user.${__Random(0,100000,)}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
<stringProp name="Argument.name">IDToken1</stringProp>
</elementProp>
<elementProp name="IDToken2" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">password</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
<stringProp name="Argument.name">IDToken2</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain"></stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/opensso/UI/Login</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.FILE_NAME"></stringProp>
<stringProp name="HTTPSampler.FILE_FIELD"></stringProp>
<stringProp name="HTTPSampler.mimetype"></stringProp>
<boolProp name="HTTPSampler.monitor">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
</HTTPSampler2>
<hashTree/>
<GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="シンプルコントローラ" enabled="true"/>
<hashTree>
<ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="wait time" enabled="true">
<stringProp name="ConstantTimer.delay">1</stringProp>
</ConstantTimer>
<hashTree/>
<HTTPSampler2 guiclass="HttpTestSampleGui2" testclass="HTTPSampler2" testname="logout" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="ユーザー定義変数" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain"></stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/opensso/UI/Logout</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">true</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.FILE_NAME"></stringProp>
<stringProp name="HTTPSampler.FILE_FIELD"></stringProp>
<stringProp name="HTTPSampler.mimetype"></stringProp>
<boolProp name="HTTPSampler.monitor">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
</HTTPSampler2>
<hashTree/>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="結果をツリーで表示" enabled="false">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>true</xml>
<fieldNames>false</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="GraphVisualizer" testclass="ResultCollector" testname="グラフ表示" enabled="false">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>true</xml>
<fieldNames>false</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="統計レポート" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>true</xml>
<fieldNames>false</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<GaussianRandomTimer guiclass="GaussianRandomTimerGui" testclass="GaussianRandomTimer" testname="ガウス乱数タイマ" enabled="true">
<stringProp name="ConstantTimer.delay">300</stringProp>
<stringProp name="RandomTimer.range">100.0</stringProp>
</GaussianRandomTimer>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>
| Threads | Complete Count | Avg(ms) | Median(ms) | 90% Line(ms) | Min(ms) | Max(ms) | through put( count/sec ) |
|---|---|---|---|---|---|---|---|
| 50 thread | 7321 9583 | 105 10 | 34 9 | 210 16 | 6 4 | 299 80 | 121.32 159.33 |
| 100 thread | 14695 18588 | 102 15 | 50 12 | 211 31 | 5 4 | 376 295 | 243.22 307.8 |
| 200 thread | 21477 24555 | 248 171 | 201 55 | 548 479 | 5 4 | 871 1176 | 355.39 404.59 |
| 500 thread | 19885 22826 | 1203 990 | 861 892 | 1929 1471 | 6 5 | 2390 13632 | 321.82 366.54 |
| 1000 thread | 21072 20927 | 2547 2553 | 2400 2520 | 3550 3235 | 6 611 | 5787 5632 | 327.47 327.5 |