Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

大規模な負荷でもドキドキしない為のJava EE

8,854 views

Published on

JJUG CCC 2015 Spring セッション資料

企業システムを始めとしたエンタープライズ向けと位置づけられるJava EEですが、本質は大規模で信頼性の高いサーバーアプリケーションを開発するためのプラットフォームです。

いわゆるSNSやソーシャルゲームなどコンシューマー向けのサービスのアーキテクチャも大規模化・複雑化している中、Java EEが提供する機能は非常に魅力的です。

このセッションではコンシューマー向けのサービスなどで培われた

JPAを用いた開発におけるデータベースのスケールアウト戦略
JUnitとJMeterクラスタで行うゲームサーバーの大規模負荷テストの自動化
など実践的なJava EE開発のケーススタディをご紹介します。

Published in: Technology
  • Be the first to comment

大規模な負荷でもドキドキしない為のJava EE

  1. 1. JJUG CCC Spring 2015 #jjug_ccc #ccc_ab3 大規模な負荷でも ドキドキしない為の Java EE
  2. 2. @nagaseyasuhito java-ja グリー株式会社 the CRAZY ANGEL COMPANY
  3. 3. Agenda その1 負荷テストするぞ その2 JPAのスケールアウト戦略
  4. 4. 負荷テストするぞ http://www.flickr.com/photos/mattt_org/2831690932 "Electrocardiogram" by mattt.org is licensed under CC BY 2.0 / Added some texts to original
  5. 5. Stress Test Anti Pattern アンチパターン その1 「シングルスレッドで実行」
  6. 6. Stress Test Anti Pattern アンチパターン その2 「ユースケースとかけ離れたシナリオ」
  7. 7. Stress Test Anti Pattern アンチパターン その3 「複数のサーバーでコマンドを叩いて手動実行」
  8. 8. そもそも負荷テストの目的は? https://www.flickr.com/photos/jakecaptive/3205277810 Thinking RFID by Jacob Bøtter is licensed under CC BY 2.0 / Added some texts to original
  9. 9. What Is Stress Test For? 負荷テストの目的 その1 システムの限界性能を知る
  10. 10. What Is Stress Test For? 負荷テストの目的 その2 高負荷時の不具合を発見する
  11. 11. https://www.flickr.com/photos/hermanolobo/8605855035 Road to Hell by Jose Padin is licensed under CC BY 2.0 / Added some texts to original Stress Test meets Continuous Integration
  12. 12. Apache JMeter 負荷テストツールのデファクトスタンダード GUIでシナリオ作成 Pure Java 分散実行に対応 Apache®, Apache JMeter™ and Apache JMeter logo are either registered trademarks or trademarks of the Apache Software Foundation in the United States and/or other countries.
  13. 13. HTTP(S) Test Script Recorder HTTP(S)のプロキシサーバーとして振る舞いHTTPリクエストをトレースする。
  14. 14. Selenium Web Driver Sampler WebアプリケーションのテストツールSeleniumのシナリオを使って負荷テストを行える。 Selenium IDEは実際のユーザーのアクティビティをそのままテストシナリオにできるので、よ り本番に近い負荷をかけることができる。
  15. 15. JUnit Request $JMETER_HOME/lib/junitにあるJUnitテストを実行できる。 • 独自プロトコルの負荷テスト • 既存のプラグインでは表現できない複雑な負荷テストシナリオ などをJUnitテストとして書いて負荷テストができる。
  16. 16. Make Scenario @Test(timeout = 1500) @SneakyThrows public void createAndShow() { String mailAddress = UUID.randomUUID() + "@example.com"; WebTarget target; target = this.client.target(this.url.toString()); target = target.path("api/user"); target = target.queryParam("mailAddress", mailAddress); User user = target.request().method("POST", User.class); assertThat(user.getMailAddress(), is(mailAddress)); Thread.sleep(1000L); target.path(user.getId().toString()).request().get(User.class); } JUnit負荷テストシナリオの例 上記の例はJAX-RSのクライアントを使いREST APIのリクエストを発行し、レスポンスの値を 使いふたたびリクエストを発行するサンプル。 独自のプロトコルや複数のコネクションなども柔軟に扱える。 アサートの条件なども独自に定義できるので複雑な負荷テストシナリオを作りやすい。
  17. 17. Running On Command Line $ jmeter -n -t stress-test.jmx -l stress-test.jtl Creating summariser <summary> Created the tree successfully using stress-test.jmx Starting the test @ Fri Apr 03 18:16:27 JST 2015 (1428052587427) Waiting for possible shutdown message on port 4445 summary + 6 in 3s = 2.0/s Avg: 57 Min: 9 Max: 294 Err: 0 (0.00%) Active: 1 Star summary + 50 in 29.4s = 1.7/s Avg: 7 Min: 4 Max: 11 Err: 0 (0.00%) Active: 1 Star summary = 56 in 33s = 1.7/s Avg: 12 Min: 4 Max: 294 Err: 0 (0.00%) summary + 44 in 26s = 1.7/s Avg: 5 Min: 5 Max: 7 Err: 0 (0.00%) Active: 0 Star summary = 100 in 59.4s = 1.7/s Avg: 9 Min: 4 Max: 294 Err: 0 (0.00%) Tidying up ... @ Fri Apr 03 18:17:26 JST 2015 (1428052646994) ... end of run コマンドラインで実行 jmeterコマンドを-nオプションで起動するとCLIモードになる。-tオプションで負荷テスト シナリオを指定して実行する。 -lオプションで指定したファイルに負荷テストの結果が保存される。
  18. 18. Running On Command Line $ jmeter-server & $ jmeter -n -t stress-test.jmx -R localhost -l stress-test.jtl Creating summariser <summary> Created the tree successfully using stress-test.jmx Configuring remote engine for localhost Using remote object: UnicastRef [liveRef: [endpoint:[10.48.138.59:63762](remote),objI Starting remote engines Starting the test @ Fri Apr 03 18:27:34 JST 2015 (1428053254765) Remote engines have been started Waiting for possible shutdown message on port 4445 summary + 50 in 29.4s = 1.7/s Avg: 7 Min: 4 Max: 11 Err: 0 (0.00%) Active: 1 Star summary = 56 in 33s = 1.7/s Avg: 12 Min: 4 Max: 294 Err: 0 (0.00%) summary + 44 in 26s = 1.7/s Avg: 5 Min: 5 Max: 7 Err: 0 (0.00%) Active: 0 Star summary = 100 in 59.4s = 1.7/s Avg: 9 Min: 4 Max: 294 Err: 0 (0.00%) Tidying up ... @ Fri Apr 03 18:17:26 JST 2015 (1428052646994) ... end of run コマンドラインで実行(分散) -Rオプションでjmeter-serverが起動したホストを指定すると分散実行される。 負荷テストの結果もマージして保存される。
  19. 19. Running On Command Line 負荷テスト結果の閲覧 CUIで出力された負荷テストの結果(*.jtl)をGUIのリスナーで閲覧できる。 負荷テストの傾向などをパッと見たいときに。
  20. 20. JMeter Maven Plugin <plugin> <groupId>com.lazerycode.jmeter</groupId> <artifactId>jmeter-maven-plugin</artifactId> <version>1.10.1</version> <executions> <execution> <phase>verify</phase> <goals> <goal>jmeter</goal> </goals> </execution> </executions> <configuration> <testFilesDirectory>${project.build.testOutputDirectory}</testFilesDirectory> <ignoreResultFailures>true</ignoreResultFailures> <suppressJMeterOutput>false</suppressJMeterOutput> <remoteConfig> <startServersBeforeTests>true</startServersBeforeTests> <serverList>${jmeter.servers}</serverList> </remoteConfig> </configuration> </plugin>
  21. 21. JMeter Maven Plugin <profile> <id>stress-test</id> <build> <plugins> <plugin> <groupId>com.lazerycode.jmeter</groupId> <artifactId>jmeter-maven-plugin</artifactId> <version>1.10.1</version> </plugin> ... </plugins> </build> </profile> 負荷テスト用プロファイル 通常のビルドプロセスに組み込まないようにjmeter-maven-pluginはプロファイルに分離する と使いやすい。 mvn clean verify -Pstress-test
  22. 22. JMeter Maven Plugin <properties> <jmeter.numberOfThreads>1</jmeter.numberOfThreads> <jmeter.loopCount>1</jmeter.loopCount> <jmeter.rampUpPeriod>60</jmeter.rampUpPeriod> <jmeter.servers>localhost</jmeter.servers> </properties> 負荷テスト環境用プロファイル Maven実行時に値を調整できるようにプロパティ化すると便利。 $ mvn clean verify -Djmeter.numberOfThreads=100 -Djmeter.servers=10.0.0.41,10.0.0.42
  23. 23. JMeter Maven Plugin $ mvn clean verify ... [INFO] --- jmeter-maven-plugin:1.10.0:jmeter (default) @ sample-jmeter --- [INFO] [INFO] ------------------------------------------------------- [INFO] P E R F O R M A N C E T E S T S [INFO] ------------------------------------------------------- [info] [info] Executing test: com.github.nagaseyasuhito.sample.jmeter.EchoEndpointST.jmx [info] Creating summariser <summary> [info] Created the tree successfully using /Users/nagaseyasuhito/Documents/workspace/ [info] Configuring remote engine for localhost [info] Using remote object: UnicastRef [liveRef: [endpoint:[10.48.138.59:62076](remot [info] Starting remote engines [info] Starting the test @ Fri Apr 03 18:16:30 JST 2015 (1428052590810) [info] Remote engines have been started [info] Waiting for possible shutdown message on port 4446 [info] summary + 98 in 58.2s = 1.7/s Avg: 6 Min: 4 Max: 11 Err: [info] summary = 100 in 59.4s = 1.7/s Avg: 7 Min: 4 Max: 146 Err: [info] Tidying up remote @ Fri Apr 03 18:17:30 JST 2015 (1428052650888) [info] Exitting remote servers [info] ... end of run [info] Completed Test: com.github.nagaseyasuhito.sample.jmeter.EchoEndpointST.jmx [INFO] [INFO] Test Results: [INFO] [INFO] Tests Run: 1, Failures: 0
  24. 24. Running On Jenkins Performance Plugin JMeterが出力する負荷テストの結果をJenkinsでプロットするプラグイン。Report filesに **/*.jtl のようにプロットする結果ファイルのパスを記述する。
  25. 25. Running On Jenkins Performance Plugin JMeterが出力する負荷テストの結果をJenkinsでプロットするプラグイン。Report filesに **/*.jtl のようにプロットする結果ファイルのパスを記述する。
  26. 26. Bug And Bottleneck https://www.flickr.com/photos/mjhagen/2973212926 Scream by Mingo Hagen is licensed under CC BY 2.0 / Added some texts to original 他ユーザーのレスポンスが返ってくる レスポンスが返ってこない リソースは余っているのにレスポンスが遅い リクエストが遅い レスポンスが遅い
  27. 27. https://www.flickr.com/photos/dailym/6790546237 bottleneck by ferrie=differentieel & Jöran Maaswinkel DailyM.net is licensed under CC BY 2.0 / Added some texts to original ボトルネックを探すぞ
  28. 28. Ganglia Resource Monitoring ボトルネックやスケールアウトの計画を立てるためにリソースのモニタリングは重要。 ロードアベレージ・CPU・メモリ・ディスクアクセス・スワップ・ネットワーク転送量・コネ クション数・GC頻度・ヒープ使用率・スレッド数など気になるところは可視化しておく。 Zabbix、Munin、MRTG、Sensuなど。下記はGangliaの例。
  29. 29. Ganglia jmxsh plugin JXMの情報をGangliaで取得するためのプラグイン。下記のオプションを有効にする。 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8887 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
  30. 30. Ganglia jmxsh plugin プラグインの設定ファイルに値を取得するホストの情報と、プロットする値を設定する。 metricの値の末尾に##diffを追加すると差分、##deltaを追加すると増分をプロットする。 Total Thread Countなどの積算値は増分をプロットすると見やすい。 modules { module { name = 'jmxsh' language = 'python' param host { value = 'localhost' } param port { value = '8887' } param name { value = 'jvm' } param metric_group { value = 'jvm' } param heap { value = 'java.lang:type=Memory HeapMemoryUsage' } param total_started_thread_count { value = 'java.lang:type=Threading TotalStartedThreadCount' } } } collection_group { collect_every = 15 time_threshold = 45 metric { name = 'jmx_jvm_heap' } metric { name = ‘total_started_thread_count##delta’ } }
  31. 31. Flight Recorder Mission Control Flight Recorderで取得したJVMの統計情報を可視化するツール。 -XX:+UnlockCommercialFeatures -XX:+FlightRecorder というオプションを付けてアプリケーションサーバーを起動し、jcmdコマンドで統計情報を取 得する。 # プロファイル開始 jcmd [プロセスID] JFR.start # データのダンプ jcmd [プロセスID] JFR.dump filename="[出力ファイル名]" recording=[レコードID]
  32. 32. https://www.flickr.com/photos/chidorian/106706292 Shogi by Ishikawa Ken is licensed under CC BY 2.0 / Added some texts to original JPAの スケールアウト 戦略
  33. 33. READが 頭打ち https://www.flickr.com/photos/mjhagen/2973212926 Scream by Mingo Hagen is licensed under CC BY 2.0 / Added some texts to original
  34. 34. Master Slave Replication Master-Slave Replication 更新系クエリ(INSERT / UPDATE / DELETE)はマスターへ発行し、検索系クエリ(SELECT) はスレーブに発行することで負荷を分散させる。 スレーブは負荷に応じて複数台用意できる。 ReplicationINSERT UPDATE DELETE Master Database Slave DatabaseApplication SELECT
  35. 35. MySQL Replication Driver MySQL ReplicationDriver com.mysql.jdbc.ReplicationDriverというJDBCドライバを使う。 jdbc:mysql:replication://master,slave1,slave2…/database java.sql.Connection#setReadOnly(true)した場合はスレーブのホストに発行される。 JPAの場合はentityManagerのunwrapメソッドでjava.sql.Connectionを取得する。 // for EclipseLink entityManager.unwrap(Connection.class).setReadOnly(true); // for Hibernate entityManager.unwrap(SessionImplementor.class).connection().setReadOnly(true); User user = entityManager.find(User.class, 1L);
  36. 36. WRITEも 頭打ち https://www.flickr.com/photos/mjhagen/2973212926 Scream by Mingo Hagen is licensed under CC BY 2.0 / Added some texts to original
  37. 37. Partitioning Partitioning 一定のルールに従ってクエリを発行するデータベースを分ける。IDの剰余などをキーにして振 り分けるのが一般的。 ジョインやソートができないのでアプリケーションの設計にも影響あり。 Master Database Application Master Database ID:1,3,5,7... ID:2,4,6,8...
  38. 38. EclipseLink Partitioning EclipseLink Partitioning EclipseLinkにはパーティショニング(シャーディング)の機能があり、複数のデータベースにク エリを振り分けられる。 @HashPartitioningの他に@ValuePartitioningや@RangePartitioningなど用途によって複数 のパーティショニング戦略が用意されている。 @Entity @HashPartitioning( name = "hashPartitioningById", partitionColumn = @Column(name = "id"), connectionPools = { "pool0", "pool1" }, unionUnpartitionableQueries = true) @Partitioned("hashPartitioningById") public class User { @Id @GeneratedValue(strategy = GenerationType.TABLE) private Integer id; @Column(nullable = false, unique = true) private String mailAddress; }
  39. 39. EclipseLink Partitioning EclipseLink Partitioning EclipseLinkにはパーティショニング(シャーディング)の機能があり、複数のデータベースにク エリを振り分けられる。 HibernateはHibernate Shardsというサブプロジェクトがあるが最近はメンテナンスが止 まっている。 <persistence-unit name="freesia" transaction-type="JTA"> <jta-data-source>jdbc/freesia</jta-data-source> <properties> <property name="eclipselink.connection-pool.pool0.jtaDataSource" value="jdbc/freesia0" /> <property name="eclipselink.connection-pool.pool1.jtaDataSource" value="jdbc/freesia1" /> </properties> </persistence-unit>
  40. 40. EclipseLink Partitioning EclipseLink Partitioning where句にpartitioningColumnで指定したカラムがある場合。 FROM User u WHERE u.id = 1 Master Database Application Master Database partitioningColumnをキーにして特 定のデータベースにクエリを発行する。 ID:1,3,5,7... ID:2,4,6,8...
  41. 41. EclipseLink Partitioning EclipseLink Partitioning where句にpartitioningColumnで指定したカラムがない場合。 FROM User u WHERE u.name = 'nagaseyasuhito' Master Database Application Master Database ID:1,3,5,7... ID:2,4,6,8... どのデータベースに対してもクエリが発 行されない。
  42. 42. EclipseLink Partitioning EclipseLink Partitioning unionUnpartitionableQueriesにtrueを設定し、where句にpartitioningColumnで 指定したカラムがない場合。 FROM User u WHERE u.name = 'nagaseyasuhito' Master Database Application Master Database ID:1,3,5,7... ID:2,4,6,8... すべてのデータベースにクエリが発行さ れ結果は結合される。
  43. 43. Master Slave Replication Partitioning Master-Slave Replication & Partitioning もちろんこれらの合わせ技も可能。 Master Database Application Master Database Replication Slave Database Replication Slave Database ID:1,3,5,7... ID:2,4,6,8...
  44. 44. Conclusion https://www.flickr.com/photos/ensh/3440275790 Heart of Light by Emmanuel Huybrechts is licensed under CC BY 2.0 / Added some texts to original
  45. 45. Conclusion 負荷テストの目的は • システムの限界性能を知る • 高負荷時の不具合を発見する JPAのスケールアウト戦略は • マスター/スレーブのレプリケーション • パーティショニング https://www.flickr.com/photos/ensh/3440275790 Heart of Light by Emmanuel Huybrechts is licensed under CC BY 2.0 / Added some texts to original

×