111
Seasar2から Springへ移行した 俺たちのアプリケーションが マイクロサービス アーキテクチャへ歩み始めた フリュー株式会社 / 関西Javaエンジニアの会 阪田 浩一 @jyukutyo #ccc_f5

JJUG CCC 2017 Spring Seasar2からSpringへ移行した俺たちのアプリケーションがマイクロサービスアーキテクチャへ歩み始めた

Embed Size (px)

Citation preview

Seasar2からSpringへ移行した

俺たちのアプリケーションがマイクロサービス

アーキテクチャへ歩み始めた

フリュー株式会社 /関西Javaエンジニアの会

阪田浩一 @jyukutyo

#ccc_f5

関ジャバ会長は、フリュー所属

通称: じゅくちょー

@jyukutyo

私のセッションは

• 私の現場の、私のコンテキストでの、現実解です。

• 書籍にあるような理想形を実現するものではありません。

• アカデミックさよりビジネスと技術を等価で捉えています。

Agenda

• Spring + Netflix OSSでのマイクロサービス化

• 試験でのSpring Cloud Contract Stub Runnerの利用

伝えたいこと

マイクロサービス、

はじめの一歩くらいなら

そんな大仰なことじゃない

サービスの変容を連載中

• 2015 Spring• クリスマスを支える俺たちとJava

• 2016 Spring• Seasar2で作った俺たちのサービスの今

• 2016 Fall• 10年運用している画像サービスでのJavaの活用と今後の展望

• 2017 Spring <- now• Seasar2からSpringへ移行した俺たちのアプリケーションがマイクロサービスアーキテクチャへ歩み始めた

前回までのあらすじ

Application (WAR)

S2Servlet DispatcherServlet

DIコンテナ DIコンテナ

Cubby Action

Service / Logic

S2Dao DAO

Interceptor

MVC Controller

Service / Logic

S2Dao DAO

Interceptor

Doma2 DAO

同一のJS/CSS/テンプレートファイル

1つのWARに

2つのDIコンテナ

Springだけに

なるように

作業中だけど、

今回はスコープ外

対象について

対象アプリケーション

サービス名 ピクトリンク

開始

2011年(前身サービスはもっと以前から)

利用者数1,000万人超

(有料会員数は秘密)

主な機能

プリントシール機で撮影した画像を取得する + SNS的な

対象アプリケーション

サービス名 ピクトリンク

提供

WebアプリケーションiOSアプリAndroidアプリ

課金

キャリア課金

d/au/SB楽天ID決済クレカ決済iOS課金Android課金

ユーザ数に比例して

•13万行のコードに(ライブラリは含まず)

並行開発/デイリーリリース

リポジ

トリ

チームA

チームCチームB

あるある

• 今日チームAがリリースするからチームBはちょっと待って

• ビルド時間が長い

あるある

• RDBMSが巨大になってきた• Oracle EE RACを今年購入した

• 13億行のテーブル...

• 負荷増大に対してフルスペックのサーバを追加する• お高い

• オンプレミスです

よろしい、ならば

サービスごとにスケール的な

前提条件

開発はビジネス要件

(新機能開発・既存改修)

と並行して実施する

第1目標

サービスクラス以降の

処理を単純に

1つのマイクロサービスに

してみる

理由

初めからきれいな

ドメイン分割を

気にしすぎても

前に進めなさそう

理由

やってみて

フィードバックを得て、

サービスを

運用改善していく

Application (WAR)

S2Servlet DispatcherServlet

DIコンテナ DIコンテナ

Cubby Action

Service / Logic

S2Dao DAO

Interceptor

MVC Controller

Service / Logic

S2Dao DAO

Interceptor

Doma2 DAO

同一のJS/CSS/テンプレートファイル

何が必要か

クライアント

サービスインスタンスA

サービスインスタンスB

サービスインスタンスC

10.5.83.126

10.5.83.127

10.5.83.128ロードバランスはどうする?

インスタンス数は動的に変更できる?

何が必要か

クライアント

サービスインスタンスA

サービスインスタンスB

サービスインスタンスC

10.5.83.126

10.5.83.127

サービスディスカバリ

ディスカバリを認識する

HTTPクライアント

126,127,128

10.5.83.128

• eureka• Service Discovery

• ribbon• Client Side Load Balancing

• Hystrix• Circuit Breaker

• など

クライアント

サービスインスタンスA

サービスインスタンスB

サービスインスタンスC

