読者です 読者をやめる 読者になる 読者になる

DesignAssembler

備忘録に近い

PokémonGOのパケット解析

f:id:hyottokoaloha:20160802205453p:plain

最近話題のPokémon GOのパケットをwiresharkで覗いてみました。

iPhoneの通信をwiresharkでキャプチャする方法は以下の記事を参考にしてください。

http://qiita.com/ionis_h/items/661a9a9d41cb1574e357

自分はwiresharkやパケット解析にあまり詳しくなく的外れな事を言ってるかもしれないのでお気付きの点があればコメントください。

なぜ調べたか

Pokémon GOがどれくらいの量の通信をどれくらいの頻度でしているかが気になったからパケット覗いてみようと思いました。

わかったこと

パケットを覗いてしばらくすると定期的に似たような通信をしていることがわかりました。

詳しく言うと、 30秒に1度約20kb のデータのやりとりをサーバーとしていました。(アップロード3kb、ダウンロード20kbといった感じです)

アップロードするのは自分の位置情報、ダウンロードするのは自分の周囲にいるポケモン、ポケストップ、ジムのデータと推定します。

19kbしかダウンロードしないことを考えるとポリゴンデータをいちいちダウンロードするような真似はしてないようですね。これはUnityの力ですね(Pokémon GOはUnityを使用しています)

ちなみにこの通信はTLS v1.2で暗号化されていたので位置情報の漏洩はほとんどないと思います。(位置バレの大きな要因はSNSへのアップロードですね)

通信量が非常に小さく収まってるのはgoogleのprotocol bufferという形式でデータのやりとりを行っているからです。

この通信の軽さのおかげで通信制限が来ていてもあまり不自由なく遊ぶことができます。

この定期通信のキャプチャを以下にあげています。やたらと容量多いのはなぜだろう

https://github.com/asmsuechan/study/blob/master/datafiles/pokemon_send_gps

また、ポケストップやジムの画像にアクセスするとその場所の画像を取っています。恐らくこの通信がPokémon GOで一番通信量を食っています。

例えば以下のURLは新宿御苑の画像です。

http://lh5.ggpht.com/pRhQ7K2PGIsevF4kJsgAZhiqCi9kVXYHZH9mw3Qj3zWjPm2Z6el_-yHv_jRICTNH5OYSXW-7oa55xpMMdiaK1Q

パケットを見ていたらnianticAPIを叩いてるのが見えました。このAPIを使ってpokevisionなどのポケモンの位置情報を表示するサービスができています(7/31にpokevisionは死にました)。正直外部から簡単にアクセスできるのはどうかと思います。

どんなAPIか推測すると、以下のような感じのハッシュがNoSQLに生やしてあり、ある緯度経度の座標が与えられるとそこに近いポケモンのハッシュが返りその値をアプリが見てポリゴンデータなどを呼び出しているようなものだと思います。

{ pokeid: 10, lang: 11.1111, lat: 11.1111, set_time: 00:00:00 }

こういう解析もあります。ポケモンデータです。これはUnityから抜いたのでしょうね。

ちなみにこの解析データを見ると、伝説のポケモンは実装されているがBaseCaptureRateが設定されていなくてイベント時に降ってくるのかな、と予測してます。

まとめ

Pokémon GOの通信は軽い

学生枠で申し込んだ開発合宿で日本酒をしこたま飲んだ話 #mokumoku_onsen

こんにちは。

夏だ!合宿だ!温泉だ!

7/16~7/18、新潟県の南魚沼でもくもく温泉 開発合宿に参加しました🎉

mokumoku-onsen.connpass.com

この合宿は開発合宿と銘打ってありますがかなりゆるふわな雰囲気で、遊びたい人は好きにダラダラしてもOK!いつでも飲酒可入浴可だぜウェイ!って感じの合宿です。もちろん開発もします。

ちなみに今回5回目で、前回までは千葉県の猫がいる土善旅館という旅館で開催されました。

