台湾に行ってきました(Computex Taipeiとかもろもろ)

休暇を利用して、台湾へComputex Taipeiの見学およびリサーチをしてきました

Computex Taipei

f:id:tomo_watanabe:20140603144456j:plain

  • ESECやETよりも人は少ないように見える。日程の前半は海外在住者のみ入場可(入場口で揉める某台湾人)

f:id:tomo_watanabe:20140604103933j:plain

  • 規模は東京ビッグサイトの東館全体の3倍くらい(南港の方が人は多い気がする)
  • 見て回るだけなら、1日半あればとりあえず回れる。期間中はパスがMRT乗り放題のカードになる

f:id:tomo_watanabe:20140603145701j:plain

  • 出展内容は玉石混交、かつアイテムによって整理されていないので、重要なのがどこにあるか把握困難
  • PCメーカのメインは南港展覧館にあり、Intel, acer, ASUSなどがPCを中心に展示している(IntelのReal Senseを使って触れないで操作するデモだけど、そんな大きなモニタキッチンにry)

f:id:tomo_watanabe:20140605123024j:plain

  • 部品としては、ケーブル・電源・キーボード・マウス・スピーカ・スマホアクセサリがほとんどで、どれも同じような物が多い
  • スマートフォンは、深センのメーカを中心にいくつかがODMの生産を受け付けている感じ。CPUはMediaTekが多い
  • 台湾ではLTEは始まっていないが、4GLTEを謳ったスマートフォンがちらほら
  • スマートウォッチ系もたくさん出ていた。しかし中身は「万歩計」付き「時計」というのがほとんど...(これはE-inkのタッチパネル)

f:id:tomo_watanabe:20140604123642j:plain

  • Android Wear」と大々的に書いてあるブースに行ったら、完全なコールドモックだったよ/(^o^)\

