緊急地震速報 by Extensionを作ったときの感謝をこめて
「緊急地震速報 by Extension」を作って経験したことなどをまとめました。今回は後編です。前編は会社のブログに書きました。
僕は生産設備を持っていませんので食料をはじめとした物流における支援は難しいのかもしれません。しかしながらシステムエンジニアとして間接的な支援や情報における支援はできます。僕は、天災における「破壊」を修復するのは人々の「生産」と考えるとともに、その「生産」の一部分を担う者として頑張っていきたいと思います。
東北地方太平洋沖地震の後、余震が頻発し緊急地震速報の警告音も頻繁に耳にするようになりました。警告音に焦らされる一方で、本当は落ち着いて揺れに対処しなければならないと思っていました。そこで緊迫感を与えないような緊急地震速報を出せないものかと今回の制作にいたりました。(なので初期バージョンには音がなかったのです)
また、本当は「緊急地震速報 by Chrome Extension」という名前で登録しようとしたのですが「Chrome」というワードは使用ができないので「緊急地震速報 by Extension」という名前になりました。
■プロキシ経由での接続
大きな会社などではプロキシ経由でインターネットに接続するようになっていて配信サーバと接続ができないようでした。
変更前
ポート12001番を使用していました。
var hosts = [ '123.123.123.123:12001' ];
変更後
ポート443番を使用するように変更しました。
var hosts = [ '123.123.123.123:443' ];
HTTPSで使用するポートを用いることにより一部の方の接続が可能になりました。
さらに変更後
ポート443番、80、8080番、12001番を使用するように変更しました。
var hosts = [ '123.123.123.123:443', '123.123.123.123:80', '123.123.123.123:8080', '123.123.123.123:12001' ];
これによって判明したのは1/3〜1/4の利用者の方は443にしか接続できなかった事です。
参考
■マルチバイト文字列のbroadcast
今回、Websocketサーバからbroadcastする際、node-websocket-serverを使用したのですが日本語を正しく送信できませんでした。
解決策
wsServer.broadcast('\\u3092');
こうすることでクライアント側には\u3092という文字列が送信されるようになります。
ちょっとライブラリの中をのぞいてみます。
server.js
broadcastメソッドです。
this.broadcast = function(data) { manager.forEach(function(client) { clientWrite(client, data); }); };
上述のとおりforEachで回してます。clientWriteメソッドも確認します。
function clientWrite(client, data) { if (client && client._state === 4) { client.write(data, 'utf8'); } }
utf8としていされてwriteされています。
manager.js
Manager.prototype.forEach = function(callback, thisArg) { var current = this._head; while (current !== null) { callback.call(thisArg, current.connection); current = current._next; } };
this._headは連結リストの先頭の要素のようです。
this._head = client;
clientオブジェクトが入っているようです。
var client = { id: connection.id, _next: null, connection: connection };
接続IDと接続、次の要素を保持しています。
connection.js
function write(connection, data) { debug(connection.id, 'write: ', (new Buffer(data)).inspect()); if (connection._socket.writable) { return connection._socket.write(data, 'binary'); } return false; }
binaryと指定されていますね。完全に読み切るには少々時間がかかるので適宜更新します。
参考
■正規表現
情報はtwitterのUser Streams API経由で取得していたのですが、ご利用者の方から震度やマグニチュードだけを簡略化して欲しいとの要望がありました。そこで正規表現を使用し文字列を整形して表示することにしました。ところが一部のご利用者には元の文章がそのまま表示されていました。
解決策
以下のようにして日本語をマッチさせるときに16進表現を使用することで解決しました。
var reg = new RegExp('\u9707\u5EA6([3-9]\u5F37*\u5F31*)', 'ig'); text.match(reg); var scale = RegExp.$1; // filter : simplify if(localStorage["simplify"] !== "false"){ var reg = new RegExp('([0-9]*/[0-9]*/[0-9]*)', 'ig'); text.match(reg); var date = RegExp.$1; var reg = new RegExp('([0-9]*\:[0-9]*)', 'ig'); text.match(reg); var time = RegExp.$1; var reg = new RegExp('\u30DE\u30B0\u30CB\u30C1\u30E5\u30FC\u30C9([0-9\.]*)', 'ig'); text.match(reg); var magnitude = RegExp.$1; var reg = new RegExp('\u3001(.*?)\u306E', 'ig'); text.match(reg); var place = RegExp.$1; if(date != '' && time != '' && place != '' && scale != '' && magnitude != ''){ text = date + " " + time + " " + place + "\n\u9707\u5EA6" + scale + "\n\u30DE\u30B0\u30CB\u30C1\u30E5\u30FC\u30C9" + magnitude; } } // filter : scale switch(localStorage["scale"]){ case "7": if(parseInt(scale, 10) < 7){return false;} break; case "6": if(parseInt(scale, 10) < 6){return false;} break; case "5": if(parseInt(scale, 10) < 5){return false;} break; case "4": if(parseInt(scale, 10) < 4){return false;} break; }
ブラウザ側の言語設定は各々異なります。通信時の文字コードを指定しても解決するかもしれません。
■twitterとの接続
node.jsを使い、以下のようなコードでtwitterと接続してます。
var client = http.createClient( 80, "stream.twitter.com", false, false, { "username" : "hogehoge", "password" : "fugafuga" } ); var request = client.request( "GET", "/1/statuses/filter.json?follow=123456789", { "host" : "stream.twitter.com" } ); request.end(); request.on( "response", function(response){ //sys.puts("response"); var chunk = ''; response.on( "data", function(data){ // データ受信処理 } ); response.on( 'end', function(){ // twitterから接続を切られた時 } ); } );
twitter切断時
実はたまに接続を切られます。切断される可能性を考慮し以下のようなコードを使用してます。
var getRequest = function(){ var request = client.request( "GET", "/1/statuses/filter.json?follow=123456789", { "host" : "stream.twitter.com" } ); request.end(); request.on( "response", function(response){ response.on( "data", function(data){ // データ受信処理 } ); response.on( 'end', function(){ // twitterから接続を切られた時 r = getRequest();// 再接続 } ); } ); return request; } var r = getRequest();
通信は必ず切れるものと考えてコードを書かなくてはいけませんね。さらに実は以下のようなスクリプトも実行してます。
request.on( "response", function(response){ //sys.puts("response"); var chunk = ''; response.on( "data", function(data){ // データ受信処理 } ); response.on( 'end', function(){ // twitterから接続を切られた時 throw new Error('twitter disconnected me!!'); } ); } );
node.jsにおいてcatchできなかったErrorはプロセスを終了させます。従って以下のようなシェルスクリプトと組み合わせてプロセスが終了してないか定期的にチェックし、終了していた場合は再起動することで接続を維持しています。
#!/bin/sh while true do isAlive=`ps -ef | grep "my-websocket-server.js" | grep -v grep | wc -l` if [ $isAlive -ge 1 ]; then echo "process is alive" else node my-websocket-server.js.js echo "process is dead" fi sleep 3 done
■アップデート
拡張機能のアップデートは自動で行われますが、新バージョンを登録してすぐに全ユーザのアップデートが完了するわけではありません。意外にも早く直後にアップデートされる方もいれば、時間がかかる方もいます。1時間で10%程度の方がアップデートされるようです。一方、90%のユーザはアップデート前のクライアントでサーバに接続しますので考慮して更新しなければなりません。
■起動
ちなみにですが以下のコマンドでWebSocketサーバを起動してます。
nohup ./check.sh > log.txt &
接続数を調べるには以下のようなコマンドを使ってます。
lsof -i:12345 | grep "node" | wc -l
■twitterからの文字列
システムの設計の問題にもなりますが、twitterからの文字列が正しくパースできないようなケースが稀にあります。入力は受け手側が意図する形式になるとは限らないということです。
- 文字列をパースできるように条件分岐する
- 但し、ブラックリスト方式に近いので未知の正しくない文字列はパース出来ない
- データストアを作って保存に対してbroadcastのトリガを引かせる
- 全てのサーバが正しくパースできなかった事はないので非常に有用だが、失敗する確率はわずかながらある
■サーバ負荷
node.jsの素晴らしさを実感する毎日ですが、接続数が1万数千を超えるとさすがに0.5〜1秒程度遅延します。早く多くの方に届けるべき情報ではあるものの、一方で多くの方が接続すると遅延が生じやすくなります。サーバは個人で賄っているので限度があります。非常に難しい問題です。財閥の末裔だったらデータセンターごと買うのになと思う毎日です。
■まとめ
僕は水や電気の節約には賛成です。しかしながら根拠が不明瞭な自粛ムードには反対です。冒頭で述べました通り、破壊を修復するのは生産であり、手を止めて遠慮する事では無いと思います。何もできることがないと考えた結論の自粛より、いつも通り働き、学び、生活する方が生産的だと僕は考えます。まだまだ対応できていない事ばかりで完璧ではありませんが今後とも宜しくお願いいたします。早く余震が収まるといいですね。(●´⌓`●)
拡張機能への要望
今まで通り@mi_eqbotまでお願いいたします。自分のブログよりも確実にチェックしてます。w
その他開発などへの興味
@mitsuaki_iまでお願いいたします。
TrackBack URL :
Comments (8)
Mac(OSX10.6.8)とWindows(Vista)の両方で利用させてもらってます。
Macで使用している場合、Chromeを終了すると拡張機能も停止してしまうようです。Windowsの場合、終了後も通知領域にChromeアイコンが表示されてるみたいで、拡張機能はONになってるようです。
ブラウザの仕様のためのようですが、Macを主に使っている私には少し残念です。
とても役に立っています。ありがとう。
緊急地震速報 by Extension 0.5.12ですが、Mac Mountain lionChrome最新版だと
緊急地震速報表示がすぐに消えてしまうのですが・・
ご連絡ありがとうございます。
Canary Buildやdev channelなどでNotificationが不安定な事が過去多々ありましたので、
お手数ですがChromeのバージョンをお聞きしてもよろしいでしょうか?
(Chromeについてのメニューで表示することができます)
どうぞよろしくお願いいたします。
>Chromeのバージョン
22.0.1229.94
です。
うーん、ひょっとしたら、不安定ということではないのかもしれないのですが、
表示された後(表示を続ける設定で)、すぐ消えてしまって、その後はMountain Lionの「通知」に入ってしまうようなのです。
ちょっと今検索してみたところ見つけたのですが
http://applech.info/mac-mountain-lion-notification-center/
(2ch系ページのようですみません)
ここに「Chrome + 地震速報」という記述があります。
「Chromeに緊急地震速報エクステンション入れてると通知センターに表示される」という投稿がありますが、
ちょうどこのことなのです。通知センターに吸収されてしまって、Chrome側は数秒で消えてしまう。
ということなのでした。
もし対応可能であれば、とても助かります。よろしくお願いします。
参考URLまでいただけて助かります!
率直なところChrome側のデフォルトの挙動なので対応が難しいと思います。もう少し調べてみます。ご不便をおかけしてすみませんがどうぞよろしくお願いいたします。
タスクバーを縦に表示する環境で使用すると震度・マグニチュードは表示されるのですが
地図は上部1cmほどしか表示されずほとんど画面の下に隠れてしまいます。
β版の概要の画像のように左に地図・右に震度が表示されていた時は
地図表示の問題はありませんでした。
お忙しいところ申し訳ありませんが、ご確認お願い致します。
使用環境
・Windows 7 Professional SP1 32ビット版
・Chromeバージョン 37.0.2062.124 m
・緊急地震速報 by Extension 0.5.17(β版 0.7.15でも同じ。)
とても役に立っておりますが、サウンド音変更することは出来ないのでしょうか?
緊急地震速報 by Extension 0.5.17
緊急地震速報 β by Extension 0.7.15
Windows 7 Professional SP1 32bit/64bit
Chomeバージョン 46.0.2490.80m