ゲーム開発に最適なサーバーサイドKotlin 〜Kotlinの導入と基盤ができるまで〜 / CEDEC 2018

ゲーム開発に最適なサーバーサイドKotlin 〜Kotlinの導入と基盤ができるまで〜 / CEDEC 2018

CEDEC 2018の発表資料です。

セッションの内容(公式サイトより):
アプリボットではこれまでJavaでサーバーサイドの共通基盤を作成し、ゲームを開発してきました。
その中で昨年、サーバーサイドKotlinに大きな可能性を感じ、今後使用していく言語として選択しました。
現在はJavaで書かれていた基盤もKotlinへと移行し、プロダクトを開発しています。
Android開発の公式言語として話題になっているKotlinですが、なぜサーバーサイドの言語として導入に踏み切ったのか?
その理由とJavaからの移行時に考えるべきポイント、今後の展望などについて事例とともにご紹介します。

https://2018.cedec.cesa.or.jp/session/detail/s5abf865c5ecde

D1531f9547e24397c7e85881fac03096?s=128

Takehata Naoto

August 24, 2018
Tweet

Transcript

  1. ゲーム開発に最適な サーバーサイドKotlin 〜Kotlinの導⼊と基盤ができるまで〜 株式会社アプリボット チーフエンジニア ⽵端 尚⼈

  2. ⾃⼰紹介 ⽵端 尚⼈ @n_takehata (Kotlin、Java) サーバーサイドエンジニア 2006.04 2007.12 2011.01 2014.04

    公務員 SES サイバーエージェント アプリボット
  3. 会社紹介 株式会社アプリボット ▪公式HP  https://www.applibot.co.jp/ スマートフォン向けゲームアプリの企画、制作、運⽤ 2010年7⽉サイバーエージェント⼦会社として設⽴ ▪事業内容 ▪技術ブログ https://blog.applibot.co.jp/

  4. アプリボットのサービス スマートフォン向けゲームを中⼼に、 様々な事業領域にチャレンジしています。

  5. ゲームの技術セット サーバー   ・Kotlin   ・Java クライアント  ・Unity  ・Cocos-2dx インフラ   ・AWS   ・GCP

  6. ゲームの技術セット サーバー   ・Kotlin   ・Java クライアント  ・Unity  ・Cocos-2dx インフラ   ・AWS   ・GCP

  7. 今⽇は新規のゲームプロダクトで 使っているサーバーサイドKotlin についてお話します。

  8. Kotlinとは? •  JetBrains社が開発したオブジェクト指 向⾔語 •  Androidの開発⾔語として有名 •  JVM⾔語の⼀つ

  9. アジェンダ 1.なぜKotlinが最適なのか? 2.Kotlinの採⽤を決意するまで 3.Kotlin導⼊の流れ、問題点 4.今後の展望

  10. アジェンダ 1.なぜKotlinが最適なのか? 2.Kotlinの採⽤を決意するまで 3.Kotlin導⼊の流れ、問題点 4.今後の展望

  11. 1 なぜKotlinが最適なのか?

  12. 前提 モバイルでのソーシャルゲームで 最適という意味です

  13. ソーシャルゲーム開発で 重要な要件 •  突発的な大量トラフィックに耐えうる処理 性能 •  長期運用に備えた設計 •  不具合の軽減 1-①

  14. ソーシャルゲーム開発で 重要な要件 •  突発的な大量トラフィックに耐えうる処理 性能 •  長期運用に備えた設計 •  不具合の軽減 1-①

  15. 突発的な大量トラフィックに耐えうる 処理性能 •  コンパイル言語であり、LL言語に比べて処 理性能が高い •  同規模のLL言語で開発したアプリに比べ、 サーバー台数が半分以下の場合もあった

  16. ソーシャルゲーム開発で 重要な要件 •  突発的な大量トラフィックに耐えうる処理 性能 •  長期運用に備えた設計 •  不具合の軽減 1-①

  17. 長期運用に備えた設計 •  静的型付け言語であり、大人数での開発に 向いている •  長期運用で関わる人数、機能追加が増加し ても安定した設計をしやすい •  シンプルでモダンな実装コード

  18. ソーシャルゲーム開発で 重要な要件 •  突発的な大量トラフィックに耐えうる処理 性能 •  長期運用に備えた設計 •  不具合の軽減 1-①

  19. 不具合の軽減 •  Null安全 •  var、valキーワードを使用したmutable、 immutableの明示 •  コンパイルで防げるエラーが多い、安全性 の高い言語仕様になっている

  20. 特に魅⼒的な機能

  21. KotlinのNull安全 1-① var message: String = null コンパイルエラーになる var message:

    String? = null 変数の値にnullを設定できる
  22. var user: User? = User() user.name Null許可型のオブジェクトに直接アクセスするとコンパイルエラー if (user !=

    null) { user.name } Nullチェックをするとアクセスできるようになる
  23. •  Nullチェック漏れによるNullPointerExcepJon の減少 •  デフォルトがNull不許可になっていることによ り、不要なNull値の扱いを減らせる •  コンパイルでNull関連の不備を指摘してくれる ため、コードレビューの工数を削減できる

  24. とても安全な⾔語

  25. •  コンパイル言語で、処理性能がLL言語に比べて高 い •  静的型付け言語であり、大人数での開発にも向い ている •  安全性の高い言語仕様 •  モダンな言語

    まとめると・・・
  26. ぜひKotlinを使いましょう!

  27. アジェンダ 1.なぜKotlinが最適なのか? 2.Kotlinの採⽤を決意するまで 3.Kotlin導⼊の流れ、問題点 4.今後の展望

  28. 2 Kotlinの採⽤を決意するまで

  29. ①これまでの技術 ②Kotlinに決めた理由 ③Kotlinを採⽤をすべきか?

  30. 2-① これまでの技術

  31. 2-① A.R.T.という組織 •  Applibot Root Technologiesの略 •  横軸で技術を共有、共通化、標準化、  基盤化するための組織 • 

    詳しくはブログをご覧ください https://blog.applibot.co.jp/2018/04/05/ vision-for-art/
  32. 2-① Kotlin導⼊前の基本的な 技術セット •  Java •  Spring Framework •  MyBatis

    etc…
  33. 2-① Javaを使ってきた理由 •  コンパイル⾔語であり、処理性能がLL ⾔語に⽐べて⾼い •  静的型付け⾔語であり、⼤⼈数での  開発にも向いている •  歴史の⻑い⾔語なので事例や

     ノウハウも多い
  34. 悩みも・・・ •  歴史の⻑い⾔語でもあるがゆえ、 レガシーな部分はある •  正直新しい技術触りたい

  35. 新しい⾔語へチャレンジしたい

  36. Kotlinがいいんじゃないか?

  37. 2-② Kotlinに決めた理由

  38. Kotlinを考え始めたきっかけ •  Androidの公式の開発⾔語として 採⽤された •  Spring5.0のKotlinサポート 2-② これ使えそうじゃない?となる

  39. Kotlinを調べ始める

  40. 2-② 移⾏のメリット •  モダンな開発ができる •  Javaと同等の性能が維持できる •  移⾏コストが低く抑えられる

  41. モダンな開発ができる •  コードがシンプル –  型推論 –  String Template –  プロパティ

    –  データクラス –  etc… •  安全 –  Null安全 –  val、var 2-②
  42. Javaと同等の性能が維持できる •  Java、Scala、Groovy等と同じ  JVM⾔語 •  最終的にコンパイルされた  実⾏ファイルはJavaと  ほぼ同等になる 2-②

  43. 移⾏コストを低く抑えられる •  JetBrains社がJavaの資産を  活かすことを想定して開発した⾔語 •  Javaとの相互互換 •  Spring5.0のKotlinサポート 2-②

  44. まとめると •  モダンな実装ができる •  Javaのメリットの多くがそのまま残り、  資産(ライブラリ等)も活⽤できる •  且つ移⾏コストも低く抑えられる 2-② (Javaを使っているシステムには)

    いいこと尽くし!
  45. Kotlinに移⾏しよう!

  46. 2-③ Kotlinを採⽤すべきか

  47. Javaを使ってたから採⽤できた?

  48. Javaを使ったことがなかったら •  Javaからの移⾏じゃないと  メリット少ない? •  Kotlin使うにはJavaも覚えなきゃいけない? •  最初からKotlin使うのは微妙? 2-③

  49. そんなことはありません!

  50. •  コンパイル言語で、処理性能がLL言語に比べて高 い •  静的型付け言語であり、大人数での開発にも向い ている •  安全性の高い言語仕様 •  モダンな言語

    Javaは関係ありません! 先程のKotlinのメリット
  51. 新しい技術にありがちな問題 2-③ •  事例、情報が少ない    •  ライブラリ等が整っていない    •  技術者がいない

      
  52. Kotlinなら 2-③ •  事例、情報が少ない   →Javaの(⼤量にある)情報が役⽴つ •  ライブラリ等が整っていない   →Javaのライブラリ、フレームワークが使える •  技術者がいない

      →Javaの技術者ならすぐに馴染める
  53. 盤⽯な歴史のあるJavaから ⼤きな改善を加え 誕⽣したモダンな⾔語!

  54. ぜひKotlinを使いましょう!

  55. アジェンダ 1.なぜKotlinが最適なのか? 2.Kotlinの採⽤を決意するまで 3.Kotlin導⼊の流れ、問題点 4.今後の展望

  56. 3 Kotlin導⼊の流れ、問題点

  57. 3 導⼊までにやったこと ①Kotlinの調査 ②サードパーティライブラリの検証 ③既存のJavaコードの変換 ④Kotlin最適化

  58. 3-① Kotlinの調査

  59. 3-① 検証項⽬ •  Kotlinの特徴 •  将来性 •  移⾏コスト •  性能

  60. 先程全て話したので省略します

  61. 3-② サードパーティライブラリ の検証

  62. ※2018年1⽉当時の検証結果です

  63. •  O/Rマッパー •  テストフレームワーク

  64. O/Rマッパーの検証

  65. 検証対象 •  MyBatis •  DOMA2 •  Exposed 3-②

  66. 検証項⽬ •  Kotlinでの挙動 •  分かりやすさ •  カスタムクエリの実装⽅法 •  ⾃動⽣成ツールの性能 • 

    将来性 •  情報量 実際にKotlinでCRUDを作成して検証する 3-②
  67. 検証対象 •  MyBatis •  DOMA2 •  Exposed 3-②

  68. MyBatis •  Javaの代表的なO/Rマッパーの⼀つ •  XML等でクエリを記述し、  変数や分岐を埋め込める •  Entityクラス等の⾃動⽣成ツールも  ⽤意されている 3-②

  69. 検証結果 •  全てKotlin化しても  基本的な部分は動いた •  ⾃動⽣成ツールはKotlinに  対応していない 3-②

  70. 検証対象 •  MyBatis •  DOMA2 •  Exposed 3-②

  71. DOMA2 •  Javaの代表的なO/Rマッパーの⼀つ •  クエリはsqlファイルで記述し、  ⽂中に変数や分岐を埋め込める 3-② •  もともとSeaserグループで作られており、 ⽇本語のドキュメントが豊富

  72. 検証結果 •  全てKotlin化して使⽤するとエラーが発⽣ •  Kotlin使われている事例はあったが、 DOMA2の部分はJavaで書いている •  公式ではKotlinを”実験的に”サポート  している 3-②

  73. 検証対象 •  MyBatis •  DOMA2 •  Exposed 3-②

  74. Exposed •  JetBrains製のO/Rマッパー •  Kotlinで作られている •  DSLで直感的に記述できる 3-②

  75. 検証結果 •  まだ正式版になっていない(0.x系) •  全体的にエコシステムが整っていない •  利⽤実績もほぼない 3-②

  76. MyBatisを採⽤ •  Kotlinで使⽤しても基本的な挙動は  問題なかった •  ⾃動⽣成の部分はJavaのまま使う (もともと触らない箇所なので) ・既存の基盤でも使⽤していた  →他のものに乗り換える程のメリットがなかった 3-②

  77. テストフレームワークの検証

  78. 検証対象 •  JUnit •  Spek •  KotlinTest 3-②

  79. 検証対象 •  JUnit •  Spek •  KotlinTest 3-②

  80. JUnit •  Javaの最もメジャーな  テストフレームワーク •  KotlinでもJavaと同様に  使⽤することが可能 3-②

  81. ・基本はJavaで書いた時と同じ構成で、同じように動く ・ JMockit(モック化ライブラリ)、AssertJ(検証処理)を  併せて使⽤ class SampleServiceTest {

  82. Kotlin製も試してみよう

  83. 検証対象 •  JUnit •  Spek •  KotlinTest 3-②

  84. Spek •  Kotlin製のテストフレームワーク •  ⾮常にシンプルなテストフレームワーク •  BDDフレームワークとしても使える 3-②

  85. ・シンプルで分かりやすい構⽂ ・基本的にgiven、on、itの3種類のブロックで構成するため、  実装者による差分が⽣まれづらい class SampleServiceTest: Spek({

  86. SpringのDIができない・・・

  87. 検証対象 •  JUnit •  Spek •  KotlinTest 3-②

  88. KotlinTest •  Kotlin製のテストフレームワーク •  多機能なフレームワーク •  StringSpec, FunSpec, ShouldSpec, WordSpec,

    FeatureSpec, BehaviorSpec 等、様々な書き⽅がサポートされている 3-②
  89. ・Spekと同様、基本的に3種類のブロックで構成するため  実装者による差分が⽣まれづらい ・上記はBehaviorSpecの例 class SampleServiceTest: BehaviorSpec() {

  90. SpringのDIができない・・・

  91. ⼀旦JUnitを採⽤ •  既存の基盤をそのまま使える •  実績、情報ともに⼗分 •  Kotlin製のものの導⼊は  アップデートを待つ 3-②

  92. と、いうのが検証当時 (2018年1⽉)

  93. KotlinTestがSpringのDIに対応! 3-②

  94. KotlinTestを採⽤

  95. KotlinTestを採⽤ 3-② •  多機能な上、それによる煩雑さは  感じない •  パラメーターテストが便利 •  公式ドキュメントが充実

  96. StringSpecを使⽤ 3-② ・シンプルな構⽂ ・開発者が推奨している形式 class SampleServiceTest: StringSpec() {

  97. forallを使⽤したパラメータテストが便利! class SampleServiceTest: StringSpec() {

  98. KotlinTestおすすめです

  99. ここまでが検証の話

  100. 3-③ 既存のJavaコードの変換

  101. 既存の基盤 3-③ •  あくまでSpringをベースに  実装を共通化したもの •  ⾃社製フレームワークではない •  スターターキットとして  リポジトリを⽤意し、

     各プロダクトがForkして開発する
  102. ①IntelliJのConvert Java File to Kotlin File  の機能で全てのJavaファイルを変換 ②変換後に出ていたコンパイルエラー  を全て解消 ③実⾏し、発⽣したエラーを全て解消

    ⼀旦は「Javaの時と同じように動かす」ことが最優先
  103. Kotlin化しない箇所 3-③ •  MyBatisの⾃動⽣成コード •  ライブラリ、フレームワーク部分 ⼿動で触らない部分はJavaのまま扱う

  104. ここからが⻑い

  105. •  IntelliJの変換の精度は⾼くはない •  ⼀⾒すると正しいように⾒える •  似た構⽂でも微妙に違うことがある

  106. 変換時にハマった差分を 紹介します

  107. •  拡張プロパティ •  コンパニオンオブジェクト、@JvmStaic •  SAM変換

  108. 拡張プロパティ 3-③ Kotlinはクラスにプロパティを定義すると、 内部的にアクセサメソッドを⽣成する class User { var name: String

    = "" } val user = User() user.name = "takehata" user.name プロパティの値はアクセサメソッド経由で呼ばれる
  109. ・プロパティに拡張機能を持たせられる ・上記はtotalScoreがscore1とscore2の  合計値を返却する例 val score1 = 0 val score2 =

    0 val totalScore get() = score1 + score2
  110. public String getMessage() { UserService userService = new UserService(); return

    userService.createMessage(); } val message: String get() { val userService = UserService() return userService.createMessage() } ・get〜という名前の引数なしメソッドが  全てプロパティとして認識されてしまう ・なぜメソッドがプロパティに・・・と⼾惑う Java Kotlin
  111. コンパニオンオブジェクト 3-③ Javaから呼び出そうとすると、Companionというオブジェクトを 経由しなくてはならなくなる @JVMStatic companion object { fun execute()

    { // ・・・
 } } Sample.Companion.execute(); Java Kotlin staticな関数を定義したい時、コンパニオンオブジェクトを使う
  112. Sample.execute(); もともとはこう呼んでいて、コンパイルエラーになる companion object { @JvmStaJc fun execute() { //

    ・・・
 } } @JvmStaticを付けるとJavaと同じように呼び出せる
  113. Javaからの呼び出しを考慮する際に 使えるアノテーション •  @file:JvmName •  @JvmStaJc •  @JvmOverloads

  114. SAM変換 3-③ 関数型インターフェースを引数に取るメソッドには、 ラムダ式を渡すことができる public interface FuncJonSample { public Integer

    calc(Integer num1, Integer num2); } public Integer callFuncJon(FuncJonSample funcJon) { return funcJon.calc(1, 2); } 関数型インターフェース(単⼀メソッドのインターフェース) 関数型インターフェースを引数に取るメソッド callFuncJon((Integer num1, Integer num2) -> { return num1 + num2; });
  115. callFuncJon { num1: Int, num2: Int -> num1 + num2

    } ・Kotlinでも同様に呼び出すことができる ・Javaの関数型インターフェースを  Kotlinの関数型として扱ってくれる(これがSAM変換)
  116. ・Kotlin同⼠ではSAM変換が効かず、匿名オブジェクトを  明⽰的に書いて渡さなければならない ・関数によってラムダ式で渡せるものと渡せないものがあり、  なぜ・・・となった fun callFuncJon(funcJon: FuncJonSample): Int? { return

    funcJon.calc(1, 2) } 呼び出されるメソッドもKotlin化してしまうと・・・ callFuncJon(object : FuncJonSample { override fun calc(num1: Int, num2: Int): Int { return num1 + num2 } })
  117. コードの変換は完了

  118. 3-④ Kotlin最適化

  119. •  ⾼階関数 •  TypeAlias •  強制アンラップ

  120. ⾼階関数 3-④ •  関数型を引数や戻り値に取る関数のこと •  Kotlinでは関数型インターフェースが  存在せず、こちらが通常の形

  121. ⾼階関数を使わない場合 3-④ fun callFuncJon(funcJon: FuncJonSample): Int? { return funcJon.calc(1, 2)

    } callFuncJon(object : FuncJonSample { override fun calc(num1: Int, num2: Int): Int { return num1 + num2 } }) 匿名オブジェクトを明⽰的に書いている
  122. ⾼階関数を使う場合 3-④ ・ラムダ式で渡すと、匿名オブジェクトのインスタンスが  呼び出し間で再利⽤される fun callFuncJon(funcJon: (Int, Int) -> Int):

    Int? { return funcJon(1, 2) } callFuncJon { num1: Int, num2: Int -> num1 + num2 } ・ラムダ式を渡して呼び出せるようになる
  123. Type Alias •  関数リテラルに  名前を付けることができる機能 •  付けた名前で関数の引数の型として指定 することが可能 •  ⾼階関数をよりシンプルにできる

    3-④
  124. 通常の⾼階関数のコード 3-④ fun callFuncJon(funcJon: (Int, Int) -> Int): Int? {

    return funcJon(1, 2) } 引数の型に関数リテラルを書いている
  125. Type Ailiasを使ったコード 3-④ typealias FuncJonSample = (Int, Int) -> Int

    fun callFuncJon(funcJon: FuncJonSample): Int? { return funcJon(1, 2) } ・名前を付けることで、関数リテラルの意味が分かりやすくなる ・同じ関数リテラルを複数書く必要がなくなる callFuncJon { num1: Int, num2: Int -> num1 + num2 }
  126. 強制アンラップ •  Null許可型の変数を強制的に  Null不許可型に変換する 3-④ •  Nullが⼊っていた場合は NullPointerExceptionが発⽣する

  127. KotlinのNull安全 3-④ var user: User? = User() user.name Kotlinは型に?を付けるとNullを⼊れられるようになる Null許可型のオブジェクトに直接アクセスするとコンパイルエラー

    if (user != null) { user.name } Nullチェックをするとアクセスできるようになる
  128. !!を付けると強制的にNull不許可型に変換する user!!.name

  129. fun createUser(): User? { // ・・・
 } val user =

    createUser() user!!.name JavaはデフォルトでNull許可型なので、変換すると?が付く Nullチェックをしていない変数の呼び出しは、!!を付けて変換される fun createUser(): User { // ・・・
 } val user = createUser() user.name Null不許可型にして、!!も排除する
  130. Kotlin基盤が(⼀旦)完成しました

  131. Kotlin化してみてどうか? •  随所でシンプルにコードが書けるようになった   →コード⾏数が2割減 •  コンパイルで検出してくれるエラーが多く、 より堅牢なコードが書けるようになった •  Javaエンジニアでもすんなり書き始められた 3-④

  132. Kotlin化して良かった!

  133. アジェンダ 1.なぜKotlinが最適なのか? 2.Kotlinの採⽤を決意するまで 3.Kotlin導⼊の流れ、問題点 4.今後の展望

  134. 今後の展望 4

  135. マイクロサービス 4-1 •  Kotlinでのマイクロサービスの実装 •  課⾦、認証等共通の機構をマイクロ サービス化する

  136. gRPC対応 4-2 •  マイクロサービスを  Kotlin × gRPCで実装 •  Unity、サーバーKotlin間の  通信での使⽤

    •  HTTP/2を標準でサポートした Google製のRPCフレームワーク
  137. OSS化 4-3 •  マイクロサービス化した機構も  公開を検討中 •  作成したライブラリ等の  OSS化をする予定

  138. プロダクトのリリース

  139. ・UNLIMITED STUDiOと協⼒して開発中 ・本⽇話した内容を全て搭載したプロダクト ・遠くない未来にリリース(予定) 鋭意開発中

  140. 最後に

  141. •  モバイルゲーム開発において、Kotlin は有⼒な選択肢 •  Javaの資産があれば最⼤限活⽤できる •  アプリボットは今後もサーバーサイ ドKotlinに⼒を⼊れていきます

  142. ぜひKotlinを使いましょう!

  143. ご清聴ありがとうございました