Upload
norito-agetsuma
View
5.194
Download
0
Embed Size (px)
Citation preview
Java EEパフォーマンス Tips2015/09/16帰ってきた GlassFish Users Group Japan 勉強会上妻 宜人 ( あげつま のりと )
上妻 宜人 - あげつま のりと• SIer 勤務• 技術サポート部隊に所属• Java / Java EE トラブルシューティング
• はてな 見習いプログラミング日記
本日のテーマ
Java EE レイヤ毎の性能 Tips
Presentation BusinessLogic Repository
• JSF
• JAX-RS
• ....
• EJB
• CDI
• JPA/JDBC
• 外部 API 呼出• ファイル I/O
Java EE パフォーマンス Tips
• プレゼンテーション層 (JSF)• #1 古い mojarra を使わない (JAVASERVERFACES-2494 対
策 )
• ビジネス層 (EJB/CDI)
• #2 DI は EJB ではなく CDI を使う• #3 @Asynchronous 利用時はスレッドプールに注
意• データアクセス層 (JPA)
• #4 JPA の executeBatch 設定を忘れない
#1 古い mojarra を使わない(JAVASERVERFACES-2494 対策 )
#1 古い mojarra を使わない • JSF 参照実装 mojarra には、かつて性能バグがあった• JAVASERVERFACES-2494 (https://java.net/jira/browse/JAVASERVERFACES-2494)
• JSF タグ (UIComponent) が増えると遅くなる問題
• 2.1.22, 2.2.1 で修正• GlassFish4.0 が該当。 4.1 で Fix 。
GlassFish4.0 と Payara4.1 で実測• 大量の JSF タグがポイント• 1000, 3000, 5000 タグで GlassFish4.0 と Payara4.1
比較 <h:body> <h1>JSF Many Component 1000</h1> <h:form> <h:outputText value="#{testBean.name}"/> <h:outputText value="#{testBean.name}"/> <h:outputText value="#{testBean.name}"/> <h:outputText value="#{testBean.name}"/> <h:outputText value="#{testBean.name}"/>
... 以降大量の outputText を繰り返す
タグ数が多い場合は注意• GlassFish4.0: レスポンスタイムが徐々に低下• Payara4.1.153 : 5000 タグで 150 ミリ秒
1000 2000 3000 4000 50000
1000
2000
3000
4000
5000
GlassFish 4.0 (mo-jarra2.2.0)Payara 4.1.153 (mo-jarra2.2.11)
JSF タグ数
レスポン
スタイム
(ミリ秒
)
マシン情報 : MacBook Air corei5 1.7GHzJDK1.8.0_60
mojarra は色々な所で使われている• GlassFish
• 4.0 は影響あり。 4.1 以降で修正。• Payara は 4.1 ベースなので影響なし。
• WildFly
• 8.0.0 と 8.1.0 は影響あり。 8.2 以降で修正。
Java EE パフォーマンス Tips
• プレゼンテーション層 (JSF)• #1 古い mojarra を使わない (JAVASERVERFACES-2494 対
策 )
• ビジネス層 (EJB/CDI)
• #2 DI は EJB ではなく CDI を使う• #3 @Asynchronous 利用時はスレッドプールに注
意• データアクセス層 (JPA)
• #4 JPA の executeBatch 設定を忘れない
#2 DI は EJB ではなく CDI を使う
#2 DI は EJB ではなく CDI を使う• Java EE6 CDI が導入。• Java EE7 より beans.xml なしでデフォルト有効化。• Java EE5 までは EJB 間でのみ DI が利用可。
@Injectprivate StockService service;
@Dependentpublic class StockService { // ...}
EJB のループ呼び出しによるオーバヘッド• EJB のループ呼び出しは性能上悩みの種だった• 万単位以上のループのみオーバヘッド顕在化• 見つかった時には試験期間の後半
実際に測っている• Payara4.1.152 で Bean をループ呼び出し• EJB と CDI Bean を呼び出して比較
// EJB@Statelesspublic class EJBBean { public String echo(String s) { return s; }}
// CDI@ApplicationScopedpublic class CDIBean {...}
// Client@InjectEJBBean ejb;
for (int i = 0; i < loop; i++) { ejb.echo(String.valueOf(i));}
CDI はほとんどオーバヘッドがない
10000 50000 100000
271
907
2953
1 8 15
EJB @StatelessCDI @ApplicationScoped
ループ呼び出し回数
単位 : ミリ秒
マシン情報 : MacBook Air corei5 1.7GHzJDK1.8.0_60, Payara4.1.153
@Transactional もループで呼び出してみた• @Transactinal 宣言的トランザクション (Java EE7 〜 )
• ついうっかり、ループで呼び出される状況を想定@ApplicationScoped@Transactionalpublic class StockService { public void put(Stock s) { // 更新系処理 ... } public void search(Criteria c) { // 参照系処理 ... }}
@Transactional はループで呼ばないこと• @Transactional の実体はインターセプタ。• ループ呼出しコストは高い。
10000 50000 100000271 907 2953
1 8 1519098822
32761
EJB @StatelessCDI @ApplicationScopedCDI @ApplicationScoped & @Transactional
ループ呼び出し回数マシン情報 : MacBook Air corei5 1.7GHzJDK1.8.0_60, Payara4.1.153
単位 : ミリ秒
#3 @Asynchronous 利用時はEJB スレッドプールに注意
#3 @Asynchronous 利用時は EJB スレッドプールに注意• @Asynchronous の振り返り• EJB3.1 (Java EE6) で導入• シンプルに非同期タスク処理が実装できる@Statelesspublic class AsyncBean { @Asynchronous public Future<String> async() { return new AsyncResult<>("done”); }}
GlassFish デフォルトは多重度が増えない• 以下の非同期 EJB の呼び出しは、 16 多重で止まる• GlassFish4 EJB スレッドプールのデフォルト上限が
16
• Performance Tuning Guide にも言及がなく、ハマる@Injectprivate AsyncBean async;
public void callAsyncEjb() { IntStream.range(0, 100) .forEach(i -> async.async());}
“EJB Container” のプロパティを追加• 固有の入力欄はなく、プロパティとして設定• thread-core-pool-size : 最小プールサイズ• thread-max-pool-size : 最大プールサイズ
Java EE パフォーマンス Tips
• プレゼンテーション層 (JSF)• #1 古い mojarra を使わない (JAVASERVERFACES-2494 対
策 )
• ビジネス層 (EJB/CDI)
• #2 DI は EJB ではなく CDI を使う• #3 @Asynchronous 利用時はスレッドプールに注
意• データアクセス層 (JPA)
• #4 JPA の executeBatch 設定を忘れない
#4. JPA の executeBatch 設定を忘れない• JDBC 利用時は executeBatch を皆意識する• JPA を使い始めると、何故か忘れやすい• JPA 内部で自動的に最適化されている淡い期待• しかし、 EclipseLink も Hibernate もバッチ更新デフォル
ト無効
EclipseLink のバッチ書き込み設定• persistence.xml に設定• eclipselink.jdbc.batch-writing = jdbc (default: none)
• eclipselink.jdbc.batch-writing.size = ... (default: 100)
<persistence-unit name="PostgresPU"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <jta-data-source>jdbc/postgresDS</jta-data-source> <properties> <property name="eclipselink.jdbc.batch-writing" value="jdbc"/> <property name="eclipselink.jdbc.batch-writing.size" value="100"/> ...
Payara4.1 で実際に測ってみる• PostgreSQL に 100, 1000, 1 万レコードの INSERT
• JDBC を加えて O/R マッパー遅い疑惑も検証 • #1. JDBC executeBatch
• #2. JPA batch-writing=jdbc ( バッチ有効化 )
• #3. JPA batch-writing=none ( バッチ無効化 )
測定結果• INSERT 100 レコード でも十分な効果がある• JDBC と比較して、 JPA(EclipseLink) は 1 〜 2 割遅い• 1 万行であれば、 ORM コストはそれほど大きく
ない
100 レコード 1000 レコード 10000 レコード40 128 122375 169 14891243
9023
58247JDBC executeBatch EclipseLink (batch-writing=jdbc) EclipseLink (batch-writing=none)
単位 : ミリ秒
マシン情報 : MacBook Air corei5 1.7GHzJDK1.8.0_60, Payara4.1.153, PostgreSQL9.3
まとめ
#1 古い mojarra を使わないGlassFish4.1 以上 or Payara を使う
(mojarra2.1.22, 2.2.1 以上 )
#2 DI は EJB ではなく CDI を使う過ループ時の EJB のオーバヘッドは大きい
@Transactional ループ呼び出しに注意
#3 @Asynchronous 利用時はEJB スレッドプールに注意
EJB コンテナの以下プロパティを忘れずに設定thread-core-pool-size, thread-max-pool-size
#4 JPA の executeBatch 設定を忘れないEclipseLink/Hibernate デフォルトはバッチ無効
persistence.xmleclipselink.jdbc.batch-writing=jdbc