lxyuma BLOG

開発関係のメモ

androidでHTML5 audio使ったHLS形式のストリーミング再生が上手くいかない

スマホのブラウザ上で、音声のstreaming配信がしたい。

そこでhtml5 audioでHLS形式のURL(*.m3u8)指定して再生した時に

動かないandroid端末が一部あった。その対応した話。

結論は、単に、audio.type指定すれば良かったというだけの話だけど、

せっかくなので、スマホのストリーミング配信の話から、まとめる。

目次

flashスマホ

ストリーミング配信というと、PCは未だ、flash使う事が多いと思われる。

試しに、amazonの視聴見てみると、やっぱりflash

スマホflashは、どうかというと、

そこで、HLS使うのが選択肢として出て来る。

HLS

HLSはappleの作ったhttp経由でストリーミングを実現する技術。

IETFで標準化しようとしている。

所謂、擬似的なストリーミング技術で、

progressive downloadに、帯域制御を追加した物になる。

公式sampleの中身を見てみる

例えば、公式のsampleを見てみると、以下のm3u8ファイルをDLしてる。

#EXTM3U

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=232370,CODECS="mp4a.40.2, avc1.4d4015"
gear1/prog_index.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=649879,CODECS="mp4a.40.2, avc1.4d401e"
gear2/prog_index.m3u8

・・・省略・・・

帯域幅(Bandwidth)毎に音声ファイルを切り替えていて、

最後の部分がその音声ファイル(別のm3u8ファイル)なので、それをURLに打ってみる。

例えば、一行目は、https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/prog_index.m3u8

#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:9.97667,    
fileSequence0.ts
#EXTINF:9.97667,    
fileSequence1.ts
#EXTINF:9.97667,    
fileSequence2.ts

・・・省略・・・

約10秒毎に切り替える音声ファイル(tsファイル)を指定している。

なので、これを直接、また、urlに入れてみる。

例えば、一つ目であれば、https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/fileSequence0.ts

このURLをDLすると、ストリーム配信の癖に、実はダウンロード出来てしまう。

また、このコンテンツは、キャッシュ禁止を書かない限りクライアント次第ではキャッシュする事がありえるらしい ※公式doc参照

こんな感じでprogressive downloadは、ファイルがクライアントに残ったり辿れたりする。

これが、問題無いのかは確認しないといけない。

著作権上の問題と、それ以外の契約等。wikipediaに軽く書かれている。

公式doc によると、断片化ファイルを作る時に暗号化する事は出来るらしい。

HLSの対応状況

HLSは、appleの仕様なので、iosは問題無く一通り動くが、androidが端末毎に色々出てきてしまう。

  • HLS対応状況は、ここのページを参考にすると
    • iosは、3.0以上
    • android
      • 3.0でサポートしたが、頻繁にcrashするらしい。
      • 4.x以降で使えるが、特にvideo再生周りで不具合が多いみたい

やっぱり、後でandroidで苦労する事になった。

html5 audio

今回は、最終的にブラウザで再生したかったので、html5のaudioタグを使った。

再生は単純で、単にこのHLS形式のURLを埋め込めば良いだけ。

一応、このaudioタグの対応状況も書いておくと、

という事で、HLS対応とhtml5 audio対応状況から、大体、ios4とandroid4以降で行けるはずだが…

androidの一部の端末(ver4以降も含む)でHLS形式のm3u8ファイルの再生が出来なかった。

一部のandroidでHLS再生出来ない

沢山android端末で再生確認して、確か1,2割くらい再生不可の端末があった気がする。

超ありがたかったのが、ここの個人のデモページ

これを見ていて気付いたが、実は、一部のandroidでは、

ストリームのresponseのcontent-typeが以下の場合、そのままでは再生出来ない

  • application/x-mpegurl
  • application/vnd.apple.mpegurl

これらの、content-typeは、appleの仕様的にも正しいし、

ietf上も、特に後者のcontent-type使えと書いてあるので正しい。(ただ、その記述の前にand/orと曖昧に書かれているのが、ちょっと気になるが)

仕様上正しいのだが、何故か、androidの一部端末は受け付けない。

これは予想だけど、おそらく、google的にそもそもandroid内のブラウザとしては、apple的な仕様で気に入らない所は拒否する仕様にしていて、それに気付いている多くのメーカーがブラウザをカスタマイズして変えていて、逆にこの対応をしていない端末が再生できないのでは?(予想ね)

対応

さて、html5 audioのtypeで単にaudioを指定してあげたら、動いた。

audio.type = "audio/mpeg";

こうすると、殆どの端末で動いた。

判定箇所

同じく、canPlayTypeとか判定している所も、上記x-mpegurlやvnd.apple.mpegurlだと拒否られるので、

audio/mpegでチェックするように変えた。

となると、どこで再生可否判定するんだよ、という話だが、

errorイベントでキャッチする事で、判定するようにした。

(再生前にエラーが起きる)

はまってる人々

