32
Java EE パパパパパパパ Tips 2015/09/16 パパパパパ GlassFish Users Group Japan パパパ パパ パパ ( パパパパ パパパ )

Java EE パフォーマンスTips #glassfish_jp

Embed Size (px)

Citation preview

Page 1: Java EE パフォーマンスTips #glassfish_jp

Java EEパフォーマンス Tips2015/09/16帰ってきた GlassFish Users Group Japan 勉強会上妻 宜人 ( あげつま のりと )

Page 2: Java EE パフォーマンスTips #glassfish_jp

上妻 宜人 - あげつま のりと• SIer 勤務• 技術サポート部隊に所属• Java / Java EE トラブルシューティング

• はてな 見習いプログラミング日記

Page 3: Java EE パフォーマンスTips #glassfish_jp

本日のテーマ

Page 4: Java EE パフォーマンスTips #glassfish_jp

Java EE レイヤ毎の性能 Tips

Presentation BusinessLogic Repository

• JSF

• JAX-RS

• ....

• EJB

• CDI

• JPA/JDBC

• 外部 API 呼出• ファイル I/O

Page 5: Java EE パフォーマンスTips #glassfish_jp

Java EE パフォーマンス Tips

• プレゼンテーション層 (JSF)• #1 古い mojarra を使わない (JAVASERVERFACES-2494 対

策 )

• ビジネス層 (EJB/CDI)

• #2 DI は EJB ではなく CDI を使う• #3 @Asynchronous 利用時はスレッドプールに注

意• データアクセス層 (JPA)

• #4 JPA の executeBatch 設定を忘れない

Page 6: Java EE パフォーマンスTips #glassfish_jp

#1 古い mojarra を使わない(JAVASERVERFACES-2494 対策 )

Page 7: Java EE パフォーマンスTips #glassfish_jp

#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 。

Page 8: Java EE パフォーマンスTips #glassfish_jp

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 を繰り返す

Page 9: Java EE パフォーマンスTips #glassfish_jp

タグ数が多い場合は注意• 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

Page 10: Java EE パフォーマンスTips #glassfish_jp

mojarra は色々な所で使われている• GlassFish

• 4.0 は影響あり。 4.1 以降で修正。• Payara は 4.1 ベースなので影響なし。

• WildFly

• 8.0.0 と 8.1.0 は影響あり。 8.2 以降で修正。

Page 11: Java EE パフォーマンスTips #glassfish_jp

Java EE パフォーマンス Tips

• プレゼンテーション層 (JSF)• #1 古い mojarra を使わない (JAVASERVERFACES-2494 対

策 )

• ビジネス層 (EJB/CDI)

• #2 DI は EJB ではなく CDI を使う• #3 @Asynchronous 利用時はスレッドプールに注

意• データアクセス層 (JPA)

• #4 JPA の executeBatch 設定を忘れない

Page 12: Java EE パフォーマンスTips #glassfish_jp

#2 DI は EJB ではなく CDI を使う

Page 13: Java EE パフォーマンスTips #glassfish_jp

#2 DI は EJB ではなく CDI を使う• Java EE6 CDI が導入。• Java EE7 より beans.xml なしでデフォルト有効化。• Java EE5 までは EJB 間でのみ DI が利用可。

@Injectprivate StockService service;

@Dependentpublic class StockService { // ...}

Page 14: Java EE パフォーマンスTips #glassfish_jp

EJB のループ呼び出しによるオーバヘッド• EJB のループ呼び出しは性能上悩みの種だった• 万単位以上のループのみオーバヘッド顕在化• 見つかった時には試験期間の後半

Page 15: Java EE パフォーマンスTips #glassfish_jp

実際に測っている• 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));}

Page 16: Java EE パフォーマンスTips #glassfish_jp

CDI はほとんどオーバヘッドがない

10000 50000 100000

271

907

2953

1 8 15

EJB @StatelessCDI @ApplicationScoped

ループ呼び出し回数

単位 : ミリ秒

マシン情報 : MacBook Air corei5 1.7GHzJDK1.8.0_60, Payara4.1.153

Page 17: Java EE パフォーマンスTips #glassfish_jp