10.5.83.126

10.5.83.127

Eureka

ribbon

10.5.83.128

Spring

• Spring Cloud Netflix

• Spring Bootと簡単に連携できる

俺たちのサービス

Spring MVC

Spring Boot

Spring Boot

Spring Boot

Eureka

ribbon

!?

SpringCloudNetflix

Eureka

Seasar2部分が

残っているので、

Spring Bootではなく

MVCのまま

最初の移行対象

クレジットカード決済

機能

クレジットカード決済

• 外部の決済代行サービスを利用している

• 決済手段としては利用が多くない

• かといってどうでもいい機能でもない

メンバーの状況

• Spring MVCは慣れてきた

• Spring Boot/Cloudは知らない

•マイクロサービスは概念から知らない

メンバーの学習

• 各自書籍で学ぶ

• マイクロサービス概念、Netflix OSSは説明会を数回

機械的かと思いきや

コードを変更する

部分も多い

この部分は

クライアント?MS?

どっちに置く?

処理の順序とか

なんかこう入り組んだ

コードとか

ユニットテスト重要

もともと各クラスに

必ずテストクラスを

作っていた

期間 2016年11月〜17年1月

リリース 2017年1月末

ログ監視

Spring Boot ログファイル1

Spring Boot ログファイル2

インフラチームが構築

プロセス監視など

•監視会社に追加依頼

2つめの移行

別の課金機能を

同様に作業して

リリース済み

課題

マイクロサービスとして

堂々と紹介できる

ほど完成していない

ビッグバンはしない。

これまで通り

長期間に渡って

少しずつ進める

エンジニアの学習、

フィードバックループ

にとって一番いい

課題

すべてのサービスが

同じRDBMSを

使っている

書籍にもあるように

アンチパターン

これを

こうしたい

課題

オーケストレーション

方式

オーケストレーション

アプリケーション

サービスA

サービスB

サービスCアプリケーション

呼び出し側の負担が大きい

課題

コレオグラフィも

検討したい

コレオグラフィで

アプリケー

ションイベント

サービスA

サービスB

サービスC

パブリッシュ/サブスクライブ

サービスD

Spring Cloud Stream

固有の課題

• 元のアプリケーションがSpring BootではなくMVCである

• Spring Cloud使えない

• ribbon/Eurekaをそのまま使う

ribbonでのAPI呼び出し

コード

@ResourceLoadBalancingHttpClient<ByteBuf, ByteBuf>

ribbonClient;

...

HttpClientRequest<ByteBuf> request =

HttpClientRequest.createPost(url)

.withContent(“...”)

.withHeader("Content-type",

"application/json");

HttpClientResponse<ByteBuf> httpClientResponse =

ribbonClient

.submit(request)

.toBlocking()

.first();

HttpResponseStatus status =

httpClientResponse.getStatus();

ribbonとEurekaの連携は

ライブラリ

ribbon-eureka

を使う

Spring Cloudなら

すぐできるのに...!

と思うこと多数

秋までには

元のアプリケーションも

Bootに移行したい

そうしたら

Spring Cloud Sluethで

分散トレーシングも

入れれる

チームでの感想

• コード分割 + Bootのパワーで起動が早く動作確認しやすくなった

•これだけでも十分メリットだ

チームでの感想

• Spring Bootはテストコードが書きやすい

• Controller

• Application

チームでの感想

• マイクロサービスへの分割で、サービス全体を手動テストして確認する数が減った

ここでテストについて

いわゆる結合試験

• Excelに書いてある

• テスト用レコードを登録し、手でスマホを操作してテストする

•何割かGauge/Selenideで自動テストあり

既存のテスト項目を

そのまま実施して、

OKなら移行完了としたい

自動テストは

課金関連では3割ほど。

あとは手で再実施...

その中に

悩ましいケースが...

throw an exception and return

500 HTTP status code

人為的に

エラー動作を

どのように起こすか?

マイクロサービスの

コードを変えて

起動するとかは...

Spring Cloud Contract

Stub Runner

こんなGroovy DSLを

書いて...

