generated at
WebWorkerをproductionで使ってる話
shokai @shokai
横浜から京都にリモートワークしてます

右のメニューの Start presentation でスライドになります




最近CosenseWebWorker(DedicatedWorker)を導入した
注:最近流行ってるServiceWorkerではない


WebWorkerについてのスライド等がググってもぜんぜん見つからなかった
WebWorker使ってる人いるのか?
会場にはいなかった

Cosenseでは検索とか推薦とかに使ってる
知見を共有しにきました


WebWorkerとは
ブラウザで使えるマルチスレッドプログラミング環境
UIスレッドをロックしない
別のCPUで実行される
ServiceWorkerとは関係ない
ドキュメントによっては、WebWorker・ServiceWorker・SharedWorkerの3つを合わせてWebWorkersと読んでいる事がある

なぜWebWorkerか
無料でスケールする
clientのCPU数はどんどん増えてる
スマホでも4コア6コア
サーバーのCPUを使いたくない事情
CPUを専有するリクエストがあると、Socket.IOが詰まってしまう
IO・帯域・CPU等のバランス見て、ScrapboxではCPUを特に使いたくなかった

だいたいどのブラウザでも動く


Workerの使い方
UIスレッドで作る
const worker = new Worker(url)
仕事投げる
worker.postMessage(data)
objectそのまま投げれる
結果を受け取る
worker.onmessage(callback)
worker.onerror(callback)

Worker側
仕事受け取る
グローバルに function onmessage (data) を宣言しておく
UIスレッドに返す
グローバルにある postMessage(data) を呼ぶ




Scrapboxの機能
同時編集
他のページへのリンクを書く
双方向リンクになる
ページの下の方に推薦されて色々出てくる
これが結構重かった

つまりちょっと編集する毎にサーバーで推薦の計算が走ってた
キーボード打つ毎にReactが固まる
同時接続数が増えるとサーバーのCPUが専有される

WebWorkerのおかげで
他の人が今作ったばかりの新しいページが、辞書に即反映されるようになった
差分だけやりとりする
辞書作成はWebWorkerでやる


リンク記法の補完と同じデータを使っている

リンク先のリンク先までを推薦対象として
何つながりで推薦されているのか(左端)
被リンク数などからスコア計算してソート
重複避け
上の3つを全部serverでやって、CPUを専有していた(最悪25秒)
他人が同時編集したら関連ページリストもリアルタイムに更新しなければならない
clientの数だけCPU負荷がサーバーにかかっていた
WebWorkerのおかげで
全部clientにやらせて、負荷軽減した
ちょっと大きいJSONやりとりするだけで良くなった


実装
serverは「何を表示するか」を返す
session見てアクセス権限だけ確認し、大きめのJSONをドカッと返す
JSONを小さくする為の重複よけとかはしない
DBにquery投げて結果をそのままclientにスルーしちゃう
「どう表示するか」はclientのUIスレッドとworkerが担当する
WebWorkerで下処理して辞書を作る
Reactのレンダリングのタイミングで最終計算する



やってみてわかった事

巨大JSON、意外とgzipが効く
ページとリンクのリストなので、完全一致する文字列が多い
[ {title: "タイトル1", links: ["リンク先1", "リンク先2", "リンク先3"] }, {title, links}, {title, links} ]
1/4ぐらいになった

new Worker(url) すると毎回GETリクエストが飛ぶ
Worker呼ぶ直前に作ると遅い
ブロッキングしてるかも? #あとで調べる
事前に作ってworkerを使いまわす

Worker複数作る
並行実行したい処理の数だけ new Worker(url) する
それぞれ別のCPUで実行される
new Worker(url)
サーバーが301 not modifyを返せばcacheが使われる
app初期化時に必要な数だけ new Worker(url) しておく

1つのWorkerに複数の機能入れちゃう
Browserifywebpackで1つのjsにまとめる
UIスレッドとコード共有もできる
DOMが無い
scriptタグが書けないので、CDNにライブラリを任せられない
ファイルサイズに注意


Google BotはWebWorker持ってる
このスライドもサーバーサイドレンダリングしてないけど、Googleに表示される



これまで表示が遅くて気づかなかった不具合
キーボード打つ毎に点滅してた
表示が高速化され、高速に点滅するようになった
worker化すると他にも副作用(実体は気づいてなかったbug)が出ると思う


Universal(Isomorphic) Javascript
Server・UIスレッド・Workerのどこでも実行できるように書いておく
src/share/ の下に純粋な関数として実装していく
処理を別の環境にサッと移せるようになる
lambdaに一部分だけ任せるとか
逃げ道があると気が楽になる


Sentryが使える
WebWorkerの中でもUIスレッドと同様に
Raven.config(conf).install() できる
workerのバグ見たら即直す


何度もWebWorkerに仕事させる
postMessagePromiseにする
Promiseの多重実行を防ぐ

おわり