やっぱり同じくハマってる人々がいるけど、原因や対応等、ふわふわしてる感じ。

参考

Marionetteからractiveへ

昨日、天下一クライアントサイドJS MV*フレームワーク武道会で話してきた。

以下、そのスライドです。

今日は

自分が現場で使ってきた FWの変遷の軽ーい話をする

元々

  • Marionette.js使ってた
    • 振り返ると、実装が冗長になってしまった
  • 何を言ってるか?というと...

例えば

  • ulとliで、その中の要素追加削除やeventを作りたい時
  • image
<ul>
    <li>1st track click me</li>
    <li>2nd track click me</li>
</ul>
<button>new track</button>

Marionetteだと...

  • 1.html書いて全体像を作って
  • 2.templateに切り刻んで
  • 3.CompositeView作ってテスト書いて
  • 4.ItemView作ってテスト書いて
  • 5.CompositeViewをnewする処理書いて
  • 6.元のhtmlと繋いで、 ようやく実現

うわー

  • やっぱりちょっと大変だった
  • Backbone単体より遥かにマシだけど
  • 実現までのコストかかりすぎ
  • お引っ越しした方が良さそう..

気になってたのは

  • やっぱりangular
  • ただ、独自の仕組みと多すぎる学習コストが受け入れられなかった

そこで

  • 偶々ractive.jsと出会った

Centered image

ractive.js

こんな感じ

<div id="result"> </div>
<script id='myTemplate' type="text/ractive">
<p>こんにちわ{{user}} さん。
  <strong>{{messages.unread}} 件の未読メッセージがあります。</strong>
</p>
</script>
<script type="text/javascript">
var ractive = new Ractive({
  el: "#result", // 出力先の要素
  template: "#myTemplate", // template
  data: { // Reactiveに値を変えたいdata
    user: '太郎',
    messages: { total: 11, unread: 4 }
  }
});
</script>

現場でractiveその1

  • 勿論、既存のMarionette処理があった
  • そのため、当初は、Marionette.Viewのrender(template)内に埋め込んでいた
    • 当時、ractiveの強制描写も効かず、setTimeout 0で、ractive描写のtimingズラして使ってた
    • が、test書き辛くなって、この書き方は途中で辞めた

現場でractiveその2

  • その後、Viewだけ完全にractive.js使うようになった
    • Backbone.ViewやMarionette.View要らなくなった
    • MarionetteのCompositeViewやstickit等も不要になった
  • rails環境だったのでerbに直接template書いた
  • image的に、以下のMVC構成になった
    • Model : Backbone.Model
    • Controller : Marionette.AppRouter + Marionette.Controller
    • View : Ractive.js
    • カオス感が漂っているが..

ractiveでhappy?

  • やっぱり、ReactiveなMVVM FWは生産性が高い
  • 特にractive学習コスト低いので、素晴らしい
  • 殆ど大きくハマる事は無かった

本当にハマる事が無かったのか?

  • 必ずひっかかるのは、以下ぐらい
    • 1.ractive.getできるobjが実はpureなobjじゃないので、若干動作違う
    • 2.observeはoptionにinit=falseしないとinitでも反応する
  • いずれもdoc見ればすぐ解決できる軽微な罠
  • これらで大きくハマる事は無い。

ractiveの良くない所

  • 結局、また、jQueryみたいに命令的な所が増えた

    • view内で使うhelper的な関数はstatic methodっぽくclassyに書けるが、
    • 確かinstanceに対しての挙動は、当時、ゴリゴリcallback書かなきゃいけなかった気がする
  • そして、何より大事な問題が…

ractive使ってるのが

  • 俺しかいなかった
           ~∞  
   (ノ^p^)ノ
   (   )  
 ~ノ  ヽ   wwwwwww

 .

blog書いたけど

  • 中々、流行らなかった。
  • 最近は、本家の動きも少なくなってきた
    • ※追記しておくと、ここ最近は動きが再開してた。一時期、停滞していたという話。
  • SNS見ててもractive言及どんどん少なくなってる

今は?

  • もう、ractive使ってない
    • 振り返ればractive触ってたのも半年前の話
  • project変わり諸条件から、
  • 裸jsで作ってる

裸js

  • BackboneとかMarionetteとかMVVM系FW等
  • 色々知ってから裸js戻ると面白い
  • 色んなパターン当てはめようとオレオレ実装してる
  • scaleしないから良くないけど、
  • FW入れる程の物でもないのでそうしてる

振り返ると

  • ここ1年でjs FW何個踏んできたのだろうか...
  • MVVM周りで落ち着きそうだがRedOcean状態は変わらず
  • vue.jsとても良さそう
    • なんだけど、殆ど日本の情報ばっかなのも気になる
    • facebookのreactが海外記事/snsでよく見る気がする
  • 結局、どこに落ち着くか不明だが、日本で仕事する分にはvue.jsが良さそうですな

まとめ

ractiveは、もういいや...

おしまい


以上。

