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

Preview:

Citation preview

CyberAgent, Inc. All Rights Reserved

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

1

自己紹介

向井 政貴

株式会社サイバーエージェント

サーバサイドエンジニア

アメーバブログの開発・運用を担当

最近会社の料理部が楽しい

2

2015年の状況

システム刷新

まとめ

アメーバブログとは

3

CyberAgent, Inc. All Rights Reserved

アメーバブログ

4

アメーバブログ

PC、スマホ、ガラケー、アプリ

ランキングなど周辺サービス

5

アメーバブログ

2004年9月サービス開始

約12年続くサービス

6

CyberAgent, Inc. All Rights Reserved

2015年アメブロの状況

7

古いバージョンの技術

8

MySQL 4系

CentOS 4系

java 1.5 ~ 1.7

9

複数のプロダクトに

似たような処理

10

ブラウザから記事投稿できるよねアプリからも記事をかけるようにしたい!

同じ機能だけどAPIとして新しく作るか・・

アメブロ

記事投稿

API

記事投稿

11

API

ブラウザから読者登録できるよねアプリからも読者登録したい!

アメブロ

記事投稿

API

記事投稿

読者登録 読者登録

同じ機能だけどAPIとして新しく作るか・・

12

API API

調査用に記事情報を取得したい!

アメブロ

記事投稿

API

記事投稿

読者登録 読者登録

