Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Spark Framework with Kotlin
Search
stormcat24
February 20, 2017
Programming
3
3.1k
Spark Framework with Kotlin
2017.02.20 JJUG Night Seminor Kotlin
stormcat24
February 20, 2017
Tweet
Share
More Decks by stormcat24
See All by stormcat24
素早く賢く失敗するDeveloper Productivityの実現を目指して
stormcat24
4
4.6k
KubernetesのマニフェストをそれなりにCIしたい
stormcat24
4
1.3k
令和時代のSaaS開発
stormcat24
1
250
History in 5 years of CircleCI and CyberAgent
stormcat24
3
810
Kubernetes Handson Osaka
stormcat24
5
550
Kubernetes Handson
stormcat24
5
4.2k
DockerとKubernetesでアプリケーション開発にコンテナをフル活用!
stormcat24
0
290
Base Image Journey 2018
stormcat24
29
130k
kotlin-fest
stormcat24
13
17k
Other Decks in Programming
See All in Programming
Semantic Kernelのネイティブプラグインで知識拡張をしてみる
tomokusaba
0
180
良いユニットテストを書こう
mototakatsu
5
1.9k
MCP with Cloudflare Workers
yusukebe
2
220
CSC305 Lecture 26
javiergs
PRO
0
140
HTTP compression in PHP and Symfony apps
dunglas
2
1.7k
「Chatwork」Android版アプリを 支える単体テストの現在
okuzawats
0
180
Zoneless Testing
rainerhahnekamp
0
120
Fibonacci Function Gallery - Part 1
philipschwarz
PRO
0
210
ブラウザ単体でmp4書き出すまで - muddy-web - 2024-12
yue4u
2
460
Effective Signals in Angular 19+: Rules and Helpers @ngbe2024
manfredsteyer
PRO
0
130
ゆるやかにgolangci-lintのルールを強くする / Kyoto.go #56
utgwkk
1
360
モバイルアプリにおける自動テストの導入戦略
ostk0069
0
110
Featured
See All Featured
Rails Girls Zürich Keynote
gr2m
94
13k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
95
17k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
226
22k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
159
15k
Building a Modern Day E-commerce SEO Strategy
aleyda
38
7k
Why You Should Never Use an ORM
jnunemaker
PRO
54
9.1k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
5
440
Fireside Chat
paigeccino
34
3.1k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
356
29k
Making Projects Easy
brettharned
116
5.9k
Embracing the Ebb and Flow
colly
84
4.5k
Done Done
chrislema
181
16k
Transcript
2017.02.20 JJUG Night Seminor Kotlin Spark Framework with Kotlin @stormcat24
stormcat24 ‣ CyberAgent, Inc. ‣ FRESH! ‣ DevOps / Docker
Comedian ‣ ワタシコトリンチョットカケルアルヨ ‣ 最近の興味はRust
None
None
technology stack ‣ Microservices Architecture ‣ Full Dockernized ‣ Amazon
Web Services ‣ EC2 Container Services(ECS) ‣ Golang
https://speakerdeck.com/stormcat24/docker-darakefalse-fresh-nadong-hua-pei-xin-puratutohuomu
https://aws.amazon.com/jp/solutions/case-studies/cyberagent/
Golang と FRESH ‣ 2016/01のリリースから一貫してGolangを主要言語として採用 ‣ 1Service = 1Container =
1Repository ‣ Public/Internal問わず十数個のMicroservicesが誕生 ‣ シンプル
しかしつらくなってきた ‣ 基本的に筋力に頼る言語 ‣ 記述量が多い ‣ 高階関数ほしい ‣ 生産性を上げるためのコード生成 ‣
タイプ量 成果と満足感❓ ‣ ゴリゴリとAPIを書いていくのはしんどいというメンバーの総意
新たな基軸言語を求めた
Microservicesの特性を活かす ‣ 言語はServiceによって変えられる ‣ 将来的に変えることも想定していた ‣ Serviceの特性によって、適材適所なものを選んでいくのが重要 ‣ ミドル層、低レイヤー層は引き続きGolang ‣
新たに、一つの基軸言語を選ぶ(乱立は(・A・)イクナイ!!)
None
基軸言語にKotlinを選択 ‣ API実装の基軸言語として、新たにKotlinを採用 ‣ 新しいMicroservicesは基本Kotlinで ‣ Golangの既存Serviceを置き換えるわけではない
Kotlinの理由 ‣ モダンな文法 ‣ IDE(IntelliJ IDEA) ‣ 一番現実的だった ‣ 社内リソース(JVM系人材、Androidでの実績)
助走期間 ‣ 正式に選定する前に、内部の決済用ServiceをKotlinで実装 ‣ Spring Boot ‣ Springfox ‣ AnnotationからSwaggerを吐き出すやつ
‣ domaframework ‣ すごい
感想 ‣ 高い生産性 ‣ そこそこの学習コスト(Java/Spring経験による) ‣ もうちょっと薄いものでいいかもしれない? ‣ MonolithicなServiceを作るわけではないため
Spark Framework
http://sparkjava.com/
Spark Framework is ‣ Java8ベースのマイクロWebフレームワーク ‣ Apache Sparkとは別物 ‣ ググラビリティ・・・
‣ Lambda式を利用 ‣ Imspired by Sinatra ‣ Routing/Filter等最低限の機能、DIとか無いです
In Java package io.stormcat; import static spark.Spark.*; public
class Server { public static void main(String[] args) { get("/echo", (req, res) -> "Hello, " + req.queryParams("name") + "!"); } }
Spark Framework with Kotlin ‣ Java8 Lambda式だけでは満足できない ‣ Spark Frameworkのシンプルさと、Kotlinの良さを活かす
‣ 必要なライブラリを追加して、自力で統合していくスタイル ‣ SpringのようにIntegrationが充実しているわけではないが、相 性の悪さだったり、迷いはあまり無い
Kotlin使ってこ fun main(args: Array<String>) { get("/echo", { req, res
-> "Hello, ${req.queryParams("name")}!" }) }
Spark with Kotlinお品書き ‣ Controller / Routing ‣ Filters ‣
ResponseTransformer(JSON) ‣ data class ‣ 拡張関数 ‣ Guice(Dependency Injection) ‣ 愚直なDI ‣ okhttp ‣ hystrix ‣ Metrics(Jolokia)
Controller package io.stormcat.controller import spark.* class EchoController {
val echo = Route { req, res -> "Hello, ${req.queryParams("name")}!" } }
Routing package io.stormcat import io.stormcat.controller.EchoController import spark.Spark.* fun
main(args: Array<String>) { get("/echo", EchoController().echo) }
Filters package io.stormcat.filter import spark.Filter import spark.Request import spark.Response
class ResponseHeaderFilter : Filter { override fun handle(request: Request, response: Response) { response.header("Server", "Your Kotlin Server") } }
Filters package io.stormcat import io.stormcat.controller.EchoController import io.stormcat.filter.ResponseHeaderFilter import spark.Spark.*
fun main(args: Array<String>) { // filters after(ResponseHeaderFilter()) // routing get("/echo", EchoController().echo) } ‣ before/after ‣ 認証や、横断的関心事の解決
ResponseTransformer package io.stormcat import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import com.fasterxml.jackson.module.kotlin.KotlinModule
import spark.ResponseTransformer class JsonTransformer : ResponseTransformer { val mapper = ObjectMapper() .registerModule(JavaTimeModule()) .registerModule(KotlinModule()) override fun render(model: Any?): String { return mapper.writeValueAsString(model) } } ‣ JacksonのKotlinModule ‣ data class をサポート
data class package io.stormcat.controller import spark.* class EchoController
{ data class EchoResult( val name: String, val message: String ) val echo = Route { req, res -> EchoResult( name = req.queryParams("name"), message = "Hello" ) } } ‣ レスポンスをdata classで扱う ‣ Controller内に気軽に書けて(・∀・)イイ!!
拡張関数 package io.stormcat.controller import spark.Request fun Request.authUser(): User
{ val user = this.attribute<User>("authUser") return user ?: throw RuntimeException("Authorization Required") } ‣ spark.Requestに認証情報をセット ‣ spark.Requestに関数を生やして取れるようにしてしまう ‣ ラップするより拡張という選択
Guice(Dependency Injection) package io.stormcat.controller import com.google.inject.Inject import io.stormcat.service.UserService import
spark.* class UserController @Inject constructor( val userService: UserService ) { val getUser = Route { req, res -> val userId = req.params("id")?.toLong() ?: throw RuntimeException("id is required") userService.getUser(userId) } } ‣ Guiceを使ってconstructor injection
愚直なDI fun main(args: Array<String>) { val injector = Guice.createInjector(object
: AbstractModule() { override fun configure() { bindConstant().annotatedWith( Names.named("apiDomain")).to("api.yourexample.com") bind(ObjectMapper::class.java) .toProvider(ObjectMapperProvider::class.java) .`in`(Singleton::class.java) } }) val userController = injector.getInstance(UserController::class.java) // routing get("/user/:id", userController.getUser) } ‣ 依存関係、スコープを愚直に定義 ‣ ControllerをInjectorから取得
okhttp class UserApiClient @Inject constructor(val mapper: ObjectMapper, val client: OkHttpClient,
@Named("apiDomain") val apiDomain: String ) { fun getUser(ids: List<Long>): List<User> { val urlBuilder = HttpUrl.Builder().scheme(“https").host(apiDomain) .addPathSegment("users").addPathSegment(ids.joinToString(",")) val request = Request.Builder().url(urlBuilder.build()).build() val response = client.newCall(request).execute() if (response.code() != 200) { throw RuntimeException("api error") } val raw = response.body().string() val tr = object : TypeReference<List<User>>() {} return mapper.readValue<List<User>>(raw, tr) } } ‣ Microservices間の通信 ‣ data classにdeserialize
Hystrix ‣ github.com/Netflix/Hystrix ‣ 分散システムにおいて、回復力のあるアーキテクチャを実現するた めのライブラリ ‣ Circuit Brakerをサポート
Circuit Braker ‣ どこか一つのServiceがダウンした際に、リクエストをブロックする して依存サービスが連鎖的に影響を受ける ‣ エラー率が閾値を超えた際に、自動でアクセスを遮断するための仕 組み
Hystrix Dashboard https://github.com/Netflix/Hystrix/wiki/Dashboard
HystrixCommand ‣ 外部サービスへのリクエストや、Latencyを注視しておきたい処理 をHystrixCommandとして実装する ‣ HTTP Client実装を内包するような実装が多い ‣ 対象処理を容易にFutureやObservable化することができる
Hystrix interface GetUserCommandFactory { fun create(@Assisted("id") ids: List<Long>): GetUserCommand }
interface GetUserCommand { fun execute(): List<User> } class GetUserCommandImpl @Inject constructor( @Assisted("ids") val ids: List<Long>, val userApiClient: UserApiClient, val getUserCommandKey: HystrixCommandGroupKey ) : HystrixCommand<List<User>>(getUserCommandKey), GetUserCommand { override fun run(): List<User> { return userApiClient.getUser(ids) } }
Circuit Braker val circuitBreakerProperties = HystrixCommandProperties.Setter() .withCircuitBreakerEnabled(true) .withCircuitBreakerErrorThresholdPercentage(50) val
getUserCommandKey = HystrixCommand.Setter .withGroupKey(HystrixCommandGroupKey.Factory.asKey("getUser")) .andCommandPropertiesDefaults(circuitBreakerProperties) ‣ Circuit Brakerの閾値を設定できる ‣ HystrixCommand初期化時にCommandKeyとして設定
Metrics(Jolokia) ‣ Jolikiaを入れておけばJVMのMetricsが取れる ‣ Dockerを使う場合、Dockerビルドの際に入れておいて、HTTPで取 れるようにしとくのが楽
Metrics(Jolokia) FROM java:openjdk-8-jdk-alpine COPY . /spark-kotlin RUN apk
update && \ apk add --virtual build-dependencies build-base bash curl && \ cd /spark-kotlin && ./gradlew clean && \ cd /spark-kotlin && ./gradlew build && \ mkdir -p /usr/local/spark-kotlin/lib && \ cp -R /spark-kotlin/build/libs/* /usr/local/spark-kotlin/lib/ && \ curl -o /usr/local/spark-kotlin/lib/jolokia-jvm-agent.jar \ https://repo1.maven.org/maven2/org/jolokia/jolokia-jvm/1.3.5/jolokia-jvm-1.3.5-agent.jar && \ apk del build-dependencies && \ rm -rf /var/cache/apk/* && \ rm -rf ~/.gradle && \ rm -rf /spark-kotlin ENTRYPOINT java $JAVA_OPTS \ -javaagent:/usr/local/spark-kotlin/lib/jolokia-jvm-agent.jar=port=8778,host=0.0.0.0 \ -jar /usr/local/spark-kotlin/lib/spark-kotlin.jar EXPOSE 4567 8778
FRESHにおけるSparkとKotlin ‣ ユーザー・配信主用の公開APIとして本番稼働開始 ‣ 各種MicroservicesへのGatewayとしての役割を担う ‣ Hystrix Dashboardこれから活用していきたい
まとめ ‣ Spark + Kotlin十分運用していけるし、現実的な選択肢になった ‣ Microservicesトレンドの中、シンプルなものが求められている ‣ Server Side
Kotlinの波は少しずつ来ている ‣ Kotlin気持ち(・∀・)イイ!!
Thanks