ちなみに、翌日仕事だったので、途中で帰りました。

ビールごちそうさまでしたー。

IT勉強会の会場探し

もう、長い事、jsCafeというjavascriptの勉強会をやっていて、

最近、お世話になってた会場がオフィスになってしまったので、

改めて、会場を探した。

IT勉強会の会場探しは、結構、面倒くさいし、大変。

他に会場探してる方の役に立つかもしれないので、そのメモ残しておく。

IT勉強会の会場

まとめが幾つかある。

見てみると、当然、IT企業が多く、また、無料の会場も多い。

なお、ちゃんとそれぞれの会場のページも見た方が良い。

今、閉鎖した、とか、移転して今使えないとか色々あった。

通常は、この辺りのどこかで確保できるのだろう。。。

ただ、jsCafeの場合、土日開催で頻度が高く、

企業だと頻繁に土日出社して頂く事になり会場管理者に負担をかけるので、

結局、IT企業の会議室貸し出し系には手を出さなかった。

マナーですな。

貸し会議室

それ以外の選択肢に、貸し会議室という物がある

http://www.kaigishitsu.jp/

ピンキリだが、安くても、30人位で2時間だと、1万は超える。

更にprojecter + screen使うとなると、もう少しかかる。(1万位)

結局、安くても2万前後はかかる。

コワーキングスペース

よくあるのが、コワーキングスペースやカフェっぽい所。

探すと開催は無料の所があるが、参加者に一人500円〜1000円位の

ドリンクオーダー等を課す所が一般的。

まあ、商売だし、当然だけど。

30人でやるとなると、30 × 1000円 = 3万円の会議室借りてるのと一緒だったりする。

公民館

最終的に、jsCafeは、公民館を選んでみた。

公民館も色々あって、殆どが、プロジェクタすら無い机椅子だけのスペースだったり、

ダンス教室?とかが用途の部屋ばかり。

だが、探せば、プロジェクタも使える公民館もある。

部屋もピンキリあり、多い所で、200人以上入る様な場所もびっくりするような値段で(数千円とか)で借りれたりする。

今回は

中央区民館の銀座区民館をとってみた。

プロジェクタも言えば使えて、夜3時間でたった2600円になった。(30名入るらしい)

とりあえず、参加者一人100円位払えば、どうにかなる。

素晴らしい。

ただ、こういう会場は当然、

wifi使えず、また、電源も使える量に限界があるので、IT勉強会向きではないが。

いつか、気が向いたら、どんな感じの会場だったか、記事に残す。

つぶやき

どこかに

  • 無料で使えて、
  • 土日開催出来て、
  • 会場側にも負担かけなくて、
  • 電源がちゃんと使えて
  • wifi整理されてて、
  • 最低でも30名は入る

夢の会議室無いかな…

(心当たりある方は、twitter @lxyuma までこっそり教えて下さい>< )

coffeescriptの使いどころ

最近、普通の現場で、coffeescriptの使いどころが分かってきた。

今日は、そのメモと、最後に設定(また、gulpとkarmaの話)を軽く書いておく。

railsとcoffee

自分は、ずっと、rails環境にいたので、coffeescriptで書いてた。

通常のコードも、テストコードも。勿論。

railsはjs周りもレールが敷かれていてcoffeeである事に特に何も違和感ないし、

メンバーも皆coffee使う前提で話進めれる。

他のrails以外の環境に入ると、

皆に、やっぱりcoffeescript使おうぜ、という空気に持っていきにくい。

宗教上の問題で。。。

coffee

coffeeとても、簡易な記述で素晴らしい。

が、classやthis(@)の扱いで、玉に分かり辛い所が出てきて、

多分、そういう所があまり好まれない。と勝手に思ってる。

でも、最近、どこでも使える良いシチュエーションを見つけた。

現場とcoffee

  • ビルド環境の設定ファイル
  • テスト

この2つで、coffee使うと明らかに幸せになれる。

ビルド環境の設定ファイルやテストの中の「function」にあまり意味は無く、

テストやビルド環境の内部での動きはあまり意識する事は無いので、

記号(->)で書いた方が楽。

また、いずれも、その内容(設定やテストを通して伝える仕様)を伝える事に価値があるので、

coffeeの簡潔な記述にすると、大分、すっきり内容を伝えられる。

coffeeの嫌な所

さっき書いた、classとか、this周りの動き等、coffeeでおそらく嫌がられる所は、

testやビルド環境の設定ファイルでは出てこないので、

(例えば、thisなら、テストで@val入れて、@val取る位しか使わないので、)

そこまで、coffeescript固有の面倒くさい所を意識する事はない。

学習コスト非常に低い。

テストとcoffee

昔、jasmine+coffeeで書いてたが、

それが、jasmine+生jsに変わると、

毎回、function書かなきゃ行けないのが、めっちゃ苦痛だった。

書くのもつらいけど、読むのもつらくなる。

階層が深くなると、括弧やインデントに、もう、イライラしてしまうが、

