ニコニコAndroid(サーバ編) - Scalaを業務で使って

Post on 21-May-2015

12038 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

DESCRIPTION

2013 JJUG(http://www.java-users.jp/?page_id=330) #jjug_ccc ※一部発表時と異なる点があります

Transcript

ニコニコAndroid- サーバ編 -

間島 大智

目次

•ニコニコAndroidサーバって何?

• Scalaいいところ・わるいところ

•サーバチームの仕事の流れ

間島 大智

•株式会社ドワンゴ

•ニコニコAndroidサーバチーム

• 2012年新卒入社

間島 大智

•株式会社ドワンゴ

•ニコニコAndroidサーバチーム

• 2012年新卒入社

間島 大智

•株式会社ドワンゴ

•ニコニコAndroidサーバチーム

• 2012年新卒入社

•化学の知識を活用できるWebサービスのプロジェクトまだですかね

ニコニコAndroidサーバって?

社内向けAPI・DB

ニコニコAndroidサーバ

Androidアプリau STBアプリ

様々なアプリでニコニコを使うために

必要なAPIを提供

プロジェクトの歴史

• 2012/02 プロジェクト発足

• 2012/08 au STBアプリリリース

• 2012/11 Androidアプリリリース

•74 API•330万リクエスト/日

サーバ側メンバー

• mashijp (github)

• @kozo1215

• @mtgto

• @mitikage

• @kimwoonsung

• インフラ2人

環境

•言語: Scala

•フレームワーク: Play Framework 2.0

• IDE: IntelliJ IDEA 11, 12• CI: Jenkins

社内初

ニコニコAndroidサーバって?

ニコニコAndroidサーバの役目

社内向けAPI・DB

ニコニコAndroidサーバ

Androidアプリau STBアプリ

この位置に求められることって…?

ニコニコAndroidサーバに求められること

社内向けAPI・DB

ニコニコAndroidサーバ

Androidアプリau STBアプリ

アプリにとって使いやすいAPIを提供すること

使いやすいAPIとは?

•用語が統一されている

•用語が直感的に理解できる

•内部的な細かい仕様を知らなくていい

•ドキュメント化されている

•用語が統一されている

•用語が直感的に理解できる

•内部的な細かい仕様を知らなくていい

逆にこの3つを満たしていないAPIって?

社内APIを変えればいい?

長い歴史があるもの(社内API)にはどうしてもわかりにくい用語・仕様がまじってしまう

社内APIを修正すればいい…?

そう簡単には変えられない

•社内APIは多数の社内サービスが既に使っている

•仕様を変更するのは容易ではない

サーバで内部仕様を吸収

mylist.list deflist.listmylist.remove deflist....

GET /xxx/mylists/:id

社内向けAPI・DB

ニコニコAndroidサーバ

Androidアプリau STBアプリ わかりやすい!

モデルもScalaで再定義

ドメイン層

Androidアプリau STBアプリ

インフラストラクチャ層

社内向けAPI・DB

API層

ドメイン駆動設計による浄化

社内APIを叩くことだけに専念

ビジネスロジックだけに専念

リクエストの処理だけに専念

明確にレイヤー分離された設計をしています

社内向けAPIドキュメント

• Confluence(Wikiみたいなもの)で管理

•全てのAPIについて細かく記述している

ドキュメントの例

ドキュメントの例

ドキュメントの例

ドキュメントの例共通で使うレスポンスの形は別ページにしている(多重管理を避けるため)

APIドキュメントの記述

•社内向けAPIドキュメントだからといって決して手を抜いていない

•多重管理を避け、ドキュメントが古くなってしまうことを避けている

使いやすいAPIが実現できた

•用語が統一され、直感的に理解できる

•内部的な細かい仕様を知らなくていい

•ドキュメント化されている

→Scalaでモデルを再定義しアプリに提供

→社内向けだからといって甘えていない 管理しやすく見やすいドキュメント作成

Scalaの良い所・悪い所

環境

•言語: Scala

•フレームワーク: Play Framework 2.0

• IDE: IntelliJ IDEA 11, 12• CI: Jenkins

•昨年6月、新卒研修が終わりニコニコAndroidチームに配属された

•入社前

• Javaは趣味で触ってた

•が、 “Scala”は名前すら知らない

Scala…?!

Java利用者にとってのScala実体験をふまえて

Scalaって?•静的型付け言語

•関数型言語とオブジェクト指向言語のハイブリッド

• JVM上で動く

• Javaのコードも簡単に混ぜられる

Scalaって?•静的型付け言語

•関数型言語とオブジェクト指向言語のハイブリッド

• JVM上で動く

• Javaのコードも簡単に混ぜられる

って言われても何が良いのかわからん!

便利やでScala

•型推論

•コレクション関数

•対話環境

• Option型

型推論String str = “fugahoge”;String piyo = str + “piyo”;

Java

型推論String str = “fugahoge”;String piyo = str + “piyo”;

Java

型推論String str = “fugahoge”;String piyo = str + “piyo”;

Java

↑書かなくても右辺見ればわかる

型推論String str = “fugahoge”;String piyo = str + “piyo”;

Java

Scala

↑書かなくても右辺見ればわかる

型推論String str = “fugahoge”;String piyo = str + “piyo”;

Java

Scala

↑書かなくても右辺見ればわかる

val str = “fugahoge”val piyo = str + “piyo”

型推論String str = “fugahoge”;String piyo = str + “piyo”;

Java

Scala

↑書かなくても右辺見ればわかる

val str = “fugahoge”val piyo = str + “piyo”↑コンパイラが勝手にStringと推測

型推論

Scala型推論

Scala型推論

def replaceAbc(str: String) = {str.replaceAll(“abc”, “def”)

}

val fuga = replaceAbc(“abcdef”)

Scala型推論

def replaceAbc(str: String) = {str.replaceAll(“abc”, “def”)

}

val fuga = replaceAbc(“abcdef”)

↑メソッドの返り値はStringと推測される

Scala型推論

def replaceAbc(str: String) = {str.replaceAll(“abc”, “def”)

}

val fuga = replaceAbc(“abcdef”)

↑メソッドの返り値はStringと推測される

↑よって、fugaもString

Scala型推論

def replaceAbc(str: String) = {str.replaceAll(“abc”, “def”)

}

val fuga = replaceAbc(“abcdef”)

↑メソッドの返り値はStringと推測される

↑よって、fugaもString

冗長な記述をしなくていい

コレクション関数Java

String[] strs = {“a”, “fuga”, “bb”, “hogee”};List<String> result = new ArrayList<String>();for(String str : strs) { if (str.length >= 3) { result.add(str); }}return result;

コレクション関数Scala

val strs = List(“a”, “fuga”, “bb”, “hogee”);return strs.filter(_.length >= 3)

強力なコレクション関数+型推論によりコード記述量を大幅に減らせる

ちなみに

•ニコニコAndroidサーバのコードにはfor文は7つしかない

Scalaのfor使うときval list1 = List(“a”, “b”, “c”)val list2 = List(1, 2, 3)for(a <- list1; b <- list2) {println(a+b)

}

Scalaのfor使うときval list1 = List(“a”, “b”, “c”)val list2 = List(1, 2, 3)for(a <- list1; b <- list2) {println(a+b)

}

出力結果: a1 a2 a3 b1 b2 b3 c1 c2 c3

Scalaのfor使うときval list1 = List(“a”, “b”, “c”)val list2 = List(1, 2, 3)for(a <- list1; b <- list2) {println(a+b)

}

出力結果: a1 a2 a3 b1 b2 b3 c1 c2 c3

複数のコレクションの組み合わせを網羅するのに非常に便利

強力なコレクション関数+for

•コード記述量が大幅に減った•単純なコレクションの処理をする際にfor文使うことほぼなし

•たまに「forどうやって書くんだっけ」という声がでるほど…

対話環境

強力なimport文

•ブロックの中にかける

•別名をかける

Scala

importをブロックの中にかける

import java.util.Date;public void methodA(){ Date date = new Date(). .......}

public void methodB(){ java.sql.Date date = new java.sql.Date(..). .......}

Java

名前が衝突するのでimportできず、フルネームで書くしかない

importをブロックの中にかける

def methodA() = { import java.util.Date val date = new Date(). .......}def methodB() = { import java.sql.Date val date = new Date(..). .......}

Scala

別名をつけられるimport java.util.Dateimport java.sql.{Date => SqlDate}def methodA() = { val date = new Date(). .......}def methodB() = { val date = new SqlDate(..). .......}

Scala

強力なimport文

•ブロックの中にかける&別名をつけることができる

•クラス名をつける際に「かぶりそうだな…」と怯えることがなくなった

•短いクラス名をどんどんつけちゃう

•例) Client (Redis接続用クラス)