なんと自分は「学生スポンサーされる枠」でかなりお安く参加することができました!!!!!

7/16(1日目)

越後湯沢

11:40の新幹線に乗って13:00頃越後湯沢に着きました。

寿司 🍣

昼ごはんタイム!!!

f:id:hyottokoaloha:20160717002850j:plain

ヤバい🍣🍣🍣🍣🍣

ぽんしゅ館 🍶

ぽんしゅ館というおちょこ1杯(だいたい)100円の利き酒が出来る素晴らしい空間があります。

www.ponshukan.com

f:id:hyottokoaloha:20160717004049j:plain

f:id:hyottokoaloha:20160717004105j:plain

自分は1000円分ぶち込んでこんなの飲みました(一部です) f:id:hyottokoaloha:20160717004210j:plain f:id:hyottokoaloha:20160717004227j:plain f:id:hyottokoaloha:20160717004250j:plain

最高に意識を低めてから旅館に突入

六日町

他の人たちと合流した後旅館がある六日町駅に向かいました。 六日町駅からは旅館の送迎バスに乗りました(10分くらい)

五十沢温泉ゆもとかん

宿に到着 🎉 f:id:hyottokoaloha:20160717004720j:plain

部屋からはこんな景色 f:id:hyottokoaloha:20160717004338j:plain

自己紹介タイム

皆さんどんな言語使うか、今回どんなことするか、を自己紹介しました。 こんな感じの人がいました

  • 酒飲もう 🍶
  • ライブラリのメンテナンス 😎
  • 期末レポート 👼
  • HTCvive 💂

HTCviveさせてもらったのですがメッッッチャクチャ楽しかった・・・・・・
でも札束ゲー・・・💸

Tシャツ

なんともくもく温泉開発合宿のTシャツができました👕

参加者のデザイナーさんが作ってくれたイカすTシャツです。これを着て大学に行ってモテモテになるつもりです。 f:id:hyottokoaloha:20160717102308j:plain

温泉

合宿メインの温泉です。どっぷり浸かります。

f:id:hyottokoaloha:20160717004314j:plain

混浴

混浴

混浴

f:id:hyottokoaloha:20160717002247j:plain 颯爽と入浴!!!

4~5人のおじさんが無言でただ座っていました。

露天風呂だったんですが適温で外からひぐらしの鳴き声が聞こえてきてかなり良かったです。

ご飯 🍚

最高にうまいご飯 f:id:hyottokoaloha:20160717004635j:plain

花火 🎆

最高の夏にしていこうな 👊

f:id:hyottokoaloha:20160717004755j:plain

飲み会夜の部

闇の話で盛り上がりました😈

7/17(2日目)

目新しいことが無さすぎる2日目の感想

朝ごはん

セルフで取っていくスタイルの朝ごはん

f:id:hyottokoaloha:20160717100326j:plain

元気の無さを感じる盛り付け

そして朝から攻めていく🍶🍶🍶🍶🍶

f:id:hyottokoaloha:20160717105514j:plain

昼ごはん

午前中をもくもくして終了し昼ごはん。

カレーを食べました。

もくもくタイム

後ろの方で死んでいるのは俺です f:id:hyottokoaloha:20160718105653j:plain

「畳の重力が強かった」 f:id:hyottokoaloha:20160718105741j:plain

f:id:hyottokoaloha:20160718105945j:plain

散歩

息抜きに散歩に出ました。とりあえず目指すは山。しばらく山の写真が続きます。

f:id:hyottokoaloha:20160717162235j:plain f:id:hyottokoaloha:20160717162257j:plainf:id:hyottokoaloha:20160717162309j:plainf:id:hyottokoaloha:20160717162321j:plainf:id:hyottokoaloha:20160717162334j:plainf:id:hyottokoaloha:20160717162359j:plainf:id:hyottokoaloha:20160717162411j:plainf:id:hyottokoaloha:20160717162430j:plain f:id:hyottokoaloha:20160717162246j:plain