coffeeでテスト書くと、インデントも浅いし、functionも括弧もなく、

目に優しくなって、テスト内の仕様だけが浮き上がってくるので、

やっぱり、こっちの方が断然良い。

この時、他メンバーのためにも、jsでもテスト書けるようにしておくと、逃げ道があって良いかと。

この辺りの柔軟性もテストだから気軽に出来る。

実践

さて、ここから、実際にcoffee使う編の話。

最近使ってる、gulpとkarmaで書く。

とはいえ、coffeescriptも古いので、利用するやり方は整理されていて、非常に簡単。

設定と言う程の内容では無い。

gulpでcoffee

gulpは最近の3.7以降は、coffeeをデフォルトでサポートしてるらしい。

なので、coffeescriptのnpm入れるだけで書ける。

  • npm install
npm install --save-dev coffee-script
  • gulpfile.coffee
    • こんな感じ
gulp = require('gulp')
pl = require('gulp-load-plugins')()

gulp.task 'scripts', ->
  gulp.src(["app/scripts/main.js", "app/scripts/*.js"])
    .pipe(pl.concat(dist.js))
    .pipe(gulp.dest(dist.dirJs))
    .pipe(pl.connect.reload())

karmaでcoffee

  • npm
npm install karma-coffee-preprocessor --save-dev
  • karma.config.js
    files: [
      'tests/**/*_spec.coffee'
    ],

    preprocessors: {
        "tests/**/*.coffee": ['coffee']
    },

この時、filesの中に、jsのテストパスも入れてしまえば、生jsのテストと共存できる。

altjs

最近は、altjsいっぱい選択肢がある。

その中でも、coffeeであれば、

  • 昔からあるので、見てきた通り環境整理されていて、

  • ruby/pythonみたいに簡潔に書けるので、

    • ビルド環境なら設定内容、テストなら仕様をより分かり易く明示出来る

から、こういうシチュエーションで、coffee改めてオススメです。

テストや設定ファイルがごちゃごちゃしてきて大変になってきた方は、

coffee入れてみてはいかがでしょう?

以上。

最近のjsテスト/ビルド環境

最近のテスト環境

最近、ブログ書かないうちに、また、

自分のjs周りのテストやビルド環境が変わってきた。

具体的には、karmaとかgulpとかに変わった。

今日は、

  • その環境にしてどうだったか?とか、

  • 設定ファイル(gulpfile)とか書いてみる。

去年と今年と

去年、勉強会等でyeomanとかgruntの話をしていて、このblogでもどこかに書いてた。

テストはmocha chaiが良いのかなーと思ってた。これもblog書いた。

所が、世の中どんどん変わっていく。

最近の環境

今はこんな感じ。

  • build tool
    • gulp
  • test
    • jasmine2
    • sinon
    • karma

gulp vs Grunt

gulpどうなんだろう?と思いながら色々試していくうちに、かなりしっくりきた。

stream baseで書き易いので、カスタマイズし易い。

gruntは、設定ファイルが何画面にも渡って長くなると、メンテが怠い。

何がどうなってるのか分からなくなって来る。

gulpは、小分けでタスク分けて依存関係も整理しながらstreamで書くので、非常にsmartで良い。

しかも、色々タスク積んでも並行実行はやっぱり早くて気持ちがよい。

Gruntも今後、色々gulpっぽくなりそうだけど、いつどうなるか、よくわかんない。

とりあえず、今日から始めるならgulpがいい。

yeoman

初め、yeomanとgulp組み合わせて使おうと思ってたけど、

なんか、最近、yeoman要らなくなってきた。

gulpだけでも、scaffoldingっぽい事も作れる。

yeoman色々事前に用意してくれてるんだけど、

実際の現場では、殆どの場合、使えない。(勿論、家で遊ぶなら、それでいいけど)

自分でカスタマイズしていかないといけない。

となるとカスタマイズし易いgulpだけで完結した方が扱い易い。という事でyeomanもう見てない。

jasmine vs mocha

mocha + chaiは、slideでも書いたんだけど、

language chainが気持ち悪くて結局、jasmineに戻った。

※should.jsもあるけど、もうrspecもshouldじゃなくてexpectなんだよな...shouldはobject破壊するので、いつか無理がでる

jasmineは2になって、非同期テストdoneが付いてて、

もう、mochaに大きなメリットが無くなってる。

jasmineは公式pageの1枚だけ調べれば何でも出来るAll in Oneのシンプルさが素晴らしい。

sinon

sinonはまだまだ使ってるなー。

最近は、要件上、htmlが大きく動く事が少ないので、そのままhtmlFixture使ってて、

TestDoubleそんなにしなくなったんだけど、

それでも、FakeTimerとかめっちゃ使う。

。。。これ書いてて思い出したけど、そういえば、

jasmine2にもtimerあったから、sinonのtimerでなくてもいいかも。明日から変えよう...

karma

昔は、それぞれのテスト環境の上にのっかったテストランナーを使ってた。