Option型Java

・このメソッドってnull返すことあるっけ? えーとドキュメント...・あ、そうだ nullチェックしないと... if (result != null) { ....

Option型Java

・このメソッドってnull返すことあるっけ? えーとドキュメント...・あ、そうだ nullチェックしないと... if (result != null) { .... Null

PointerE

xception

Javaでぬるぽを起こす例

Java

Map<String, String> map = getMap();String result = map.get(fieldName);if (result.length() >= 3) { return “長さは”+result.length();} else { return “短い!!”;}

Mapからとってきたものが3文字以上かどうかチェックするメソッド

Javaでぬるぽを起こす例

Java

Map<String, String> map = getMap();String result = map.get(fieldName);if (result.length() >= 3) { return “長さは”+result.length();} else { return “短い!!”;}

Mapからとってきたものが3文字以上かどうかチェックするメソッド

Javaでぬるぽを起こす例

Java

Map<String, String> map = getMap();String result = map.get(fieldName);if (result.length() >= 3) { return “長さは”+result.length();} else { return “短い!!”;}

NullPointerException!!!   ( ・∀・)   | | ガッ  と    )    | |    Y /ノ    人     / )    <  >__Λ∩   _/し' //. V`Д´)/  (_フ彡        /

そんなキーねえよ

Mapからとってきたものが3文字以上かどうかチェックするメソッド