f:id:tomo_watanabe:20140605135154j:plain

  • ハードウェアばかりで、ソフトウェア系はほぼ皆無(当たり前だけど)
  • 展示側からすると、MOQいくつでいくらで買う?くらいの商売しか考えていないような感じ
  • ドラレコがかなり出ていた。HUDもチラホラ。GoProのパクリのような物も。自動車関連製品の安いのはここから輸入されるような感じがする。実際、台北のタクシーはほぼ100%ドラレコが付いている。光華商場でもドラレコはかなり売られていた(バックミラーの下に付いてるのがドラレコ

f:id:tomo_watanabe:20140604184021j:plain

まとめ

方向性や情報収集が目的なら数年に1度行けば十分かと。自分たちの戦略があって、何が足りないか探しに行くにはいいかもしれない。 がっつり商談するか、現地にコネがあって、いくつか会社をハシゴするのが現地周り的にはヨサゲ

マスプロダクトなハードウェアは台湾や中国から日本に入ってくるけど、今のkickstarterのような新しいニッチなハードウェアは中華圏からではなく、むしろ先進国から産まれてくるということを認識できた。マスなハードウェアは箱でしか無く、これからのハードウェアはネットワークとの連携でむしろソフトウェアが重要視されるため、今までの流れが変わりつつあるように思える

台北市

収入格差が大きくなっている感じ。昔よりも高級車が増えた。台北市内の土地が高騰していて、軒並み億の値段が付いているとのこと。実際、天母の建築中のマンションは10億らしい。あまりに高いのでむしろ東京の土地を買いに行く台湾人もいるとか。この土地の高騰はバブルっぽい感じがする。一般の人が普通に投資してる。中国の資金が流れ込んでるのか、中国の経済崩壊が起きたら巻き込まれる可能性がありそう(証券会社に集まる人々。取り付け騒ぎかとオモタ)

f:id:tomo_watanabe:20140603154558j:plain

  • 台北駅の少し南の風景、だいたいこんな感じ

f:id:tomo_watanabe:20140603175300j:plain

  • 老若男女みんなスマートフォン、老齢になると大画面のを使っている。みんなそれで写真撮ってますが使いこなしているのか?
  • iPhoneは見たところ10% 〜 15%程度、ほとんどがAndroid端末
  • バスこんなに走ってたかな...高速道路でも一般道でもトラックはあまり多くない
  • 暑いからか、ショートパンツの女性が多い
  • 鼎泰豊あいかわらず美味しいです

f:id:tomo_watanabe:20140603184121j:plain

f:id:tomo_watanabe:20140605151523j:plain

  • ユニクロは日本のほうが安いw
  • LINEユーザ多い、そこら中でピコピコ音がしてた
  • 光華商場はそれほど安くない
  • ヤマト運輸が制服とかも日本とほとんど同じっぽい感じだった
  • 足裏マッサージは痛くなかった。足裏+脹脛で50分2000円くらい

f:id:tomo_watanabe:20140605213416j:plain

  • Fablab台北は街中のわかりづらい場所にある...。3DプリンタからCNC、レーザカッターまでひと通りそろっている

f:id:tomo_watanabe:20140605182755j:plain

f:id:tomo_watanabe:20140608095030j:plain

  • 教室は円形に並べられてた。これは最前列ツライw

f:id:tomo_watanabe:20140608095521j:plain

新竹 ITRI

今回、某台湾人の計らいで新竹にあるITRIに行ってきました。日本で言うところの産総研と言った感じ。台北のFabLabとかとリンクしないのかなーとか思ったりしたけど、台北からバスで1時間と離れているので難しそう。

f:id:tomo_watanabe:20140606151559j:plain

  • 中でやってるのを少し見せてもらいましたが、ソフト・ハードともにやってる模様
  • 災害時にスマホから写真を撮って、投稿するとマップに表示されるシステムとか作ってました。たぶんこれ。これってセカイカメry
  • その他独自アーキテクチャCPUとか、UI/UX研究とか
  • 中の人たちは箔付けのために転職してくることが多く、早ければ数ヶ月で大手メーカへ転職してしまうらしい

観光地

前回台湾に行った時に、故宮などの有名どころはひと通りいったので、今回はちょっと違うところへ行ってきました。車で回ったのでだいぶ楽でしたが普通だとかなり面倒な場所かもしれません。観光地は中国人と韓国人がバスツアーで回っているらしくかなり多いです。日本人は最近は個人ツアーなのかもしれません。そういえば彼らはみんな高いデジタル一眼を持っているのですが、日本で流行りのミラーレス一眼ではなかったですね。なんででしょう

  • 野柳地質公園は奇岩の公園。かなり観光地として人の手が入ってしまっていて微妙...

f:id:tomo_watanabe:20140607120232j:plain

  • 平溪線はノスタルジックな感じで良い。鉄道好きならなお楽しめる。今回は車で行ったけど、電車の方が楽しそう

f:id:tomo_watanabe:20140607154447j:plain

f:id:tomo_watanabe:20140607144044j:plain

  • 十分瀑布は台湾のナイアガラらしいです。日本のナイアガラの吹割の滝よりはマシです(^^;

f:id:tomo_watanabe:20140607140346j:plain

歩いて行く隣には平溪線が走っている

f:id:tomo_watanabe:20140607151558j:plain

まとめのようなもの

たまーに海外行くと、色々現地の事情とか見られてやっぱりいいですね。今回は現地で某ニセ台湾人に案内してもらったり、たまたま現地にいた某スマートグラス関係者と情報交換したりできました。何年か前に台湾に行ったこともあって、時間の流れとかトレンドの移り変わりも見えてました。Computex Taipeiとかのキッカケが無いとなかなか行けないので、ちょうど良い機会でした。次は何年後に行こうかなー

ArduinoのWiFiシールド(CC3000)でWiFiのセットアップ

ArduinoWiFiが使える、かつTELEC取得済みのCC3000 WiFiシールドを入手したので、セットアップしてみました。

まずはハンダ付け

スイッチサイエンスから購入したのですが、ブツがビニールに入ってるだけで特に説明書らしきものはありません。Arduinoシールドとはいえ、ピンソケットも付属していないので、一緒にピンソケットも購入しておいたほうがいいでしょう。

このようなWiFiシールドだとさらに重ねる可能性があるので、ピンソケットをこのようにハンダ付けしておきます。なおこのシールドはSPIを使用していますので、重ねる場合は注意して下さい。

f:id:tomo_watanabe:20140510204418p:plain

WiFiのセットアップ

WiFiのセットアップはsparkfunのページのCC3000 Hookup Guideを見ながら行えば問題ありません。

CC3000のライブラリのダウンロード

ライブラリのコードはGithubにありますが、リポジトリのmasterをココからzipファイルで落としてきて展開した方が楽です。落として展開したフォルダを「SFE_CC3000_Library-master」→「SFE_CC3000_Library」のようにちょっと修正して、ライブラリフォルダの中に放り込みます。Macだと~/Documents/Arduino/librariesになると思います。

WebClientで動作確認

ライブラリにファイルを置いたら、ArduinoIDEを起動します。動作確認はArduino Uno R3で行いました。

f:id:tomo_watanabe:20140510210119p:plain

ファイル→スケッチの例→SFE_CC3000_Library→WebClientでサンプルスケッチをオープンします(※Macの場合)

// Constants
char ap_ssid[] = "SSID";                  // SSID of network
char ap_password[] = "PASSWORD";          // Password of network
unsigned int ap_security = WLAN_SEC_WPA2; // Security of network
unsigned int timeout = 30000;             // Milliseconds
char server[] = "www.example.com";        // Remote host site

SSIDとPASSWORDの部分を、接続したいSSID名とパスワードに変更するだけです。serverは接続確認を行う先のURLです。このスケッチは起動時にクライアントのIPアドレスを設定して、"http://www.example.com/index.html"をGETで取ってくるようになっています。書き換えたらArduinoに書き込みます。書き込みが終ったらシリアルモニタを起動します。この時通信速度は115200に設定します。

正常にIPアドレスを取得し、www.example.comからデータを取得できると下記のようにGETで取得したログが流れます。

f:id:tomo_watanabe:20140510210750p:plain

このスケッチをベースにスケッチを書けば通常のEthernetと同じように使えるようになります。

SmartConfigを使ったセットアップ

このCC3000のウリは、スマートフォンSSIDとパスワードを設定できることのようなので、そちらも試してみます。

ファイル→スケッチの例→SFE_CC3000_Library→SmartConfigでサンプルスケッチをオープンします(※Macの場合)

ここでスマートフォンアプリを用意しておきます。iOS版とAndroid版があるのですが、iOS版はiTunesからダウンロードできるのに対し、Android版はなぜかTIにアカウントを登録してapkを自分でインストールする必要があります。今回はapkインストールするのが面倒だったので、iOS版でセットアップしましたが基本的には同じようです。

iPhone

  • SSID : 接続するアクセスポイント
  • Password : パスワード
  • Key : オプションなので特に設定する必要はありません

ここまで設定を入力したら、Aeduinoにスケッチを書き込んでシリアルモニタを起動します。Arduinoが起動すると"Starting SmartConfig"とメッセージが出たら、スマートフォンの"start"ボタンをタップします。成功すると、下記のようにsparkfunのサイトにpingを打って動作確認してくれます。

f:id:tomo_watanabe:20140510212737p:plain

うまく設定ができない(Arduinoタイムアウトする)と下記のようになります。スマートフォンのアプリの"start"のタイミングに気をつけてください

f:id:tomo_watanabe:20140510212928p:plain

FastConnect

SmartConfigでの設定は、毎回起動時にSSIDの設定しなければならず面倒ですが、実は一度SmartConfigでSSIDとパスワードを設定しておけば、CC3000内部に記憶されるので、このFastConnetが利用できるようになります。(電源を外しても記憶しているようなので、内部Flashに記憶しているのかな)

ファイル→スケッチの例→SFE_CC3000_Library→FastConnetでサンプルスケッチをオープンします(※Macの場合)

SmartConfigで設定済みなら、何の問題もなく下記のように動作するはずです。

f:id:tomo_watanabe:20140510213758p:plain

このSmartConfigは結構便利ですね。

netatmo ウェザーステーションを買ってAPIを叩いてみる

netatmo ウェザーステーションとは

netatmoウェザーステーション

公式HP ※公式のJPは死んでるっぽいので、USサイト

その名の通り気象情報をセンサーで取得するデバイスです。基本構成は2つのセンサーデバイスで、大きいほうが室内用、小さいほうが屋外用となっています。電源は室内用はUSBから、屋外用は単4電池2本で駆動します。それぞれのセンサーから取得できるデータは以下になります。屋外用のセンサーというのは実はありそうで無いので、ベランダなどに設置しておくと今の屋外気温が取得できるので面白いです。

  • 室内用

    • 気温
    • 湿度
    • 気圧
    • CO2
    • 騒音
  • 屋外用

    • 気温
    • 湿度

これを設置してユーザ登録してログインすると、PCではこんな画面でデータを見ることができます。

※地図の部分はボカしています

f:id:tomo_watanabe:20140501213157p:plain

ちなみに、全世界の今の気温もGoogleMap上でみることができます。クリック

また、iPhoneアプリAndroidアプリもあります。Androidではウィジェットに設定できるのでこれは便利!です。

f:id:tomo_watanabe:20140501214917p:plain

IFTTTと連携

このデータを元にIFTTTを使って連携しようと考えました。例えばこんな感じのレシピを作れます。

f:id:tomo_watanabe:20140501213757p:plain

これは「湿度が10%を切ったら、Tweetする」ということになります。とはいえ、本当にやりたかったのは「毎朝8時に、今の屋外の気温をTweetする」のようなことがしたかったのでこれはイマイチです。IFTTTではnetatmoウェザーステーションはトリガーにしか使えない、かつこの場合、3つの事象が含まれるのでIFTTTでは実現できません。

netatmoのAPIを利用

というわけで、netatmo APIを利用して作ることにしました。ここにアクセスして、「MY APPLICATIONS」を登録することでAPIを叩くことができるようになります。

APIの取得の仕方等はNetatmo ウェザーステーションを買ってみたので Node.js でいじってみたを参照して下さい。

今回はVPSを使わず、手元にあったRaspberry Piを使用して、下記のような構成を作ってみました。

結果

これはこれで簡単にできるので、もう少し色々連携させたくなってきました\(^o^)/ Raspberry Pi上でMQTTのブローカーを導入して、周辺デバイスと連携とか

Mosquitto(MQTT)を動かしてみた

MQTTとは

MQTT(MQ Telemetry Transport : MQはMessage Queuing?)というプロトコルで、TCP/IP層で動作するWebSocketっぽいもの(ザックリ)。詳しくはそこはかとなく書くよん。を見てもらった方が分かりやすいです。簡単に言えば、M2Mでの使用を考えた軽量なメッセージプロトコルという感じです。Mosquittoはブローカの実装で使用します。

2013年のカンファレンスでは、2011年当時の「Beluga」(現Facebook Messenger)に使われているという発表があったようです

MQTTのブローカとクライアントの動作確認の構成

今回下記のような構成でMQTTの動作確認を行いました。

  • ブローカ(サーバ):Mosquitto
  • クライアント:Paho Python版クライアント

f:id:tomo_watanabe:20140418232239p:plain

Mosquittoにsub.pyで接続し、pub.pyによってpublishされるとsub.py側に出力されることを確認します。

Mosquitto(ブローカ)のインストール

パッケージからインストール

Ubuntuでは基本的にはパッケージインストールができるので

$ sudo apt-get install mosquitto

以上でインストールと起動まで完了します。停止・再起動はserviceコマンドで行えます。

ソースからコンパイルする場合

開発環境のインストール

$ sudo apt-get install gcc make g++ libssl-dev libc-ares-dev

Mosquittoのダウンロードページからソースを落として展開し、makeしてインストールします。

$ wget http://mosquitto.org/files/source/mosquitto-1.3.1.tar.gz
$ tar -xvzf http://mosquitto.org/files/source/mosquitto-1.3.1.tar.gz
$ cd mosquitto-1.3.1
$ make
$ sudo make install

環境設定ファイルを設定します。

$ sudo cp /etc/mosquitto/mosquitto.conf.example /etc/mosquitto/mosquitto.conf

デフォルトでMQTTは1883ポートを使用します。必要に応じてサーバのポートを開放します。

起動します。

※引数なしの起動方法では前述のconfファイルは使われず、デフォルトで起動します。-cオプションでファイルを指定する必要がありますが、何も修正していなければ動作はデフォルトとなります。

$ mosquitto
1397719018: mosquitto version 1.3.1 (build date 2014-04-17 15:13:13+0900) starting
1397719018: Using default config.
1397719018: Opening ipv4 listen socket on port 1883.
1397719018: Opening ipv6 listen socket on port 1883.

この場合、サーバがIPv4/6両対応なので、ポートそれぞれでリッスンしている状態になります。

PahoクライアントでPub/Subを実行する

Pahoの下の方に各言語用のダウンロードリンクがありますので、そこからcloneしてきて利用するのがいいのと思います。今回はPythonクライアントのexampleを少し修正してsub.pyとpub.pyを作成しました。

paho MQTT pythonライブラリのインストール

$ sudo pip install paho-mqtt

sub.pyの実行

詳しい解説は後回しにしてまずは実行してみましょう。まずは待ち受けのsub.pyを実行しておきます。

コードはsub.pyにあります。

起動して、ブローカサーバに接続するとこのようなメッセージになります。

$ python sub.py
rc: 0
Subscribed: 1 (0,)

ブローカサーバへの接続が失敗した場合こんなメッセージが出ます。

Traceback (most recent call last):
  File "sub.py", line 60, in <module>
    mqttc.connect("HOST", 1883, 60)
  File "/Library/Python/2.7/site-packages/paho/mqtt/client.py", line 588, in connect
    return self.reconnect()
  File "/Library/Python/2.7/site-packages/paho/mqtt/client.py", line 710, in reconnect
    self._sock = socket.create_connection((self._host, self._port), source_address=(self._bind_address, 0))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 571, in create_connection
    raise err
socket.error: [Errno 61] Connection refused

接続に成功していればブローカサーバ(Mosquitto)の方では以下のようなメッセージが表示されているはずです

1397719236: New connection from 2001:3e0:0:*:*:*:*on port 1883.
1397719236: New client connected from 2001:3e0:0:*:*:*:* as paho/E917914937EA634F0F (c1, k60).

一部"*"でマスクしています。

pub.pyの実行

次にpub.pyを実行して、メッセージを送信します。

コードはpub.pyにあります。

コンソールをもう一つ開いてpub.pyを実行すると直ぐに終了します。

$ python pub.py
1

sub.pyの出力の方を見てみると、1行メッセージが追加されていると思います。

rc: 0
Subscribed: 1 (0,)
message 0 Hello MQTT!

pub.pyを実行することにより、subscribeしていたsub.pyの方にメッセージ送信が行われたことがわかります。

解説

sub.pyの方から見ていきます。(以下抜粋)

# メッセージを受信した時に呼ばれる。
def on_message(mqttc, obj, msg):
    print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))