今、karma使うようになった。

こういう大きなeco systemに乗っかるメリットは、

困った時にググるとほぼ100%同じ事やってる人がいる事。

大体pluginとか助けてくれる。

HtmlFixtureもあるし、browser実行環境、report等、揃ってる。

勿論、他のtoolやtest FW単体でも出来るんだけど、

共通化されて整理されてるのは素晴らしい。

以前mocha固有の設定とかでイライラする事あったんだけど、karmaのおかげで大分助かってる。

gulpの設定等

今から使ってるgulp設定をおいておく

これは、今、componentっぽい物を幾つか作ってるのに使ってる設定で、

それぞれのprojectで使うには色々調整必要だと思う。

けど、なんかの参考迄に。

gulpのコマンド

  • 以下を準備してる
// all(buildして、testして、監視します)
gulp

// build(js/cssをbuildします)
gulp build

// test(jsをtestします)
gulp test

// 単にindex.htmlが見たい
gulp server

// 新たにコンポーネントを追加したい
gulp scaffold --name your_new_component_name

設定

  • gulpfileはこんな感じにしてる
    • $は、var $ = require('gulp-load-plugins')();の事。
gulp.task("hint", function() {
    return gulp.src(paths.scripts)
        .pipe($.jshint())
        .pipe($.jshint.reporter("default"))
});

gulp.task('scripts', ['hint'], function() {
    return gulp.src(paths.scripts)
        .pipe($.uglify())
        .pipe($.concat(dist.script))
        .pipe(gulp.dest(dist.dir))
        .pipe($.connect.reload());
});

gulp.task('styles', function() {
    return gulp.src(paths.styles)
        .pipe($.compass({
            project: path.join(__dirname, 'app/styles')
        }))
        .pipe($.csso())
        .pipe($.concatCss(dist.style))
        .pipe(gulp.dest(dist.dir))
        .pipe($.connect.reload());
})

gulp.task('connect', ['scripts', 'styles'], function() {
    $.connect.server({
        root: './',
        port: server_port,
        livereload: true
    });
});

gulp.task('html', function(){
    return gulp.src(paths.html)
        .pipe($.connect.reload());
});

gulp.task('watch', function(){
    gulp.watch(paths.scripts, ['scripts']);
    gulp.watch(paths.styles, ['styles']);
    gulp.watch(paths.html, ['html']);
    gulp.src(karma_filepaths)
        .pipe($.karma({
            configFile: karma_config,
            action: 'watch'
        }));
});

gulp.task('test', function(){
    return gulp.src(karma_filepaths)
        .pipe($.karma({
            configFile: karma_config,
            action: 'run'
        }))
        .on('error', function(err) {
            throw err;
        });
});

gulp.task('scaffold', function(){
    gulp.src(paths.templates.script)
        .pipe($.rename(argv.name + ".js"))
        .pipe($.template({name: argv.name}))
        .pipe(gulp.dest('app/scripts/'));
    gulp.src(paths.templates.style)
        .pipe($.rename(argv.name + ".scss"))
        .pipe($.template({name: argv.name}))
        .pipe(gulp.dest('app/styles/sass/'));
    gulp.src(paths.templates.test)
        .pipe($.rename(argv.name + "_spec.js"))
        .pipe($.template({name: argv.name}))
        .pipe(gulp.dest('tests/'));
    gulp.src(paths.templates.html)
        .pipe($.rename(argv.name + ".html"))
        .pipe($.template({name: argv.name}))
        .pipe(gulp.dest('tests/fixtures/'));
});

gulp.task('server', ['connect'], function(){
    gulp.src('./index.html')
        .pipe($.open('', {url: "http://localhost:" + server_port}));
});


gulp.task('default', ['server', 'watch']);
gulp.task('build', ['scripts', 'styles']);

gulpfileは現在進行形で編集中

  • 今現在もこのgulpfile色々追加したり直したりしながら、使ってる
    • 例えば、今日は、testからdocument生成用のtaskを書いてた。
    • scaffold系は、それ専用のpluginもググるといっぱいあるので、そっち使ってもいいかもしれない。
  • 無駄な所あったらごめんなさいね(concat周りとか)

gulpとjasmine2

まとめ

  • FrontendはRedOceanで次々に変化起きるので、どんどん乗っかって楽するのが良い

1章レイテンシと帯域幅:ハイパフォーマンスブラウザネットワーキング

今、社内でO'Reillyの

「ハイパフォーマンスブラウザネットワーキング」

ハイパフォーマンス ブラウザネットワーキング ―ネットワークアプリケーションのためのパフォーマンス最適化

ハイパフォーマンス ブラウザネットワーキング ―ネットワークアプリケーションのためのパフォーマンス最適化

の勉強会(読書会)をしていて、

1章のレイテンシーをやったので、以下にも貼っておきます。

今この本読んでる方、沢山いらっしゃると思うので、

読書の手助けによければどうぞ。

ハイパフォ読書会