5kmくらいフラフラしました。

夜ご飯

f:id:hyottokoaloha:20160718101745j:plain

今日も美味しかった。

そのあと1時くらいまで界隈の諸々を日本酒と共に話しました。

↓この日本酒とても美味しかったです。参加者の方が新潟の酒造で買ってきた銘柄なしのお酒です(。◉ᆺ◉)

f:id:hyottokoaloha:20160718102504j:plain

8/18(3日目)

最終日、異様な眠気と戦いながら10時にチェックアウトしました。

次回予告

次回は猫がいる旅館で開催!

mokumoku-onsen.connpass.com

合わせて読みたい

参加者の id:niwatakoさんのブログです

niwatako.hatenablog.jp

参加者の id:sinsokuさんのもくもく成果ブログです sinsoku.hatenablog.com

インスタンス変数をDRYにする

Railsで同じビューを全ページに実装する時、全コントローラーに同じインスタンス変数を生やすのはDRYじゃないのでapplication_controller.rbにまとめて書きました。

#application_controller.rb
class ApplicationController < ActionController::Base
  before_action: set_variables

  private
    def set_variables
      @hoge = Hoge.all
      @foo = Foo.all
    end
end

もっといい対処法をご存知の方がいれば教えて下さい。

Railsアプリケーションを6倍速くしてから更に5倍速くした

hyottokoaloha.hatenablog.com

500msから更に5倍速くした

やった事

Rails.cache

Rails.cache使ってモデルメソッドの結果をキャッシュに入れました。

def hoge
cache_variable = Rails.cache.read("#{column_name}_cache")
  if cache_variable.nil?
    #重い処理してarrayに入れる
    Rails.cache.write("#{column_name}_cache", array.to_a, expires_in: 24.hour)
    array
  else
    cache_variable
  end
end

こうしたらアクセスしてもsql文が発行されなくなった。

最終的に

Completed 200 OK in 3271ms (Views: 53.9ms | ActiveRecord: 2300.6ms)
↓
Completed 200 OK in 112ms (Views: 15.4ms | ActiveRecord: 4.9ms)

29倍速くなりました。

キャッシュ便利すぎて怖いので何かデメリットをご存知の方がいれば教えて下さい。

参考

Railsアプリを6倍高速化した話

正確に言えば通常の6倍遅いRailsアプリケーションを通常の速度にした、といった感じです。

まず、こちらを見てください

Completed 200 OK in 3271ms (Views: 53.9ms | ActiveRecord: 2300.6ms)

あぁ・・・・・

ActiveRecord2300msって・・・・あと800msくらいよくわからない時間がある・・・

鈍速なのでレスポンスタイム500msを目指します。

計測

newrelic見るとだいたいどこがボトルネックか分かりました。

f:id:hyottokoaloha:20160505121109p:plain

コントローラーでviewに投げる変数作るときにだいぶ時間食ってますね。

やった事

  • activerecord周りの処理の見直し
  • fragment cacheの使用

この2つでだいぶ速くなりました。

activerecord周りの処理

ログを見るとselect文が大量発生していました。

Article Load (4.2ms)  SELECT  `articles`.* FROM `articles` WHERE `articles`.`id` = 1 LIMIT 1
Article Load (3.9ms)  SELECT  `articles`.* FROM `articles` WHERE `articles`.`id` = 5 LIMIT 1
Article Load (3.2ms)  SELECT  `articles`.* FROM `articles` WHERE `articles`.`id` = 61 LIMIT 1
Article Load (2.8ms)  SELECT  `articles`.* FROM `articles` WHERE `articles`.`id` = 31 LIMIT 1
Article Load (3.1ms)  SELECT  `articles`.* FROM `articles` WHERE `articles`.`id` = 78 LIMIT 1

まずこれをなんとかするためにこのsqlが発行される場所を見ました。そして該当箇所を探してincludesします。