@Transactional もループで呼び出してみた• @Transactinal 宣言的トランザクション (Java EE7 〜 )

• ついうっかり、ループで呼び出される状況を想定@ApplicationScoped@Transactionalpublic class StockService { public void put(Stock s) { // 更新系処理 ... } public void search(Criteria c) { // 参照系処理 ... }}

Page 18: Java EE パフォーマンスTips #glassfish_jp

@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

単位 : ミリ秒

Page 19: Java EE パフォーマンスTips #glassfish_jp

#3 @Asynchronous 利用時はEJB スレッドプールに注意

Page 20: Java EE パフォーマンスTips #glassfish_jp

#3 @Asynchronous 利用時は EJB スレッドプールに注意• @Asynchronous の振り返り• EJB3.1 (Java EE6) で導入• シンプルに非同期タスク処理が実装できる@Statelesspublic class AsyncBean { @Asynchronous public Future<String> async() { return new AsyncResult<>("done”); }}

Page 21: Java EE パフォーマンスTips #glassfish_jp

GlassFish デフォルトは多重度が増えない• 以下の非同期 EJB の呼び出しは、 16 多重で止まる• GlassFish4 EJB スレッドプールのデフォルト上限が

16

• Performance Tuning Guide にも言及がなく、ハマる@Injectprivate AsyncBean async;

public void callAsyncEjb() { IntStream.range(0, 100) .forEach(i -> async.async());}

Page 22: Java EE パフォーマンスTips #glassfish_jp

“EJB Container” のプロパティを追加• 固有の入力欄はなく、プロパティとして設定• thread-core-pool-size : 最小プールサイズ• thread-max-pool-size : 最大プールサイズ

Page 23: Java EE パフォーマンスTips #glassfish_jp

Java EE パフォーマンス Tips

• プレゼンテーション層 (JSF)• #1 古い mojarra を使わない (JAVASERVERFACES-2494 対

策 )

• ビジネス層 (EJB/CDI)

• #2 DI は EJB ではなく CDI を使う• #3 @Asynchronous 利用時はスレッドプールに注

意• データアクセス層 (JPA)

• #4 JPA の executeBatch 設定を忘れない

Page 24: Java EE パフォーマンスTips #glassfish_jp

#4. JPA の executeBatch 設定を忘れない• JDBC 利用時は executeBatch を皆意識する• JPA を使い始めると、何故か忘れやすい• JPA 内部で自動的に最適化されている淡い期待• しかし、 EclipseLink も Hibernate もバッチ更新デフォル

ト無効

Page 25: Java EE パフォーマンスTips #glassfish_jp

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"/> ...

Page 26: Java EE パフォーマンスTips #glassfish_jp

Payara4.1 で実際に測ってみる• PostgreSQL に 100, 1000, 1 万レコードの INSERT

• JDBC を加えて O/R マッパー遅い疑惑も検証 • #1. JDBC executeBatch

• #2. JPA batch-writing=jdbc ( バッチ有効化 )

• #3. JPA batch-writing=none ( バッチ無効化 )

Page 27: Java EE パフォーマンスTips #glassfish_jp

測定結果• 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

Page 28: Java EE パフォーマンスTips #glassfish_jp

まとめ

Page 29: Java EE パフォーマンスTips #glassfish_jp

#1 古い mojarra を使わないGlassFish4.1 以上 or Payara を使う

(mojarra2.1.22, 2.2.1 以上 )

Page 30: Java EE パフォーマンスTips #glassfish_jp

#2 DI は EJB ではなく CDI を使う過ループ時の EJB のオーバヘッドは大きい

@Transactional ループ呼び出しに注意

Page 31: Java EE パフォーマンスTips #glassfish_jp

#3 @Asynchronous 利用時はEJB スレッドプールに注意

EJB コンテナの以下プロパティを忘れずに設定thread-core-pool-size, thread-max-pool-size

Page 32: Java EE パフォーマンスTips #glassfish_jp

#4 JPA の executeBatch 設定を忘れないEclipseLink/Hibernate デフォルトはバッチ無効

persistence.xmleclipselink.jdbc.batch-writing=jdbc