パフォーマンスを左右する要素

  • レイテンシー
    • 送信元がパケットを送信してから宛先が受信するまでの時間
  • 帯域幅
    • 論理的、物理的通信路における最大スループット
    • どれだけ情報をながせるか?の最大値
    • フレッツ等で、回線速度が100Mbps等話しているのは、要するに帯域幅の事。

レイテンシー構成要素

  • ルータ時間を消費する主要な構成要素

    • 伝搬遅延(propagation delay)
    • 伝送遅延(transmission delay)
    • 処理遅延(processing delay)
    • キューイング遅延(queuing delay)
  • 書籍の中の説明文が分かり辛いので

  • 別サイト参考に言い換えていくと

伝搬遅延

  • パケット送信時にかかる物理的な遅延
    • 例えば、光ファイバーだと、光の屈折等で遅延する時間の事(後で出て来る)

伝送遅延

処理遅延

  • パケットのヘッダー処理
    • パケットの次の方向を決定する間に、ルータが行うビットレベルのエラー検知
    • これらによっての遅れの話。

キューイング遅延

  • 帯域幅以上のパケットが到達した場合、ルータは、超過したパケットをバッファに溜め込む。
    • このパケットをバッファに保持する時間がキューイング遅延。
    • 輻輳(ふくそう)遅延も同じ話らしい(次の章で出て来る)

伝搬遅延

  • 光は毎秒29万km(真空中の早さ)
    • 光を運搬する素材により遅くなる
    • 屈折率が大きい程光も遅く伝わる
  • 光ファイバの屈折率(1.4~1.6)による遅延も含めて概算すると
    • 毎秒約20万km(元と比べると10万kmも違うんですね)

東京版伝搬時間の表

  • 書籍に有る様なアメリカ視点の数値見ても面白くないので
  • レイテンシを東京を中心にクラウド/VPS等と計算してみる
    • AWS(PaaS等も) :アメリカ西海岸 (※勿論、東京リージョン他もありますが、あくまで1例です)
    • digital ocean(最近おすすめのVPSです。news記事参照) :最近追加されたシンガポールで見てみる
ルート 距離 真空中の伝搬時間 光ファイバの伝搬時間 光ファイバでの往復時間
東京-サンフランシスコ 8280km 28 ms 41ms 83ms
東京-シンガポール 5350km 18ms 27ms 54ms

東京版伝搬時間

  • 東京-SFはNY-SF距離4000kmの2倍。NY-Sydneyが1.5万kmなので、その半分。
    • 意外と短い(勿論、直線距離なので、実際はもっと複雑で距離かかるが)
  • RTT:往復時間(Round Trip Time)
    • NY-Sydneyが1.5万kmで160msと非常に遅い(北半球と南半球またぐと距離かかりますね)

人間の遅れの知覚

  • 100-200msの遅延:遅れを知覚
  • 300msの遅延:反応が鈍いと報告
  • 1000ms超える:ユーザーの意識が切り替わる。
  • この数値とさっきのNY-Sydney(160ms)と比べると、いかに伝搬時間大きいか分かる。

ラストマイル問題

  • 大幅なレイテンシ:大抵、最後の数マイル

traceroute

  • 経路するルータを見るならtraceroute
    • windowsの方はtracert
    • mac は 勿論、黒い画面からtracerouteいけるが、実はGUIもある(アプリケーション=>その他=>ネットワークユーティリティ)
  • 参考

googleにtraceroute

$ traceroute google.com
traceroute: Warning: google.com has multiple addresses; using 74.125.235.101
traceroute to google.com (74.125.235.101), 64 hops max, 52 byte packets
 1  xx.xx.xx.xx ( xx.xx.xx.xx)  1.416 ms  1.073 ms  1.470 ms
 2  xx.xx.xx.xx ( xx.xx.xx.xx)   1.991 ms  2.660 ms  1.772 ms
 3  xx.xx.xx.xx ( xx.xx.xx.xx)   2.167 ms  1.334 ms  1.391 ms
 4  xx.xx.xx.xx ( xx.xx.xx.xx)   1.726 ms  1.682 ms  1.927 ms
・・・省略・・・
11  209.85.251.39 (209.85.251.39)  2.467 ms
    nrt19s02-in-f5.1e100.net (74.125.235.101)  2.543 ms
    209.85.251.39 (209.85.251.39)  2.665 ms

tracerouteの見方

$ traceroute to google.com (74.125.235.101), 64 hops max, 52 byte packets
 1  12.34.56.789 (12.34.56.789)  1.416 ms  1.073 ms  1.470 ms

// 順序番号(TTL) IPアドレス 1回目のTTL=1パケット往復時間 2回目 3回目
  • ホップ:ルータがサブネット間を超える度に1カウント
  • ここでは、最大64回迄ルータ移動する
  • tracerouteはTTLを使ってる

