アメブロの大規模システム刷新と それを支えるSpring

10,641 views

Published on

Spring Day 2016 の発表資料です

Published in: Engineering
0 Comments
16 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
10,641
On SlideShare
0
From Embeds
0
Number of Embeds
5,253
Actions
Shares
0
Downloads
31
Comments
0
Likes
16
Embeds 0
No embeds

No notes for slide

アメブロの大規模システム刷新と それを支えるSpring

  1. 1. CyberAgent, Inc. All Rights Reserved アメブロの大規模システム刷新と それを支えるSpring 1
  2. 2. 自己紹介 向井 政貴 株式会社サイバーエージェント サーバサイドエンジニア アメーバブログの開発・運用を担当 最近会社の料理部が楽しい 2
  3. 3. 2015年の状況 システム刷新 まとめ アメーバブログとは 3
  4. 4. CyberAgent, Inc. All Rights Reserved アメーバブログ 4
  5. 5. アメーバブログ PC、スマホ、ガラケー、アプリ ランキングなど周辺サービス 5
  6. 6. アメーバブログ 2004年9月サービス開始 約12年続くサービス 6
  7. 7. CyberAgent, Inc. All Rights Reserved 2015年 アメブロの状況 7
  8. 8. 古いバージョンの技術 8
  9. 9. MySQL 4系 CentOS 4系 java 1.5 ~ 1.7 9
  10. 10. 複数のプロダクトに 似たような処理 10
  11. 11. ブラウザから記事投稿できるよね アプリからも記事をかけるようにしたい! 同じ機能だけどAPIとして 新しく作るか・・ アメブロ 記事投稿 API 記事投稿 11
  12. 12. API ブラウザから読者登録できるよね アプリからも読者登録したい! アメブロ 記事投稿 API 記事投稿 読者登録 読者登録 同じ機能だけどAPIとして 新しく作るか・・ 12
  13. 13. API API 調査用に記事情報を取得したい! アメブロ 記事投稿 API 記事投稿 読者登録 読者登録 同じ機能だけどAPIとして(ry 記事取得 記事取得 13
  14. 14. フレームワークの混在 14
  15. 15. Struts 1.x , S2Struts, Spring 3.x ● Struts から Spring3 に移行しようと頑張った 形跡はある… ● ややこしい ● 学習コスト 15
  16. 16. 苦労の多い動作確認環境 16
  17. 17. 動作確認方法の歴史 2013年 .class SCP!!!! ビルド .class チーム共用 ステージングサーバ 17
  18. 18. 動作確認方法の歴史 2013年 2014年 個人開発用サーバ PUSH DEPLOY 18
  19. 19. 動作確認方法の歴史 2013年 2014年 個人開発用サーバ 2015年 ココ 19
  20. 20. 失われた思想(?) 20
  21. 21. 実際のコード例:記事データ検索メソッド public void search(boolean showEntryTextFlag) { !? !? 21
  22. 22. いろいろあるけど 大まかに言うと 22
  23. 23. 保守性の低下 増大していく開発コスト ビューとビジネスロジックの強い依存関係 刷新前システムの課題 23
  24. 24. CyberAgent, Inc. All Rights Reserved さらに 24
  25. 25. CyberAgent, Inc. All Rights Reserved _人人人人人人人人人人人_ >  データセンター移設  <  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄ 25
  26. 26. アメブロのシステムの大部分が 稼働していたDCから撤退 26
  27. 27. 保守機材の在庫が限界 27
  28. 28. LBやコアスイッチの 調子が悪化 28
  29. 29. 1. 既存システムのまま移設する 2. 作り直して移設する 選択肢 29
  30. 30. どちらにするか 30
  31. 31. 作り直すとして 間に合うのか 31
  32. 32. 何か障害が起きないか 32
  33. 33. 怖い 33
  34. 34. 刷新前のシステムつらい 34
  35. 35. 面白くない 35
  36. 36. このままだと新しい人が 入ってこない 36
  37. 37. 世間の技術トレンド から遅れる 37
  38. 38. 開発速度は遅くなる一方 38
  39. 39. 39
  40. 40. CyberAgent, Inc. All Rights Reserved システム刷新だ 40
  41. 41. システム規模 41 40超 数百台 刷新後のあるAPIサーバ へのリクエスト数 60万リクエスト/m サーバ台数 プロジェクト数 5300万人(2016年8月時点)会員数
  42. 42. ページがみれないと広告が表示されないため売上に直撃 社内他サービスへの影響 芸能事務所から問い合わせ システムになにかあると... 42
  43. 43. CyberAgent, Inc. All Rights Reserved システム刷新の対象 43
  44. 44. 44 フロント エンド バック エンド
  45. 45. CyberAgent, Inc. All Rights Reserved システム刷新の要件 45
  46. 46. 障害なし 長期のサービス停止は回避 DC移設までに完遂 画面や機能の仕様を維持 開発コスト減少 一般的なアーキテクチャ MUST WANT 46
  47. 47. 47
  48. 48. CyberAgent, Inc. All Rights Reserved Why Spring? 48
  49. 49. 低い導入コスト 開発陣の慣れ テンプレートの使い回し 刷新前のシステムにSpringの採用実績 49
  50. 50. ローカル開発環境 開発コストの削減 ローカル開発による生産性の向上 50
  51. 51. @Controller, @Service, @Repository 共通の認識があるレイヤードなアーキテクチャ アーキテクチャを統一しやすい 51
  52. 52. CyberAgent, Inc. All Rights Reserved システム刷新 52
  53. 53. 保守性の低下 増大していく開発コスト ビューとビジネスロジックの強い依存関係 刷新前システムの課題(再掲) 53
  54. 54. 保守性の低下 増大していく開発コスト ビューとビジネスロジックの強い依存関係 従来システムの課題 54
  55. 55. CyberAgent, Inc. All Rights Reserved 保守性の低下 55
  56. 56. 処理が適切な粒度でまとまっていない 56 刷新前システムの課題
  57. 57. アメブロ ORM ビジネスロジック 57 人によって処理を書く場所がバラバラ 再利用性や可読性が低い 刷新前のシステム
  58. 58. レイヤードアーキテクチャ 58
  59. 59. 刷新後システムの設計 Controller Facade Service Repository リクエストを受けて 結果を返す 一操作に必要な Serviceの呼び出し 最低限、 まとめておくべき ビジネスロジック DBアクセスや APIの呼び出し 59 ポリシーの統一化 再利用性、可読性向上
  60. 60. メンテされているのかわからない APIドキュメント 刷新前システムの課題 60
  61. 61. wikiにAPIドキュメント用意 手動でメンテナンス 実際のソースコードとの整合性が不安 61
  62. 62. Springfox + Swaggerによる 自動ドキュメント化 62
  63. 63. https://github.com/springfox/springfox https://github.com/swagger-api/swagger-ui 63
  64. 64. @RequestMapping( method = RequestMethod.GET, value = "/v1.0/public/blogger/{amebaId}" ) public BloggerResponse getBlogger ( @PathVariable("amebaId") String amebaId ) { return bloggerFacade.getBlogger(amebaId); } 64
  65. 65. エンドポイントを自動的にドキュメントに反映 複雑なドキュメントはSwaggerのアノテーションが必要 エンドポイントのきれいな一覧だけでもだいぶありがたい 65
  66. 66. ここまでで25分ぐらい 66
  67. 67. 保守性の低下 増大していく開発コスト ビューとビジネスロジックの強い依存関係 刷新前システムの課題 67
  68. 68. 自己紹介 服部 拓也 2013年 株式会社サイバーエージェント入社 入社以来アメーバブログのサーバーサイドを担当 現在はディレクションとマネジメントを兼務 マイブームは喘息 68
  69. 69. CyberAgent, Inc. All Rights Reserved 69  開発コスト
  70. 70. 70 ローカル開発環境ない ・ 
  71. 71. 動作確認方法の歴史 (再掲) 2013年 2014年 個人開発用サーバ 2015年 ココ 71
  72. 72. 72
  73. 73. 73 ローカル開発環境構築が容易 フロントエンドエンジニアでも簡単 -> 開発速度UP
  74. 74. 74 プロダクトごとに リポジトリが分散
  75. 75. 75
  76. 76. 76
  77. 77. 77 ソースコードもリリースジョブも 再利用性が低い 削減できるはずのコスト
  78. 78. 78 リポジトリの統合 ・
  79. 79. 79
  80. 80. マルチプロジェクト化 80
  81. 81. 81 リリースジョブを共通化できる 処理の見通しがよくなる 処理を共通化しやすくなる
  82. 82. 刷新後システムの処理共通化 API βAPI α Controller Facade Service Repository Controller Facade ・・・ 82
  83. 83. マルチプロジェクト構成によるコードの共通化 リリースジョブの統一化 -> 開発効率UP 83
  84. 84. CyberAgent, Inc. All Rights Reserved だがしかし… 84
  85. 85. まとめたことによって 不要なのにBean化されてしまうクラスが増大 85
  86. 86. 86 起動のためだけに 不要なBeanの設定が必要
  87. 87. まとめたことによってBean化されるクラスが増大 リポジトリ Project A Project B Service Project Repository Project 87
  88. 88. まとめたことによってBean化されるクラスが増大 facade service B repository B facade service A service C service D repository A repository C Project A Project B 88 リポジトリ
  89. 89. まとめたことによってBean化されるクラスが増大 facade service B repository B facade service A service C service D repository A repository C リポジトリ Project A 89
  90. 90. まとめたことによってBean化されるクラスが増大 facade service B repository B facade service A service C service D repository A repository C リポジトリ Project A 90
  91. 91. どうしたか まずは Service 91
  92. 92. FacadeクラスがAutowiredしているServiceクラスだけを Bean化しよう! ● @Facadeアノテーションを作った ● @FacadeのついたクラスをBean化して、 そのFacadeクラスがAutowiredしているServiceクラスだけを Bean化するInitializerを作った 92
  93. 93. @ComponentScan( excludeFilters = { @ComponentScan.Filter( type = FilterType.ANNOTATION, value = Facade.class ), @ComponentScan.Filter( type = FilterType.ANNOTATION, value = Service.class ) } ) 適用例 public class Main {  public static void main(String[] args) {   new SpringApplicationBuilder(Main.class)    .initializers(     new FacadeInitializer()    )    .run(args);  } } 93
  94. 94. まとめたことによってBean化されるクラスが増大 facade service B repository B facade service A service C service D repository A repository C リポジトリ @Facade @Service @Service repository D @Service @Service Project A 94
  95. 95. まとめたことによってBean化されるクラスが増大 facade service B repository B facade service A service C service D repository A repository C リポジトリ @Facade @Service @Service repository D @Service @Service 95 Project A
  96. 96. まとめたことによってBean化されるクラスが増大 facade service B repository B facade service A service C service D repository A repository C リポジトリ @Facade @Service @Service repository D @Service @Service 96 Project A
  97. 97. どうしたか Repository編 97
  98. 98. プロダクト毎に接続先DBの情報をapplication.ymlに宣言する ● 宣言した接続先に関連するRepositoryだけをBean化する Initializerを作った 98
  99. 99. まとめたことによってBean化されるクラスが増大 A repository B repository C Datasource C repositoryA repository B repository C repository application.ymlInitializer 99
  100. 100. 100 jdbc: blog: // [HOST] + [SCHEME] の組み合わせにユニークなネームスペースをつけたイメージ master: url: jdbc:mysql://[HOST]/[SCHEME]?zeroDateTimeBehavior=convertToNull&... initialSize: 1 maxTotal: 1 ... slave: url: jdbc:mysql://[HOST]/[SCHEME]?zeroDateTimeBehavior=convertToNull&... initialSize: 1 maxTotal: 1 ... entry: slave: url: jdbc:mysql://[HOST]/[SCHEME]?zeroDateTimeBehavior=convertToNull&... initialSize: 1 maxTotal: 1 ... application.yml
  101. 101. @ComponentScan( excludeFilters = { @ComponentScan.Filter( type = FilterType.ANNOTATION, value = Repository.class ), @ComponentScan.Filter( type = FilterType.ANNOTATION, value = Facade.class ), @ComponentScan.Filter( type = FilterType.ANNOTATION, value = Service.class ) } ) public class Main {  public static void main(String[] args) {   new SpringApplicationBuilder(Main.class)    .initializers(     new RepositoryInitializer(),     new FacadeInitializer()    )    .run(args);  } } 101 適用例
  102. 102. まとめたことによってBean化されるクラスが増大 facade service B repository B facade service A service C service D repository A repository C リポジトリ repository D 102
  103. 103. まとめたことによってBean化されるクラスが増大 facade service B repository B facade service A service C service D repository A repository C リポジトリ repository D 103
  104. 104. 外部API接続用のRepositoryが 残ってる 104
  105. 105. RepositoryクラスのBean化(API接続用) @Configuration public class SampleConfiguration { @Bean public SampleApiClient SampleApiClient( HttpClient httpClient, @Value("${sample.server.scheme}") String scheme, @Value("${sample.server.host}") String host, @Value("${sample.server.port}") Integer port ) { ・・・ } @Bean public SampleRepository SampleRepository (SampleApiClient sampleApiClient) {・・・} } # application.yml sample: scheme: http host: sample.ameba.jp port: 80 105
  106. 106. RepositoryクラスのBean化(API接続用) @ConditionalOnProperty ( prefix = "sample", name = "enabled", havingValue = "true", matchIfMissing = false ) @Configuration public class SampleConfiguration { @Bean public SampleApiClient SampleApiClient( HttpClient httpClient, @Value("${sample.server.scheme}") String scheme, @Value("${sample.server.host}") String host, @Value("${sample.server.port}") Integer port ) { ・・・ } @Bean public SampleRepository SampleRepository (SampleApiClient sampleApiClient) {・・・} } # application.yml sample: scheme: http host: sample.ameba.jp port: 80 enabled: true 106
  107. 107. まとめたことによってBean化されるクラスが増大 facade service B repository B facade service A service C service D repository A repository C リポジトリ repository D 107
  108. 108. まとめたことによってBean化されるクラスが増大 facade service B repository B facade service A service C service D repository A repository C リポジトリ repository D 108
  109. 109. 共通化して起こった問題も Springの機能で解決 109
  110. 110. 保守性の低下 増大していく開発コスト ビューとビジネスロジックの強い依存関係 刷新前システムの課題 110
  111. 111. CyberAgent, Inc. All Rights Reserved  ビューとビジネスロジックの  強い依存関係 111
  112. 112. Data Store 刷新前のシステム アメブロ ビジネスロジック ORM 他サービスAPI 他サービスのAPI 112 ビュー
  113. 113. 刷新前の チームとシステムの関わり方 113
  114. 114. サーバーサイドフロントエンド 刷新前のチームとシステムの関わり方 サーバーとフロントでチームが別れている ただし同じシステムをさわる フロントエンドの最新技術導入しづらい アメブロ ORM ビジネスロジック ビュー 114
  115. 115. 刷新前の他部署との連携状況 115
  116. 116. 刷新前の他部署との連携状況 アメブロ ビジネスロジック ORM 秋葉原ラボ ティーン向け ブログサービス etc.. 記事データ 取りたい アメブロにも投 稿したい この中に組み込 まれている機能 を使いたい 116 ビュー ◯◯API ORM 新規作成 ビジネスロジック
  117. 117. API Centricな アーキテクチャ ・ 117
  118. 118. Data Store 刷新前のシステム(再掲) アメブロ ビジネスロジック ORM 他サービスAPI 他サービスのAPI 118 ビュー
  119. 119. 刷新後のシステム PC アメブロ Controller API Facade Service スマホ アメブロ Controller Facade Service Controller Facade Service Repository ビューを生成する 処理に集中 ビジネスロジック の提供に集中 Data Store 他サービスのAPI 119 JSON ビュー ビュー
  120. 120. すぐに良いことが 120
  121. 121. 他部署からの依頼対応工数の削減 AMPを特別対応なしで完了 スマホ版フロントエンドの刷新も特別対応なし   参考) アメブロ2016 ~ React/ReduxでつくるIsomorphic web app ~ https://developers.cyberagent.co.jp/blog/archives/636/ 121
  122. 122. CyberAgent, Inc. All Rights Reserved API Centricの課題 122
  123. 123. 通信コスト 123
  124. 124. 複数レイヤにキャッシュを入れて対応 PC アメブロ Controller API Facade Service スマホ アメブロ Controller Facade Service Controller Facade Service Repository Data Store 他サービスのAPI 124 ビュー ビュー Client でキャッシュ Repositoryでキャッシュ
  125. 125. APIの呼び出し元・先が わかりづらい (静的な意味で) 125
  126. 126. 未解決 126
  127. 127. CyberAgent, Inc. All Rights Reserved まとめ 127
  128. 128. 技術面 ビジネス面 組織面 128
  129. 129. 技術面でよかったこと 129
  130. 130. フロントエンドの技術がトレンドに乗りやすくなった 参考) アメブロ2016 ~ React/ReduxでつくるIsomorphic web app ~ https://developers.cyberagent.co.jp/blog/archives/636/ 一般的なアーキテクチャに則った作りにできた セキュリティリスクも抑制できている 技術的によかったこと 130
  131. 131. ビジネス面でよかったこと 131
  132. 132. AMP対応を短期間で完遂 フロントエンドを刷新しPVや広告収益が 短期間でより多くの施策が実現可能に ビジネス的によかったこと 132
  133. 133. 組織面でよかったこと 133
  134. 134. フロントエンドの開発チームを分離できた 新規の人材を取り込みやすくなった よりよいサービスにしようという意識が強くなった 組織的によかったこと 134
  135. 135. CyberAgent, Inc. All Rights Reserved 最後に 135
  136. 136. 本当に やってよかった 136

×