nullチェック追加

Map<String, String> map = getMap();String result = map.get(fieldName);if (result != null && result.length() >= 3) { return “長さは”+result.length();} else { return “短い!!”;}

Java

nullチェック追加

Map<String, String> map = getMap();String result = map.get(fieldName);if (result != null && result.length() >= 3) { return “長さは”+result.length();} else { return “短い!!”;}

Java

どうしてもnullチェック忘れてしまう…!

Option型って?

Option[T]

Some[T] None

値がある 値がない

値がある・ないを型で表現している

Scala

Option型の挙動Scalaval map = Map("key1" -> "value1", "key2" -> "value2")// Map#getの返り値はOption型(ここではOption[String])println(map.get(“key1”))//→ Some(value1)println(map.get(“fuga”))//→ Nonemap.get(“key1”).getOrElse(“ないとき”)//→ value1map.get(“fuga”).getOrElse(“ないとき”)//→ ないとき

Someのときは中身を取り出し、Noneのときは引数を返す

型で値がないことに気づける

val map = getMap()val result = map.get(fieldName)

↑resultの型は Option[String]この時点で「値がないこともありうる」ということに気づける

Scala

val map = getMap()val result = map.get(fieldName)result.filter(_.length > 3).map(e => “長さは”+e.length).getOrElse(“短い”)

さっきの例をScalaで実装

Scala

ぬるぽがない安心感

• Scalaの標準ライブラリは原則nullを返さない(Scalaはnullを使わない文化)

•つまりnullチェックを書く必要がない

社内向けAPIドキュメント

社内向けAPIドキュメント

nullがありうるものをドキュメントに残すのに苦労しなかった

Scalaの悪いところ...

•コンパイル遅すぎ!

•社内にScalaについて聞ける人が少ない