TTL

  • Time To Live:パケットの生存期間
    • パケットが永遠に徘徊しないように、生き延びられる回数を指定する安全装置の仕組み
    • 期間は、ホップ数で指定
    • パケット送出時に、必ずTTLを設定(MAX=255)
    • ルータ1つ経由毎にTTL1減算。
    • TTL=1になった(ここを1引くとTTL0になる)時に、ICMP(echoで使われるプロトコル) Time Exceedエラーを送出
    • 参考

traceroute 原理

  • tracerouteは、TTL=1から初めて、エラー返されたらIPや時間計測して、1ずつ増やして繰り返す
  • ここの記事の図が分かりやすい
    • TTL = 1から始まり、1台目のルータにすぐに破棄されたら、
    • 次はTTL = 2にして、2台目のルータに破棄され、
    • 次はTTL = 3で、、、というか形で試して行く

tracerouteのエラー

各ユーザーの帯域幅

  • Userが使用できる帯域幅はクライアントと宛先サーバー間の一番小さい帯域幅に依存する
    • akamaiの2013年の平均帯域幅でも、日本は11.7Mbps
    • 昔自分も使ってたYahoo BBのADSLが8MBで、それより少し大きい位。当たり前だけどブロードバンド普及してる。
  • speedtestで、近くのサーバー(多分、東京)への上がり下りスピードを計測できる
    • 家のブロードバンド環境で試してみて、プロバイダーの言ってた値と比較すると面白いかも

余談

次は

TCP/UDPもやる予定です。

土日にまとめよう。

gulp vs Grunt

概要

gulpの入門記事を以下に書いて行きます。

  • 目次
    • gulpとは
    • gulp sample
    • gulp支える技術(Streamと並行性)
    • grunt vs gulp どっち使う?

gulp.jsとは

  • いわゆるフロントエンド周りの
    • task runner
    • build tool

Gruntあるでしょ

  • その通り
    • いわば、Gruntの簡易版
  • 去年位?出来た後発のtool
  • 最近、blog記事も増えてきた

公式Page売り文句

  • 複雑なtaskも管理し易く、simpleで、使うのが簡単
  • 一時fileのdisk書き込みが無いので早くて能率的
  • simpleで期待通り動くようにPluginのGuidelineがある
  • APIが小さく学習時間かからない

リソース

使い方の流れ

  • Gruntと一緒
  • pluginを追加
    • npm install gulp-xxx --save-dev
  • gulpfileの更新
    • gulp.task('task-name', function(){ /* settings */ });

gulpfileとは

  • 設定file(Gruntfileと一緒)
// 公式pageより
// requireは省略。
gulp.task('scripts', function() {
  return gulp.src(paths.scripts)
    .pipe(coffee())
    .pipe(uglify())
    .pipe(concat('all.min.js'))
    .pipe(gulp.dest('build/js')); });

gulp.task('watch', function() {
  gulp.watch(paths.scripts, ['scripts']); });

gulp.task('default', ['scripts', 'watch']);

install

npm install -g gulp
$ gulp -v
[gulp] CLI version 3.6.2

やってみる

  • 今から、jsファイルをミニファイする簡単なgulp project作ってみる

project作成

  • まずは、地盤固めから
mkdir my_first_gulp
cd my_first_gulp
npm init
npm install --save-dev gulp

plugin追加

  • minifyするplugin
npm install gulp-uglify --save-dev

設定ファイル

  • gulpfile.jsを作成
var gulp = require('gulp');
var uglify = require('gulp-uglify');

gulp.task("uglify", function() {
    return gulp.src('js/*js')
        .pipe(uglify())
        .pipe(gulp.dest("build/js"));
});

gulp.task("watch", function() {
    return gulp.watch('js/*js', ['uglify']);
});

gulp.task('default', ['uglify', 'watch']);

実験

  • 試しにBackbone突っ込む
mkdir js
cd js
curl -O http://backbonejs.org/backbone.js
cd ..

run

  • 実行
    • build/js/backbone.jsがminifyした?
gulp

振り返ってみる

  • 今作ったsampleを一つずつ振り返ってみる
  • 関連する技術をピックアップしながら進める
    • streamや並列性の話

library呼び出し

var gulp = require('gulp');
var uglify = require('gulp-uglify');
  • gulp本体とplugin呼び出してる

task

gulp.task("uglify", function() {
    return // ...any action
});
  • これで、taskを定義している
  • この後、以下を黒い画面で実行できる
    • gulp uglify

taskの中身

    return gulp.src('js/*js')
        .pipe(uglify())
        .pipe(gulp.dest("build/js"));
  • src => pipe => destという流れ
    • gulp = node.jsのstreamを使ってる(後述)
  • pipeの中で、uglify()等、先ほど呼び出した関数を実行

streamとは

例示

  • ※stream handbookより
  • 今から以下の2つを比較して、何故stream要るか見てみる
    • text読んで渡す処理を
    • 通常とstream使用で比較してみる

通常で書くと

var http = require('http');
var fs = require('fs');
var server = http.createServer(function (req, res) {
    fs.readFile(__dirname + '/data.txt', function (err, data) {
        res.end(data); }); });