# 接続先のブローカを設定する
mqttc.connect("HOST NAME", 1883, 60)
# "message"というtopicをsubscribeする
mqttc.subscribe("message", 0)

sub.pyは起動すると"message"というtopicが来るまで待機状態になります。pub.pyを実行して。メッセージを受信すると、topicとqosとpayloadを表示します。今の場合は

  • topic : message
  • qos: 0
  • payload : Hello MQTT!

pub.pyの方はこれだけですね。

publish.single("message", "Hello MQTT!", hostname="HOST NAME")

"message"というtopicに"Hello MQTT!"を送信しています。qosは省略されていますが、デフォルトが0になっています。今回は簡単にするためにtopcを"message"としましたが、これを"$SYS/#"のようにワイルドカードで指定したり、"paho/test/single"のようにすることもできます。この指定の仕方はMQTTの仕様のようで、これを利用して各々のM2M機器がsubscribeする対象を設定できるようになっています。

まとめ

MQTTを実動作させるテストを行いました。MQTTの実装はLinuxMacだけでなく、mbedやArduinoAndroidなどにもありますし、様々な言語での実装もあります。またNode.jsでWebSocketと組み合わせてブラウザへの出力もできます(この辺は後日)僕はこのM2M用途としてのMQTTはよく知らなかったのですが、実際使用してみると結構いろいろなことが出来そうな気がします。プロトコルはシンプルだし、QoSの概念が導入されているのも面白い点です。