Calendar.includes(article: [:tags])

これで発行されるsql文がスマートになりました。

SELECT `calendars`.* FROM `calendars`  ORDER BY date DESC
  Article Load (1.7ms)  SELECT `articles`.* FROM `articles` WHERE `articles`.`id` IN (1, 61, 27, 129, 127, 131, 142, 128, 30, 83, 84, 29, 85, 137, 19, 80, 28, 159, 78, 90, 35, 79, 149, 31, 134, 77, 87, 156, 163, 157, 133, 81, 17, 88, 138, 24, 151, 89, 93, 130, 152, 153, 154, 140, 161, 135, 145, 144, 155, 86, 82, 164, 41, 139, 132, 36, 162, 160, 95, 96, 62, 148, 166, 92, 25, 141, 165, 143, 136, 18, 21, 147, 20, 146, 34, 52, 150, 94, 46, 53, 91, 39, 23, 22, 38, 158, 167, 47, 33, 37, 42, 45, 63, 32, 43, 48, 40, 51, 49, 44, 168, 54, 56, 169, 179, 180, 184, 50, 65, 171, 172, 173, 177, 178, 181, 182, 183, 55, 170, 64, 66)

ここで時間は

Completed 200 OK in 1052ms (Views: 65.1ms | ActiveRecord: 124.4ms)

とりあえず3倍速ですね。

fragment cacheの使用

ボトルネックの該当viewは全ページに表示されるメニューだったので、キャッシュを使います。

該当viewを以下のように変更します。パーシャル化してあるのでこれだけでOKです。

<% cache("right_menu", skip_digest: true, expires_in: A_DAY) do %>
#該当view
<% end %>

これで時間を見ると

Completed 200 OK in 496ms (Views: 23.1ms | ActiveRecord: 41.9ms)

更に倍速の6倍速!目標に到達しました。

Chromeでの実測だとだいたい+1000msされるのですが、これはAnalyticsやFacebookの読み込みが影響しているようです。どうにかしたいです・・・・・

参考

Railsでブラウザ判定

Railsでブラウザ判定する時にrequest.env['HTTP_USER_AGENT']でブラウザ判定しようとしたのですが、ChromeからうまくSafariが取れませんでした。

request.env['HTTP_USER_AGENT']を見てみると

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.86 Safari/537.36

となっていて、正規表現Safariという文字があった時に処理をするようにしているためChromeでも反応してしまいます。

どうしようかなと迷っていたらbrowserといういいgemを見つけました。

インストールはGemfileに以下を追加するだけです。

#Gemfile
gem 'browser'

Safariの判定は、使いたい場所で

<% if browser.safari? %>

とするだけです。モバイル判定も

<% if browser.device.mobile? %>

とするだけです。とても便利です。

参考

Rubyの時間の扱い

時間を表すクラスにはDateとTimeとDateTimeクラスがあって、Dateクラスは日付、Timeクラスは時間、DateTimeクラスは日付と時間をそれぞれ扱います。

時間を表示する

最初1つずつフォーマットごとに切り出して連結していたのですが、strftimeという便利なメソッドがありました。

date_time = Time.now
=> 2016-05-02 06:58:51 +0900
date_time.year.to_s + "/" + date_time.month.to_s + '/' + date_time.day.to_s + '/' + " " + date_time.hour.to_s + ":" + date_time.min.to_s
=> "2016/5/2/ 6:58"
date_time.strftime("%Y/%m/%d %H:%M")
=> "2016/05/02 06:58"

文字列を時間に変換する

文字列を時間に変換するにはどうすればいいかと思っていたらparseメソッドが用意されていました。流石です。

date_time = "2016/05/01 00:00"
DateTime.parse(date_time.gsub("/","-"))
=> #<DateTime: 2016-05-01T00:00:00+00:00 ((2457510j,0s,0n),+0s,2299161j)>

参考

http://docs.ruby-lang.org/ja/1.9.3/class/DateTime.html