server.listen(8000);
  • 問題点
    • 扱いづらい
    • memoryにfile内容をbufferしてる
      • もし、data.txt馬鹿でかいと、多くのUserがメモリを食い散らかす

Stream使うと

var http = require('http');
var fs = require('fs');
var server = http.createServer(function (req, res) {
    var stream = fs.createReadStream(__dirname + '/data.txt');
    stream.pipe(res);
});
server.listen(8000);
  • happy
    • 奇麗
    • diskからdata受け取るときに、data破片毎にclientに書き込める
      • さっきの、でかいバッファ分の利用がなくなった
  • このstreamの運搬してるのがpipe
  • ここまでstream handbookでしたー

gulpのpipe

    return gulp.src('js/*js')
        .pipe(uglify())
        .pipe(gulp.dest("build/js"));
  • gulp.src : file のstreamを作ってる
  • pipe : streamを流す
  • dest : streamを保存

gulp.srcについて

  • 第2引数がoptions objでbuffer有無を設定出来る
    • falseにすると、streamとしてfile.contentsを渡せるが
    • 実はデフォルトはtrue
      • File bufferも一応残してる
      • pluginがstream対応していない事があるので、そうしてるらしい

sample

gulp.task('default', ['uglify', 'watch']);
  • default taskを指定
    • これで、黒い画面でgulpだけで打つと、uglifyとwatchタスクを実行する
    • ※ちなみに、以前あったgulp.runは今使えないっぽいので注意

並列化について

gulp.task('default', ['uglify', 'watch']);
  • 基本的に、自動的に並列化(並行化※後述)される
    • 例だと、uglifyとwatchは順序は保証されない

並列化の仕組み

  • js = single thread
    • あれ?並列化?
    • どうやってるんだろう?

言葉の整理から

  • 1task以上を同時に実行する言葉は2つある
    • parrallel(並列)
    • concurrent(並行)
  • 違いは、後者の並行が、
    • 本当に並列である場合もあれば、
    • 場合によっては時間毎に切り替える等の仮想的な並列である場合もある
  • 参考

gulpは?

  • gulpは、concurrency(並行)
  • orchestratorを使ってるらしい
    • orchestratorは、出来る範囲の並行性の中でタスクや依存性の順序並べや実行をしていくmodule

Orchestratorのconcurrency

  • ソースを追うと、どうやら、
    • 単に全task実行して
    • それぞれ終了時のcallbackを指定していってるだけっぽい
    • IO部分は確かに並列だが、それ以外はいつも通り
    • (実際は、後述の直列の要素もあるのでもう少し複雑だが)
  • "In node, everything runs in parallel, except your code" っていうヤツでしょうか。

gulpのTask実行順を指定

  • gulpの話に戻ると
  • taskを並列(並行)でなく直列にするには、以下、2つの条件が必要
    • 1)依存してるtaskは非同期
    • 2)taskの依存関係を指定

1)依存してるtaskは非同期

  • 以下、いずれかの方法
    • streamを返す事(src->pipe->destまでは非同期で直列)
    • callbackを呼び出す事(mochaのdoneと同じ)
    • promiseを返す事

2)taskの依存関係を指定

  • 以下のdepsに、依存してるタスク名を書く
  • taskの関数
    • gulp.task(name[, deps], fn)
    • name : taskの名前
    • deps:依存関係のあるタスク
    • fn:その後実行する関数
gulp.task('default', ['uglify', 'watch'], function(){
    // uglifyとwatch終わった後に実行される
});

複雑な順番

gulp.task('a');
gulp.task('b', ['a']);
gulp.task('c', ['a']);
gulp.task('d', ['b', 'c']);

以上

gulp vs Grunt

  • これも、どっちが生き残るのかな?

pluginの数は?

  • Gruntが勿論、多い
    • 2899個
  • gulpは比較すると少ないが、数は増えてる
    • 706個
    • 普通に使う分には困らなさそう
  • 数値はいずれも、公式ページから(2014/5/25現在)

Gruntのこれから

  • 視点を変えてGruntの方を見てみる
  • ver0.5で、並列実行が付く?(Roldmap
    • grunt.registerTask('name', ['jshint', 'concat'], { parallel:true });
  • ver1.0で、先程出てきたOrchestrator対応する?(grunt 1.0 alfa

Gruntさん

  • 大分gulpを意識してる?
  • 並列性(並行性?)が入ってきそう
  • 書き方も変わりそう

Gruntでgulp like

  • gulp本家がsrcからdestまでのstream使う仕組みを外出し
  • これでGruntでもgulp likeに書ける

まとめ

  • 今日からビルドツール/タスクランナー使い始めるなら
    • gulpいいよ。楽だし早いし。
  • もうGrunt使ってたり、環境整理の機会がもっと先だったら
    • Gruntの未来に注目。gulpっぽくなるかも。
  • 最終的にどっちが残るの?
    • 分かりません。