電書部技術班,電子書籍配信サーバーに挑む

第7回 電書フリマにおける配信サーバーの改善

この記事を読むのに必要な時間:およそ 3 分

今回は,文学フリマ版の電書サーバーの補足説明を行った後に,文学フリマにおける電書販売のフィードバックを受けて,電書フリマではどのように電書サーバーを改善したのかを解説します。

文学フリマ版電書サーバーの補足

URLハンドラ

iOSには「ダウンロードしたデータを指定したアプリで起動する」という機能がなかった(バージョン3.2から追加)ため,URLハンドラがよく使われます。例えば,あるURLのPDFをGoodReader(iPad/iPhone用のPDFリーダー)で読み込むためには,リンクを以下のようにします。

GoodReaderのURLハンドラ

ghttp://foo.jp/bar.pdf

httpではなくghttpというプロトコルを使うことで,ダウンロードしたデータがGoodReaderに引き渡されます。EPUBリーダーのStanzaならstanza://です。文学フリマ版電書サーバーが購入者に送信するメールには,通常のhttpの他,ePubならstanza,pdfならghttpのリンクを載せています。

運営コスト

コストをざっと計算すると,フリマの参加費が4,500円。herokuの使用料が約9ドル,s3の使用料は5セントでした。ちなみにherokuで課金されたのは,フリマ当日にダイノ(同時に起動するWebのインスタンス数)を1→2と増やしたのと,メール送信のプラグインSendgridを当日だけproバージョンにした部分のみです。電書部で作成した「電子書籍の出てくる本のレビュー集」である「未来のテキスト」は約160冊(単価100円)販売できたため,サーバー経費は十分にまかなえた計算になります。

電書フリマ構想

この成功を受けて構想されたのが,電書のみを販売する「電書フリマ」です。電書フリマは,ブース一つだった文学フリマとは異なり,渋谷のコラボカフェを借りて電書部が主催するイベントです。実際には,2010年7月17日に開催されました。

フィードバックとして,技術班には以下の要望が寄せられました。

  • タイムアウトの解消/PDF分割の解消
  • 複数の場所でフリマを開催して,売上計算は個別にしたい
  • iPhone対応
  • タグのサポート

電書の数も大幅に増えることになったため,PDF担当はtk_zombieに加えていなちょうinachou)⁠EPUB担当は小嶋に加えて早坂tamisukeと2名の応援を仰ぐことになりました。

タイムアウトの解消/PDF分割の解消

第3回で解説したとおり,文学フリマ版の電書サーバーは以下のような処理の流れでした。

  1. 購入ボタンを押す
  2. 電書マスターをAmazon S3(Amazonのストレージサービス)からダウンロード
  3. 電書にメアドを埋め込む
  4. 再びS3にアップロード
  5. 購入完了メール(ダウンロードURL付き)を送信

1~5の処理がherokuの制限である30秒を超えるとタイムアウトエラーになります。特に電書マスターのPDFが大きすぎると処理が間に合わないため,サイズの大きなPDFは分割して対応していました。

電書フリマ版の電書サーバーでは,この問題を解決するために処理フローを以下のように変更しました。

  1. 購入ボタンを押す
  2. 電書生成処理をバックグラウンドにセット
  3. ありがとうメールを送信(ここで販売は完了)
  4. バックグラウンドで電書生成(マスターDL,メアド埋め込み,S3アップロード)
  5. 完了メール(ダウンロードURL付き)を送信

Delayed_job

電書生成をバックグラウンドで行われることで,販売処理は即座に終了するためタイムアウトはなくなり,かつ電書マスターPDFを分割する必要もなくなります。

rubyにはいくつかバックグラウンド処理用のライブラリがあり,言語自身にもスレッド機能がありますが,herokuではDelayed_job(DJ)を使います。DJで使えるバックグラウンドプロセスの数は,ワーカ(worker)で指定します。デフォルトでは0で,一つ増やす毎に0.05ドル/時間(36ドル/月)課金されます。

DJの特徴は,特定のメソッドだけを非同期動作にしたり,あるクラス全体を非同期動作にしたり,柔軟な設定ができる点です。以下は実際に使っているコードの一部です。handle_asynchronously宣言したメソッドを,通常のrubyのメソッドのように呼び出せば非同期に動作します。

Delayed_jobを使って,非同期なメソッドを定義する

class DenshoMaker
  def make
    begin
      send_registration_email
      make_ebooks
      send_completion_email
    rescue
      send_error_email($!.to_s)
    end
  end
  # 非同期宣言
  handle_asynchronously :make
end

非同期処理をスタート/ストップさせるにはrakeを使います。herokuコマンドを使って,サーバ側のrakeを起動します。

rakeによる非同期処理

heroku rake jobs:work   # 非同期処理開始
heroku rake jobs:clear  # キューに残っている非同期タスクをクリア

複数の場所で開催

電書フリマは,メイン開催は渋谷のコラボカフェそのほかにも「僕たちだけがおもしろい」公開収録現場電書フリマ@京都で開催することになりました。また,渋谷でも周辺のカフェで販売する企画も出ました。そのため,だれが,どの電書を,何冊販売したの集計をとる必要があります。文学フリマ版で認証を導入していたので,これを拡張することにしました。

文学フリマ版の認証

    def authorize(login, password)
      login == "samurai" && password == "sushi"
    end

電書フリマ版の認証

  def authorize(login, password)
    users = %w(id1 id2 id3 id4 id5)
    passwords = %w(pw1 pw2 pw3 pw4 pw5)
    users.each_with_index do |u,i|
      return true if (u == login && passwords[i] == password)
    end
    nil
  end

文学フリマ版で電書を販売すると,送信先のメールアドレスを持つCustomerモデルのインスタンスをつくります。これは複数のBookモデル(名前がよくないのですが,購入した電書の情報)のインスタンスを持っています。電書フリマ版は,Bookモデルにsold_byという属性を追加して,ログインしたユーザー名を販売時に保存するようにしました。ユーザー名はcurrent_userメソッドで取得できます。

売上は,sold_byカラムでグルーピングして計算しています。

電書フリマはすべてのアカウントで同じ電書を販売するので,アカウント毎のカスタマイズは不要です。そのため販売する人のアカウントはモデルにしませんでした。

著者プロフィール

松永肇一(まつながけいいち)

株式会社ライフメディア 開発部マネージャ。東京都出身。"GNU for Towns"のためにリチャード・ストールマンに会いにいったり,ビル・アトキンソンとアンディ・ハーツフェルドの開発した"MagicCap"の日本語化を担当したり,よく分からない仕事を経て現在はWebアプリのエンジニア。Rubyon Rails(ときどきSinatra)を使って開発する毎日。スマートフォンアプリのレビューとランキングサイトであるスマートワールドでは電子書籍レビューも担当中。Twitterはma2

コメント

コメントの記入