スキーとGoProとウェアラブルの関係を考える

初めての趣味でのエントリになります。

今シーズンは週末に冬山に行くことが多いです。スタッドレス買ったので勿体無いというのもありますが。一昨年にGoPro Hero2(今はHero3)を買ってからスキーの時に持参して撮影をしてみたりしてるので公開してみようと思います。

GoProのアタッチメントはヘッドストラップでこんな感じ

f:id:tomo_watanabe:20131230091507j:plain

まずは動画を公開。場所はオグナほたかスキー場のゲレンデトップから第6ペアリフト沿いのコースを滑ってます。1本目は割りとゆっくりめでウェーデルンで速度を調整しています。2本目は同じ小回りですが、こちらはずらさずカービングターンでスピードを殺さずに滑っています。違いがわかるでしょうか?

ウェーデルンで滑走

カービングで滑走

撮影した斜面を下から見るとこんな感じ。最大斜度は約20度ちょっとくらい

f:id:tomo_watanabe:20140201100208j:plain

GoProとは

GoProは今は「アクションカメラ」というジャンルを確立した「デジタルカメラの派生」で、JVC・ケンウッドソニーが対抗機種を投入して盛り上がっています。GoProの成功に関してはこちらの記事アクション・カメラの代名詞「GoPro」誕生秘話が創業者へのインタビュー映像で明らかに

