pixiv insideは移転しました! ≫ http://inside.pixiv.blog/

「超大量絵馬」の裏側

f:id:edvakf:20170127130523p:plain

こんにちは、@harukasanです。 普段はpixivのインフラチームでpixivのインフラや画像配信インフラを担当しています。 今回は社内でおもしろそうな話をしてたので聞いていたところ、1,000万ユーザー記念企画をやることになっていました。 さて、今日は「超大量絵馬」企画がどのように動いているかをさくっと解説したいと思います。

「超大量絵馬」の主な機能

今回の「超大量絵馬」は3人のエンジニアがフルコミットで開発しました。 プロトタイピングが始まったのは1月の中旬くらいでしたが、 前回の記事にあったとおり、絵馬の形が決まってから本体の開発期間は2週間少ししかありませんでした。 開発しないといけないことはたくさんありましたが、次の3つがメイン機能です。

  • 隣の人が描いているのがわかるリアルタイムお絵かき
  • サーバで生成したサムネイル画像をスクロールして閲覧できるビューア
  • 誰が描き始めたかわかる通知機能

これらを実現するために、今回は通常のHTTP APIに加えてSocket.ioによる通信を採用しました。 Socket.ioはWebsocketやAJAX long polling等複数のプロトコルを用いて様々な環境でリアルタイム通信を実現するライブラリです。 絵馬やユーザ情報、画像はHTTPで配信し、描いたストローク情報、 通知等はSocket.ioを使って配信することで、リアルタイム配信を実現しています。

例えば、お絵かきをするときの通信は次の様になっています。

f:id:edvakf:20170127130540p:plain

青い線がSocket.ioによる通信です。ユーザーがストロークを描くと、 元に戻せなくなったタイミングでサーバにストローク情報が送信されます。 送信されたストローク情報は、隣り合った絵馬を描いているユーザと、 詳細画面を開いているユーザには、そのまま配信され描画されます。

サーバでは送られてきたストローク情報を元に、ブラウザと全く同じ画像を生成し保存しています。 この画像を表示することで、ストロークを毎回描画せず、高速な表示を実現しています。

Client/Server-side Javascript on AWS

f:id:edvakf:20170127130558p:plain

さて、この仕組みを2週間で実現するために、「超大量絵馬」はすべてJavaScriptで開発しました。 サーバ側にはサーバサイドJavaScriptの代名詞となっているNode.jsを使用しています。

Node.jsを用いた理由は次の通りです。

  • クライアントサイドとサーバサイドでコードを共有できる
  • イベントドリブンでリアルタイム通信に向いている
  • 新規プロジェクトでメンテナンスの必要もないので新しいことをいろいろできる
  • @geta6が好きだった

クライアントサイドとサーバサイドでコードを共有できることは今回のプロジェクトでかなり役立ちました。 例えば、クライアント、サーバそれぞれでストローク情報から画像を生成する必要がありますが、 サーバではNode canvasを用いることでクライアントと全く同じコードで描画を実現しています。 また、絵馬は5角形の複雑な形をしているため、座標計算が非常にややこしいですが、 この部分も共通化することができました。

アプリケーション構成

アプリケーションの構成は次のようになっています。

f:id:edvakf:20170127130616p:plain

インフラにはAmazon AWSを用いています。 以前インタビュー記事で、Amazon EC2を用いると非常にコストがかかると説明したことがありますが、 今回は期間が1週間と短いため、クラウドサービスを利用することでスピーディな展開が可能でした。 このようにpixivでは用途に合わせて複数のインフラを使い分けて運用しています。

フロントにはnginxを使いました。すこし難がありますが、WebSocketもプロキシすることができます。 静的ファイルや画像はnginxが直接配信するようにしています。

データストアにはRedisを使用しています。 今回はセッション、ジョブキュー、Pub/Subのすべて1つのRedisサーバで実現しました。 画像の生成は時間ががかかるため、Redisに一旦キューイングし、非同期に実行しています。

絵馬画像を貼り合わせてタイル画像を生成する

サーバでは2種類の画像を生成しています。 1つはユーザが描いたものと全くおなじ絵馬画像、そしてもう1つは絵馬を敷き詰めて並べたタイル画像です。 このタイル画像には複数の種類があります。

f:id:edvakf:20170127130630p:plain

zはズーム率を示しており、z=1がオリジナルサイズです、z=2だとオリジナルの1/2サイズになります。 これらの画像を並べることで各ズームでのスクロールを実現しています。

確保するメモリを少なくするため、前回生成したタイル画像を読み込み、 更新があった絵馬の部分だけを置き換える実装にしました。

4つの座標系

「超大量絵馬」のスクロール画面では4つの座標系を変換して扱っています。

f:id:edvakf:20170127130644p:plain

  • グローバル座標系
  • タイル座標系
  • 絵馬座標系
  • ウインドウ座標系

1つめはグローバル座標系。 世界の中心を原点として右方向にx、下方向にyになっています。 2つめはウインドウ座標系で、ウインドウ中心を原点としているもので、 スクロールに合わせて原点を動かしてレンダリングします。

3つめはタイル座標系で、単位ベクトルの長さがタイルのサイズになっている座標系です。4つめは絵馬座標系で、絵馬毎のidになっています。絵馬のURIにも使っているものです。この4つの座標を変換しながらレンダリングを行っています。

まとめ

2週間という非常に短い開発期間でしたが、日頃のプロジェクトに存在する様々な障壁がないため、 新しい技術をふんだんに取り入れることができました。普段さわらない技術を使うことができてとても楽しかったです。

次回の記事では、技術的に詳しい内容にも触れる予定です。 それでは、3月2日(日)まであと少しとなりましたが、「超大量絵馬」をお楽しみください。