コンパイル

•遅い!!とにかく遅い!!

•メモリ食い過ぎー!

•ビルド時のJavaの設定-Xms512m -Xmx1024m -XX:MaxPermSize=512m

Scala

コンパイル

•遅い!!とにかく遅い!!

•メモリ食い過ぎー!

•ビルド時のJavaの設定-Xms512m -Xmx1024m -XX:MaxPermSize=512m

Scala

Jenkinsで並行ビルドするとスワップアウト...→ Jenkinsサーバのメモリ増設(4GB → 8GB)

社内に聞ける人が少ない

•弊社内でScalaのプロジェクトはニコニコAndroidサーバが初めて

•社内ノウハウが少ない…

• Scala書ける人もあんまりいない…

学習コストはそんなにかからない

開発メンバー5人中4人がScalaをはじめて1年たっていない

学習コストはそんなにかからない

開発メンバー5人中4人がScalaをはじめて1年たっていない

でも1ヶ月勉強したら業務で書けるようになった

どうやって勉強?

•おすすめ本「JavaプログラマのためのScalaプログラミング」

•疑問は社内IRCでブツブツ言う

•誰か答えてくれる

• #scalaというチャンネルもできた

少しずつ広まっている...!

最近は社内でもScalaを使う流れができつつあり、今後は改善されそう!

ドワンゴでは4プロジェクトがScala使ってます

チームの実際の仕事の流れ

仕事のサイクル

計画

振り返り 実装

レビューリリース

マージ

1周=2週間のアジャイル開発

開発メンバー全員で2週間にやることを計画誰がどの仕事をするか割り振る

全員で計画する

計画の前準備

会議をする前に各自でやらないといけないことを洗い出し、チケットとして作成チケット管理にはAtlassian製品のJIRAを使っています

全員で見積もるチケットを作った人がチケットの内容を説明

工数見積もり(予想)を各自で出す

見積もりがあわなかったら最大3回やる

全員でやることを共有できているので突然誰かが倒れても大丈夫なチームに

実装する

• IntelliJ IDEA 12 Ultimate($200)使ってます

• キャンペーンのときに全員買った

• 買うまではCommunity Edition(無料)

• 会社で買ってくれるという話も出てる

• ScalaのIDEはIntelliJ IDEA一択

• EclipseでのScala開発はめっちゃ重い…

テストに力を入れています

1000以上のテストケースによりコードカバレッジ80%以上を達成

FishEyeでコードレビュー

全員が仕事を理解しているので誰でもレビューできるみんなで気軽にレビューコメントをつける

GitHub:eよりFishEyeのほうが便利

•誰がレビューOK出しているのか、どれぐらい見ているのかがすぐ分かる!

全員のレビューOKが出ればマージする

Jenkins•ビルド

•テストの実行

• developブランチにマージするとJenkinsが動き出す

•テストが落ちるとチームメンバーに罵られる

振り返る(KPT)

毎週水曜日にKPTKeep(続けたいこと), Problem(困ったこと),Try(次やりたいこと) をみんなで自由に書く

KPTで改善された例

Jenkins用サーバで並行ビルドできない

もっとスペックの高いJenkins用サーバを調達し、並行ビルド可能に

Problem

Try

思ったことを気軽に書けるので「ちょっとした問題」を改善していく

流れを作ることができた

強いチーム作りに成功

「全員で」計画し「全員で」振り返ることで常に意識を共有

全員が主体的に動く強いチームを作っています

ドワンゴで働きませんか

• Scalaに興味のある人もない人も

• 「ドワンゴ 採用」で検索!

• http://info.dwango.co.jp/recruit/

• 7月から新オフィス(歌舞伎座)

お待ちしています!

まとめ• 美しいAPIをアプリチームに提供できた• Scalaで再モデリング・ドキュメント整備

• Scalaを業務で楽しく使ってます!• 強力なScalaの機能で効率の良い開発が可能に

• 強いチームでやってます• 全員が仕様を理解できている

• 誰が風邪引いても困らない

top related