個人的にGoProの登場時に凄いなと思ったのは

  • LCDレスなどの従来のデジカメにあった機能レスの割り切り
  • 広角でかつ、映像自体がかなり綺麗に取れる
  • 天候に左右されないレンズ性能とハレーションの少なさ(逆光でも白飛びしない)
  • 各シーンに合わせたアタッチメント

購入してから性能的には満足していますが、一方でいくつか不満点もあります(ただしHero2に限る)

  • 電池持ちが悪い。8GBのSD差しても意味ない...(´・ω:;.:...
  • ボタンの反応がわかりづらい。音がするものの、もうちょっとなんとかならんの・・・
  • 手ぶれ補正が欲しい(スキー動画揺れすぎ)

ウェアラブルデバイス

自分が思うにGoProは「ウェアラブルデバイス」です。スポーツという制限化の中で、身に付けることによって従来撮れなかった映像を撮れるのです。スキーの場合は両手が塞がっているので、こういう場合に非常に便利です。

ウェアラブルに必要な要素として

  • 必然性
  • ファッション性

だと思いますが、GoProは「スポーツという制限化での必然性」があります。この例はGoogle Glassが遠隔手術に使われたり(Google Glassで手術をライブ中継、教育効果に期待:米大学病院)、Kinectが手術の病巣カルテをジェスチャーで操作する(Opect)どちらも必然性があって、制限化での活用が非常に有効です。またどちらも一般の人にとっての日常ではなく、非日常な利用です(Google Glassの発表の時は飛行機からパラシュートで飛び降りてくるというアクションカメラ的デモであって、実はなんの日常的な必然性は無い...)

一方で我々がウェアラブルとして身につけているものは

  • 眼鏡
  • 腕時計
  • 指輪・ネックレス
  • 帽子・手袋

どれも今は必然性よりもファッション性が重要視されている製品です。眼鏡は目が悪い人にとっては必然ですが、ファッション性が重視されてますし、腕時計は昔は正確な時間を知ると言う意味で必然性がありましたが、今はファッション性の方が高いですね。指輪は元来、魔除けや魔力を身につけるといった必然性から生まれたもののようです。帽子・手袋は防寒の必然性もありますがファッション性もあります。

ウェアラブルデバイス百花繚乱の昨今ですが、ウェアラブルは「必然性」ありき、後「ファッション化」するのものではないのでしょうか?今のところ「日常の必然性」を具現化したウェアラブルデバイスは難しいのでは?と考えています。自分としては「非日常シーンでの必然性」を具現化した「面白いウェアラブルデバイス」が出てくることを期待しています。

Node.js + Socket.IO + pm2でデーモン化とクラスタリング

Socket.IOをpm2でクラスタリングするには、ちょっと工夫が必要だったのでメモ

forever

Node.jsのデーモン化といえばforeverです。しかしクラスタリングしようとすると、Node.jsのコードをクラスタリング対応で書かないといけないのでやや面倒だったりします。今回、foreverよりも高機能なpm2を使ってクラスタリングを試してみました。

pm2

pm2はforeverと同じようにNode.jsをデーモン化するツールですが、モニタ機能やクラスタリング機能などかなり高機能になっています。

pm2のインストール

pm2はグローバルでインストールします。

$ npm install -g pm2

対応するNode.jsのバージョンは古いと動かないかもしれません。今回はv0.10.20(on Mac)で動作確認しました。動かすNode.jsアプリはこちらのSocket.IOのサンプルアプリです。このコードだとSocket.IOのバージョンが古いようなので、一旦npm uninstall socket.ioして、再度npm install socket.ioで再インストールしました。

pm2を使ってクラスタリング

$ pm2 start app.js -i max

として起動するとプロセスがCPUコア数分だけ起動し、クラスタリングされます。簡単ですね。起動リストを表示させるには

$ pm2 list

f:id:tomo_watanabe:20140131120059p:plain

プロセスが4つ起動されているのがわかります。この時にブラウザでアクセスして確認します。

f:id:tomo_watanabe:20140131120326p:plain

片方がxhr-pollingになってたりしますね。これリロードするとwebsocketになったり、xhr-pollingになったりなんとも不安定になります。

ログを表示するには

$ pm2 logs

f:id:tomo_watanabe:20140131120712p:plain

ところどころに警告がでています。

warn  - client not handshaken client should reconnect

調べてみるとSocket.IOの接続に起因するものでSocket.IO or WebSocket を AmazonELB でバランスする検証にありました。

つまり、 Socket.IO は接続確立までに 2 回リクエストを投げるということです。 そしてこの最初の要求と、 WS の接続要求は、「同じインスタンス」にアクセスする必要があります。 二つの接続がバランスされて別々のサーバに行くと、ハンドシェイクがエラーになり、リトライが発生します。 たまたま二回同じサーバにいけば、接続が確立できるので、バランスするサーバが増えるほど成功確率が減 り、確立までの時間が長くなります。

redisの導入

解決策としてredisをセッションキーストアとして利用する、ということのようです。今更だけどSocket.ioについてまとめてみる

redisのPub/Subを利用して、各プロセス間でセッション共有をするということです。

このサイトの記事のとおりなのですが、ひと通りメモ

redisのインストールと起動

$ brew install redis
$ redis-server /usr/local/etc/redis.conf &
[1] 3234
$ [3234] 31 Jan 11:29:03.112 * Max number of open files set to 10032
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 2.8.1 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in stand alone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 3234
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

[3234] 31 Jan 11:29:03.113 # Server started, Redis version 2.8.1
[3234] 31 Jan 11:29:03.113 * The server is now ready to accept connections on port 6379

redisのpub/sub機能

redisにはpublisher/subscriberという機能があって、いずれかのプロセスが受けた通知をredisに設定することで、他のプロセスに通知を行い共有できるということです。この機能を使うことでクラスタリングされたプロセス間でセッションの共有を行えます。

サーバアプリの書き換え

app.jsを以下のように書き換えます

var app = express()
    , server = http.createServer(app)
    , redis = require('socket.io/lib/stores/redis')  // socket.ioにredisがある
    , redisConf = { host: '127.0.0.1', port: 6379 }
    , io = require('socket.io').listen(server);

// storeタイプをredisに変更
io.set('store', new redis({
    redisPub: redisConf,
    redisSub: redisConf,
    redisClient: redisConf
}));

pm2でクラスタリング

再びpm2でクラスタリングを試してみます。

$ pm2 start app.js -i max

この時のアクセスログを見ると警告が無くなり、かつ正常にクラスタリングされているようです\(^o^)/

f:id:tomo_watanabe:20140131120626p:plain

モバイル版ChromeのSPDYプロキシはIPv6に対応

モバイル版のChrome正式にSPDYに対応しました。

たまたま実験してて気づいてたのですが、SPDYプロキシはIPv6に対応しているようです。

実験

グローバルなIPv6が振られているMac上でサーバアプリを起動して、IPv6でアクセスできるようにします。今回はNode.jsを使ったサーバアプリを起動しておきます。また、アクセスしてきたIPアドレスをログで表示できるようにしておきます。

IPv6でアクセスできるようにするには、Node.jsでlistenを下記のように書いておきます。

server.listen(3000, '::');

PC版Chromeでアクセス

IPv6指定でアクセスすると、このように接続されます。これはまあ普通です。

f:id:tomo_watanabe:20140120125629p:plain

Node.jsのログでIPアドレスを確認してみます。同じマシンからアクセスしてるので同じIPv6アドレスです。

f:id:tomo_watanabe:20140120130136p:plain

AndroidChromeでアクセス

AndroidChromeでSPDYをON(帯域幅の管理→データ使用量を節約)でアクセスしてみます。

f:id:tomo_watanabe:20140120130720p:plain

同じようにアクセスできました。この時のアクセスログを見てみると

f:id:tomo_watanabe:20140120130845p:plain

謎のIPv6アドレスからアクセスが来ていますね。これを調べてみます。

f:id:tomo_watanabe:20140120162437p:plain

どう見てもGoogleさんです。本当にry

ちなみにSPDYをOFF(データ使用量を節約しない)に設定すると

f:id:tomo_watanabe:20140120131721p:plain

このようにアドレス解決できません(今まではこうだった)

追試

IPv6アクセスの確認サイトである「The KAME project」にアクセスしてみます。 このサイトのIPv6アドレスは「2001:200:dff:fff1:216:3eff:feb1:44d7」です。

モバイル版ChromeでSPDY ONで「http://www.kame.net/」へアクセス(IPv4)と「http://[2001:200:dff:fff1:216:3eff:feb1:44d7]」(IPv6)へそれぞれアクセスしてみてください

亀が泳いでいればIPv6でアクセスしていることになります。IPv6アドレスでアクセスした場合には亀が泳いでいるのが確認できると思います。

f:id:tomo_watanabe:20140120133300p:plain

まとめ

Android端末で僕が契約しているdocomo XiのLTEでは端末にはIPv6は割り当てられず、IPv4アドレスしか振られません。なので普通にURLを入れるとIPv4でのアクセスになります。しかしIPv6アドレスを直打ちすることで、SPDYプロキシがIPv6へのアクセスを可能にしていることがわかりました。

ただし、PCからのアクセスだとWebScoketで接続できますが、SPDY経由だとxhr-pollingになるのでWebSocketのプロキシには対応していないようです...

追記:httpsアクセスはSPDYプロキシは通ってないようですね(まぁそうか)