同じ機能だけどAPIとして(ry

記事取得 記事取得

13

フレームワークの混在

14

Struts 1.x , S2Struts, Spring 3.x

● Struts から Spring3 に移行しようと頑張った

形跡はある…

● ややこしい

● 学習コスト

15

苦労の多い動作確認環境

16

動作確認方法の歴史

2013年

.class

SCP!!!!

ビルド

.class

チーム共用ステージングサーバ

17

動作確認方法の歴史

2013年

2014年 個人開発用サーバ

PUSH

DEPLOY

18

動作確認方法の歴史

2013年

2014年 個人開発用サーバ

2015年 ココ

19

失われた思想(?)

20

実際のコード例:記事データ検索メソッド

public void search(boolean showEntryTextFlag) {

!? !?

21

いろいろあるけど大まかに言うと

22

保守性の低下

増大していく開発コスト

ビューとビジネスロジックの強い依存関係

刷新前システムの課題

23

CyberAgent, Inc. All Rights Reserved

さらに

24

CyberAgent, Inc. All Rights Reserved_人人人人人人人人人人人_>  データセンター移設  <

 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄

25

アメブロのシステムの大部分が

稼働していたDCから撤退

26

保守機材の在庫が限界

27

LBやコアスイッチの調子が悪化

28

1. 既存システムのまま移設する

2. 作り直して移設する

選択肢

29

どちらにするか

30

作り直すとして間に合うのか

31

何か障害が起きないか

32

怖い

33

刷新前のシステムつらい

34

面白くない

35

このままだと新しい人が入ってこない

36

世間の技術トレンドから遅れる

37

開発速度は遅くなる一方

38

39

CyberAgent, Inc. All Rights Reserved

システム刷新だ

40

システム規模

41

40超

数百台

刷新後のあるAPIサーバ

へのリクエスト数 60万リクエスト/m

サーバ台数

プロジェクト数

5300万人(2016年8月時点)会員数

ページがみれないと広告が表示されないため売上に直撃

社内他サービスへの影響

芸能事務所から問い合わせ

システムになにかあると...

42

CyberAgent, Inc. All Rights Reserved

システム刷新の対象

43

44

フロントエンド

バックエンド

CyberAgent, Inc. All Rights Reserved

システム刷新の要件

45

障害なし

長期のサービス停止は回避

DC移設までに完遂

画面や機能の仕様を維持

開発コスト減少

一般的なアーキテクチャ

MUST WANT

46

47

CyberAgent, Inc. All Rights Reserved

Why Spring?

48

低い導入コスト

開発陣の慣れ

テンプレートの使い回し

刷新前のシステムにSpringの採用実績

49

ローカル開発環境

開発コストの削減

ローカル開発による生産性の向上

50

@Controller, @Service, @Repository

共通の認識があるレイヤードなアーキテクチャ

アーキテクチャを統一しやすい

51

CyberAgent, Inc. All Rights Reserved

システム刷新

52

保守性の低下

増大していく開発コスト

ビューとビジネスロジックの強い依存関係

刷新前システムの課題(再掲)

53

保守性の低下

増大していく開発コスト

ビューとビジネスロジックの強い依存関係

従来システムの課題

54

CyberAgent, Inc. All Rights Reserved

保守性の低下

55

処理が適切な粒度でまとまっていない

56

刷新前システムの課題

アメブロ

ORM

ビジネスロジック

57

人によって処理を書く場所がバラバラ

再利用性や可読性が低い

刷新前のシステム

レイヤードアーキテクチャ

58

刷新後システムの設計

Controller

Facade

Service

Repository

リクエストを受けて結果を返す

一操作に必要なServiceの呼び出し

最低限、まとめておくべきビジネスロジック

DBアクセスやAPIの呼び出し

59

ポリシーの統一化

再利用性、可読性向上

メンテされているのかわからない

APIドキュメント

刷新前システムの課題

60

wikiにAPIドキュメント用意

手動でメンテナンス

実際のソースコードとの整合性が不安

61

Springfox + Swaggerによる

自動ドキュメント化

62

https://github.com/springfox/springfox

https://github.com/swagger-api/swagger-ui

63

@RequestMapping( method = RequestMethod.GET, value = "/v1.0/public/blogger/{amebaId}" ) public BloggerResponse getBlogger ( @PathVariable("amebaId") String amebaId ) { return bloggerFacade.getBlogger(amebaId); }

64

エンドポイントを自動的にドキュメントに反映

複雑なドキュメントはSwaggerのアノテーションが必要

エンドポイントのきれいな一覧だけでもだいぶありがたい

65

ここまでで25分ぐらい

66

保守性の低下

増大していく開発コスト

ビューとビジネスロジックの強い依存関係

刷新前システムの課題

67

自己紹介

服部 拓也

2013年 株式会社サイバーエージェント入社

入社以来アメーバブログのサーバーサイドを担当

現在はディレクションとマネジメントを兼務

マイブームは喘息

68

CyberAgent, Inc. All Rights Reserved

69

 開発コスト

70

ローカル開発環境ない ・ 

動作確認方法の歴史 (再掲)

2013年

2014年 個人開発用サーバ

2015年 ココ

71

72

73

ローカル開発環境構築が容易

フロントエンドエンジニアでも簡単

-> 開発速度UP

74

プロダクトごとに

リポジトリが分散

75

76

77

ソースコードもリリースジョブも

再利用性が低い

削減できるはずのコスト

78

リポジトリの統合 ・

79

マルチプロジェクト化

80

81

リリースジョブを共通化できる

処理の見通しがよくなる

処理を共通化しやすくなる

刷新後システムの処理共通化

API βAPI α

Controller

Facade

Service

Repository

Controller

Facade・・・

82

マルチプロジェクト構成によるコードの共通化

リリースジョブの統一化

-> 開発効率UP

83

CyberAgent, Inc. All Rights Reserved

だがしかし…

84

まとめたことによって

不要なのにBean化されてしまうクラスが増大

85

86

起動のためだけに

不要なBeanの設定が必要

まとめたことによってBean化されるクラスが増大

リポジトリ

Project A

Project B

Service Project Repository Project

87

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

Project A

Project B

88

リポジトリ

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

Project A

89

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

Project A

90

どうしたか

まずは Service

91

FacadeクラスがAutowiredしているServiceクラスだけを

Bean化しよう!

● @Facadeアノテーションを作った

● @FacadeのついたクラスをBean化して、

そのFacadeクラスがAutowiredしているServiceクラスだけを

Bean化するInitializerを作った

92

@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

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

@Facade

@Service

@Service

repository D

@Service

@Service

Project A

94

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

@Facade

@Service

@Service

repository D

@Service

@Service

95

Project A

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

@Facade

@Service

@Service

repository D

@Service

@Service

96

Project A

どうしたか

Repository編

97

プロダクト毎に接続先DBの情報をapplication.ymlに宣言する

● 宣言した接続先に関連するRepositoryだけをBean化する

Initializerを作った

98

まとめたことによってBean化されるクラスが増大

A repository B repository

C Datasource

C repositoryA repository B repository

C repository

application.ymlInitializer

99

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

@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

適用例

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

repository D

102

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

repository D

103

外部API接続用のRepositoryが残ってる

104

RepositoryクラスのBean化(API接続用)

@Configurationpublic 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: httphost: sample.ameba.jpport: 80

105

RepositoryクラスのBean化(API接続用)@ConditionalOnProperty (

prefix = "sample", name = "enabled",havingValue = "true",matchIfMissing = false

)@Configurationpublic 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: httphost: sample.ameba.jpport: 80enabled: true

106

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

repository D

107

まとめたことによってBean化されるクラスが増大

facadeservice B

repository B

facade

service A

service C

service D

repository A

repository C

リポジトリ

repository D

108

共通化して起こった問題もSpringの機能で解決

109

保守性の低下

増大していく開発コスト

ビューとビジネスロジックの強い依存関係

刷新前システムの課題

110

CyberAgent, Inc. All Rights Reserved

 ビューとビジネスロジックの 強い依存関係

111

Data Store

刷新前のシステム

アメブロ

ビジネスロジック

ORM

他サービスAPI

他サービスのAPI

112

ビュー

刷新前のチームとシステムの関わり方

113

サーバーサイドフロントエンド

刷新前のチームとシステムの関わり方

サーバーとフロントでチームが別れている

ただし同じシステムをさわる

フロントエンドの最新技術導入しづらい

アメブロ

ORM

ビジネスロジック

ビュー

114

刷新前の他部署との連携状況

115

刷新前の他部署との連携状況アメブロ

ビジネスロジック

ORM

秋葉原ラボ

ティーン向けブログサービス

etc..

記事データ取りたい

アメブロにも投稿したい

この中に組み込まれている機能を使いたい

116

ビュー

◯◯API

ORM

新規作成

ビジネスロジック

API Centricな

アーキテクチャ ・

117

Data Store

刷新前のシステム(再掲)

アメブロ

ビジネスロジック

ORM

他サービスAPI

他サービスのAPI

118

ビュー

刷新後のシステム

PC アメブロ

Controller

API

Facade

Service

スマホ アメブロ

Controller

Facade

Service

Controller

Facade

Service

Repository

ビューを生成する処理に集中

ビジネスロジックの提供に集中

Data Store

他サービスのAPI

119

JSON

ビュー ビュー

すぐに良いことが

120

他部署からの依頼対応工数の削減

AMPを特別対応なしで完了

スマホ版フロントエンドの刷新も特別対応なし

  参考) アメブロ2016 ~ React/ReduxでつくるIsomorphic web app ~

https://developers.cyberagent.co.jp/blog/archives/636/

121

CyberAgent, Inc. All Rights Reserved

API Centricの課題

122

通信コスト

123

複数レイヤにキャッシュを入れて対応

PC アメブロ

Controller

API

Facade

Service

スマホ アメブロ

Controller

Facade

Service

Controller

Facade

Service

Repository

Data Store

他サービスのAPI

124

ビュー ビュー

Client でキャッシュ

Repositoryでキャッシュ

APIの呼び出し元・先が

わかりづらい

(静的な意味で)

125

未解決

126

CyberAgent, Inc. All Rights Reserved

まとめ

127

技術面

ビジネス面

組織面

128

技術面でよかったこと

129

フロントエンドの技術がトレンドに乗りやすくなった

参考) アメブロ2016 ~ React/ReduxでつくるIsomorphic web app ~

https://developers.cyberagent.co.jp/blog/archives/636/

一般的なアーキテクチャに則った作りにできた

セキュリティリスクも抑制できている

技術的によかったこと

130

ビジネス面でよかったこと

131

AMP対応を短期間で完遂

フロントエンドを刷新しPVや広告収益が

短期間でより多くの施策が実現可能に

ビジネス的によかったこと

132

組織面でよかったこと

133

フロントエンドの開発チームを分離できた

新規の人材を取り込みやすくなった

よりよいサービスにしようという意識が強くなった

組織的によかったこと

134

CyberAgent, Inc. All Rights Reserved

最後に

135

本当に

やってよかった

136

Recommended