org.springframework.cloud.contract.spec.Contract.make {

request { method 'POST' url '/example' body([ "id": 12345 ])

} response {

status 201 body([ "name": "john" ]) headers {

contentType('application/json;charset=UTF-8’)

} }

}

スタブを配備し

Stub Runnerを

起動すると...

$ java -jar -Dstubrunner.ids=com.jyukutyo:stub-example stub-runner.jar

DSLのとおりに動作する

Webアプリケーションを

起動してくれる

$ curl -D- -X post -H 'Content-Type:application/json' -d "{"id":12345}" http://localhost:14161/example

HTTP/1.1 201 CreatedContent-Type: application/json;

charset=utf-8Transfer-Encoding: chunkedServer: Jetty(9.3.16.v20170120)

{"name":"john"}

org.springframework.cloud.contract.spec.Contract.make {

request { method 'POST' url '/example' body([ "id": 12345 ])

} response {

status 201 body([ "name": "john" ]) headers {

contentType('application/json;charset=UTF-8’)

} }

}

これで

マイクロサービスの

エラー動作を

作り出せる

技術的詳細

Spring Cloud Contract

Stub Runner

の前に

Spring Cloud Contract

• コンシューマ駆動契約(CDC)フレームワークの1つ

• APIを使う側がほしい仕様(契約)を書き、提供側は契約を常に満たす• 使う側: コンシューマ

• 提供側: プロデューサ

Spring Cloud Contract

• 契約をGroovy DSLで書ける

• DSLからスタブを生成できる

• WireMockを使ってスタブをアプリケーションとして起動する• Spring Cloud Contract WireMock

WireMock

• HTTPベースのAPIシミュレーターhttp://wiremock.org/

契約の使い道

• プロデューサはAPIがつねに契約を満たすかの検証に使える

• コンシューマは契約内容をスタブにしてテストできる

そのスタブを簡単に

アプリケーションとして

起動してくれるのが

Spring Cloud Contract

Stub Runner

Spring Cloud ContractStub Runner

• スタブをJARにして、Mavenリポジトリに置いておく• ローカル、インハウスでも

• Stub Runnerはリポジトリからスタブをダウンロードし適当なポートで起動する

Spring Cloud ContractStub Runner

• 複数のスタブも1つのStub Runner から起動できる

• スタブをサービスディスカバリ(Eureka)に登録もできる

https://github.com/spring-cloud/spring-cloud-contract/tree/master/spring-cloud-contract-stub-runner

クライアント

サービスインスタンスA

サービスインスタンスB

サービスインスタンスC

Eureka

ribbon

設定変更なし!

クライアント

スタブ

Eureka

ribbon

スタブをMavenリポジトリへ

Artifactoryなど開発者1

MS1のスタブJAR

開発者2

MS2のスタブJAR

GitでDSLを管理

$ mvn clean deploy/install

org.springframework.cloud.contract.spec.Contract.make {

request { method 'POST' url '/example' body([ "id": 12345 ])

} response {

status 201 body([ "name": "john" ]) headers {

contentType('application/json;charset=UTF-8’)

} }

}

{"uuid" : "91053c7f-106d-4441-9dd0-

b2b0fccb3388","request" : {"url" : "/example","method" : "POST","bodyPatterns" : [ {"matchesJsonPath" : "$[?(@.id ==

12345)]"} ]

},"response" : {"status" : 201,"body" : "{\"name\":\"john\"}","headers" : {"Content-Type" : "application/

json;charset=UTF-8”

WireMockのJSON

Stub Runner起動

ArtifactoryなどStubRunner

MS1のスタブJAR

MS2のスタブJAR

Eureka

MS1:10001 MS2:10002

$ java -jar -Dstubrunner.ids=[groupId]:

[artifactId],(略)

-Dstubrunner.idsToServiceIds.[スタブ1のartifactId]=hoge

-Dstubrunner.idsToServiceIds.[スタブ2のartifactId]=fuga

stub-runner.jarEurekaへの登録名

/stubs

スタブのポート

Eurekaと

連携させていれば

Eurekaからでも

確認できる

Spring Cloud Contract Verifier

• テストコードでスタブを利用できる

• まだ使っていないので紹介だけ

@RunWith(SpringRunner.class)@SpringBootTest(

webEnvironment=WebEnvironment.NONE)@AutoConfigureStubRunner(

ids = {"com.example:http-server-dsl:+:stubs:6565"},

workOffline = true)@DirtiesContextpublic class LoanApplicationServiceTests {

参考: InfoQ記事

Spring Cloud Contractについて

Marcin Grzejszczak氏とのQ&A https://www.infoq.com/jp/news/2017/05/spring-cloud-contract

(公式翻訳: 私)

展望

• 2年くらいでマイクロサービス化が落ち着けばいいな

ご清聴

ありがとうございました