Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Realtime Messaging with Firebase #phpcon2017

Realtime Messaging with Firebase #phpcon2017

PHP Conference Tokyo 2017で発表しました。

Sota Sugiura

October 08, 2017
Tweet

More Decks by Sota Sugiura

Other Decks in Technology

Transcript

  1. サービスとして求められていたこと w ڝ߹ΑΓૣ͘ग़͢ w ཁ݅ఆٛɺ։ൃɺ2"ɺϦϦʔε ·ͰΛϲ݄Ͱ࣮ݱ͢Δ ① 7⽉月上旬のリリース w ੈք؍Λ࣮ݱ͢ΔΪϦΪϦΛ

    w ·ͣಈ͘΋ͷΛੈʹग़͢ ② 最⼩小機能で動くものを ʢສμ΢ϯϩʔυɺҰ೔ ສग़඼͞ΕΔτϥϑΟοΫ ͷΞϓϦͰ΋໰୊ͳ͘ʣ
  2. Subscribe Data Subscribe /messages { "messages": { "1": { "text":

    "hello, firebsae", "user": "sota1235" } } }
  3. Subscribe Data { "messages": { "1": { "text": "hello, firebase",

    "user": "sota1235" }, "2": { "text": "hello, phpcon", "user": "phper" } } } Subscribe /messages New data
  4. Subscribe Data { "messages": { "1": { "text": "hello, firebase",

    "user": "sota1235" }, "2": { "text": "hello, phpcon", "user": "phper" } } } Subscribe /messages New data New data
  5. Even if many clients { "messages": { "1": { "text":

    "hello, firebase", "user": "sota1235" }, "2": { "text": "hello, phpcon", "user": "phper" } } }
  6. Not Only Database feature • 認証機能 • Facebook, Twitter, Custom

    Auth, etc… • Rule for each tree • Permission • Validation
  7. { "rooms": { "1": { "messages": { "1": { "text":

    "hello, php", "user": "sota1235" } } } } } 例例えばこんなJSON
  8. { "rooms": { "1": { "messages": { "1": { "text":

    "hello, php", "user": "sota1235" } } } } } 例例えばこんなJSON νϟοτϧʔϜΛදݱ
  9. { "rooms": { "1": { "messages": { "1": { "text":

    "hello, php", "user": "sota1235" } } } } } 例例えばこんなJSON ෦԰͝ͱͷVOJRVFͳ*%
  10. { "rooms": { "1": { "messages": { "1": { "text":

    "hello, php", "user": "sota1235" } } } } } 例例えばこんなJSON νϟοτϧʔϜ಺ͷ ίϝϯτҰཡ
  11. { "rules": { ".write": true, ".read": true, ".validate": "validation rule"

    } } Ruleの基本⽂文法 ① おまじない ② Read/Write 権限
  12. { "rules": { ".write": true, ".read": true, ".validate": "validation rule"

    } } Ruleの基本⽂文法 ① おまじない ② Read/Write 権限 ③ Validationルール
  13. 例例. 意図しないKeyの追加 { "rooms": { "1": { "messages": { "1":

    { "text": "hello, php", "user": "sota1235" } } } } } • 意図しないJSON key を追加させたくない • rootにはroomsだけ⽣生 えてて欲しい
  14. 例例. 意図しないKeyの追加 { "rooms": { "1": { "messages": { "1":

    { "text": "hello, php", "user": "sota1235" } } } }, "extra": { "msg": "pwned" } } • 意図しないJSON key を追加させたくない • rootにはroomsだけ⽣生 えてて欲しい • こういうのを追加させ ない
  15. { "rules": { ".write": false, "rooms": { ".write": true, ".read":

    true } } } 例例. 意図しないKeyの追加
  16. { "rules": { ".write": false, "rooms": { ".write": true, ".read":

    true } } } 例例. 意図しないKeyの追加 Tree最上部への書き込みを 許容しない
  17. { "rules": { ".write": false, "rooms": { ".write": true, ".read":

    true } } } 例例. 意図しないKeyの追加 Tree最上部への書き込みを 許容しない “rooms”配下への あらゆる書き込みを許可する
  18. { "rules": { ".write": false, "rooms": { ".write": true, ".read":

    true } } } 例例. 意図しないKeyの追加 Tree最上部への書き込みを 許容しない “rooms”配下への あらゆる書き込みを許可する ਌ͷ3VMFΑΓ΋5SFF಺ͷ3VMF༏ઌ͢Δ
  19. { "lives": { "1": { "messages": { "1": { "user":

    "sota1235", "image": "hoge.png", "text": "hello" } }, "notifications": { "buy_item": { "text": “sota1235さんが商品を購⼊入したぞい" } } } }, "alive_lives": { "1": true, "2": false } } メルカリチャンネルの例例(⼀一部略略)
  20. { "lives": { "1": { "messages": { "1": { "user":

    "sota1235", "image": "hoge.png", "text": "hello" } }, "notifications": { "buy_item": { "text": “sota1235さんが商品を購⼊入したぞい" } } } }, "alive_lives": { "1": true, "2": false } } メルカリチャンネルスキーマ(再掲)
  21. { "rules": { ".write": false, "lives": { "$live_id": { "messages":

    { "$message_id": { ".validate": "newData.hasChildren(['user', 'image', 'text'])" } }, "notifications": { "buy_item": { ".validate": "newData.hasChildren(['text'])" } } } }, "alive_lives": { ".write": false, ".read": false } } } Rule
  22. { "rules": { ".write": false, "lives": { "$live_id": { "messages":

    { "$message_id": { ".validate": "newData.hasChildren(['user', 'image', 'text'])" } }, "notifications": { "buy_item": { ".validate": "newData.hasChildren(['text'])" } } } }, "alive_lives": { ".write": false, ".read": false } } } Rule +40/SPPUʹॻ͖ࠐΈΛڐՄ͠ͳ͍
  23. { "rules": { ".write": false, "lives": { "$live_id": { "messages":

    { "$message_id": { ".validate": "newData.hasChildren(['user', 'image', 'text'])" } }, "notifications": { "buy_item": { ".validate": "newData.hasChildren(['text'])" } } } }, "alive_lives": { ".write": false, ".read": false } } } Rule NFTTBHFT഑Լͷ৽σʔλʹ VTFS JNBHF UFYULFZΛڧ੍͢Δ
  24. { "rules": { ".write": false, "lives": { "$live_id": { "messages":

    { "$message_id": { ".validate": "newData.hasChildren(['user', 'image', 'text'])" } }, "notifications": { "buy_item": { ".validate": "newData.hasChildren(['text'])" } } } }, "alive_lives": { ".write": false, ".read": false } } } Rule ʁʁʁ
  25. { "rules": { ".write": false, "lives": { "$live_id": { "messages":

    { "$message_id": { ".validate": "newData.hasChildren(['user', 'image', 'text'])" } }, "notifications": { "buy_item": { ".validate": "newData.hasChildren(['text'])" } } } }, "alive_lives": { ".write": false, ".read": false } } } Rule ͜ΕΛ࢖͏
  26. { "rules": { ".write": false, "lives": { "$live_id": { "messages":

    { "$message_id": { ".validate": "newData.hasChildren(['user', 'image', 'text'])" } }, "notifications": { "buy_item": { ".validate": "newData.hasChildren(['text'])" } } } }, "alive_lives": { ".write": false, ".read": false } } } Rule "alive_lives": { ".write": false, ".read": false } ΫϥΠΞϯτ͔Β͸ ಡΈॻ͖ΛҰ੾ͤ͞ͳ͍
  27. { "rules": { ".write": false, "lives": { "$live_id": { ".read":

    "root.child('alive_lives').child($live_id).val() === true", "messages": { "$message_id": { ".validate": "newData.hasChildren(['user', 'image', 'text'])" } }, "notifications": { "buy_item": { ".validate": "newData.hasChildren(['text'])" } } } }, "alive_lives": { ".write": false, ".read": false } } } Rule
  28. { "lives": { "1": { ... }, "2": { ...

    } }, "alive_lives": { "1": true, "2": false } } つまり alive_lives[“1”]がtrueなので 読み取り可能
  29. { "lives": { "1": { ... }, "2": { ...

    } }, "alive_lives": { "1": true, "2": false } } つまり alive_lives[“2”]がfalseなので 読み取り不不可
  30. { "lives": { "1": { ... }, "2": { ...

    } }, "alive_lives": { "1": true, "2": false } } つまり ライブ放送開始/終了了時に ここのフラグを編集する
  31. { "rules": { ".write": false, "lives": { "$live_id": { ".read":

    "root.child('alive_lives').child($live_id).val() === true", "messages": { "$message_id": { ".validate": "newData.hasChildren(['user', 'image', 'text'])" } }, "notifications": { "buy_item": { ".validate": "newData.hasChildren(['text'])" } } } }, "alive_lives": { ".write": false, ".read": false } } } Rule(再掲) Adminユーザ以外に ⼀一切の書き込みを許容しない
  32. ③ Firebaseへリクエスト • WorkerからFirebaseへリ クエスト • Firebase REST APIを叩く •

    kreait/firebase-phpを利利⽤用 • 公式で紹介されてる2つ のうち、こちらのほうが 質がいい
  33. Timestamp • データにtimestampを付与し ておく • このtimestampと現在時刻を ⽐比較して表示するかどうか判 定する { "text":

    “old", "timestamp": 1507226243 } { "text": “still old", "timestamp": 1507226300 } { "text": “new", "timestamp": 1507228000 } 視聴開始
  34. Switch Firebase Instance • 複数のFirebase Realtime Databaseをつなぎ かえる場合はSDKのインスタンスを使いまわ せない •

    ⼀一意なkeyと⼀一緒にインスタンス⽣生成する必要 がある • 複数インスタンスの運⽤用については次から
  35. インスタンス管理理 • Firebase ProjectとGCP Projectは必ず1対1 • ただしGCP ProjectをGUI以外で作る⽅方法がな い… •

    Project作成、Rule設定、Auth情報ダウンロー ド等全てマニュアルでやらなければいけない
  36. Realtime Database vs Cloud Firestore • 新規プロダクトは基本Cloud Firestoreでよい • Cloud

    FirestoreはRealtime Databaseの課題 を解決する形で実装されている
  37. Realtime Database vs Cloud Firestore • ただし以下の場合はRealtime Databaseを検討 すべし •

    Read/Writeオペレーションが⼤大量量に発⽣生する • 10milli sec単位でもLatencyを速くしたい