2010-11-19

twitter より (2010-11-18)

  • 19:38  そろそろかな。iOS アップデート。iTunes のアップデートが来たし。→
Powered by twtr2src.

2010-11-18

欲しいのはオフライン機能 - Cocoa アプリとして作り直す #0 (Blogger Glass)

Blogger Glass も、内部リンクの置き換えができるようになったことで、目標であった「Blogger で作ったブログを Blogger とは独立した表示システムで見る」に到達したと言える。もちろん、あれこれ気になる点は残っているし、実現したいアイデアもある。けれど、ここらでちょっと別方向に進んでみようかと考えている。それは、Cocoa アプリとして作り直すこと、つまり Objective-C で書く。

Blogger Glass を Cocoa アプリにすることで実現したいことは、何よりオフラインで記事を読めるようにすることだ。(iMac のような)デスクトップ型の Mac で動かすアプリならオフライン機能にほとんど意味はない。しかし、MacBook や iPhone のように外に持ち出すことの多い機器で動かすなら、オフライン状態に対応することはユーザ体験を向上させる。

まずは Mac 版を作り、その後 iPhone アプリに作り直す。ユーザ体験やデザイン(意匠)はともかく、設計と実装は共通化したいものだけど、作る前から欲張るのはやめておこう。まずは動くモノを作ることだ。

という思いのもと、今日から Xcode との格闘を始めたのだけど、以前、ヒレガス本で勉強したことをすっかり忘れてしまっていることがわかった。もう、どこに何を書けば良いのかがさっぱりわからなくなっている。

GAE 版の進化の跡をたどるようにして作るかな。つまり、こんな感じ。

  1. 一覧表示
  2. Google Data API Objective-C Client Library の組み込み
  3. 記事表示
  4. ラベル検索
  5. 内部リンクの置き換え

オフライン機能(つまり取得したフィードの保存)はどこで実現するか。「内部リンクの置き換え」の前あたりが適当だろう。「内部リンクの置き換え」ではひもづけ情報の保存が必要なのだから。

今日の感じだと、「一覧表示」を曲がりなりにでも Cocoa アプリとして実現する最初のステップが、実は一番時間がかかるかもな。とりあえずリクエストハンドラ(だけ)を書けば、一応のアプリとして動かすことのできる GAE とは違うわ。

ま、今日は予告だけ。

関連リンク

関連記事

twitter より (2010-11-17)

Powered by twtr2src.

2010-11-17

タスクキューを使う - 内部リンクを置き換える #3 (Blogger Glass)

内部リンクの置き換えをデータストアを使って単純に実装すると、「一覧画面」表示リクエストの処理に時間がかかるようになってしまい(データストアへの書き込みが発生するときのみ)、GAE の管理コンソール(の Current Load)に赤文字が出るようになってしまった(→「CPU 使い過ぎ? - GAE 管理コンソール上の警告」参照)。

今回は、GAE のタスクキュー API を使って「一覧画面」リクエストの処理時間を短くすることに挑戦してみた。

タスクキュー API の使い方

GAE でタスクキュー API を利用して何らかの処理を行う際には以下の 3 つが必要になる。

  1. タスクを登録するキューの設定
  2. タスクを処理するハンドラの定義
  3. タスクを登録するコードの追加

以下で、これらを順番に今回の実装を例に説明する。ただし、公式ドキュメントにもあるように、タスクキュー API はまだ experimental 扱いなので、この先、仕様変更の可能性は高い。ここに書かれている内容もいつまでも正しいとは限らない。

タスクを登録するキューの設定

GAE のタスクキュー API で使用するキューには、名前(name)、処理速度(rate)、実行量(bucket_size) を設定することができる。詳細については「Python Task Queue Configuration」を参照。設定は queue.yaml という名前の YAML ファイルに記述する。1 つの GAE アプリが持つことのできるキューは 10 までとなっている。

タスクキューにはデフォルトキューが用意されており、これを(デフォルトの設定のままで)使う分には特に設定しなくて良い(つまり、queue.yaml を作らなくても良い)。以下にデフォルトキューの(デフォルトの)設定を示す。

Default Queue Settings
項目設定値
name default
rate 5/s
bucket_size 5

簡単に言うと、デフォルトキューでは 1 秒間に 5 つのタスクを処理するようになっている。

今回の実装では、デフォルトキューをデフォルトのまま使っている。このため queue.yaml は作っていない。

タスクを処理するハンドラの定義

タスクを処理するコードは、通常のリクエストハンドラと同様に定義する(google.appengine.ext.webapp.RequestHandler を継承)。タスク登録時に指定しない限り、タスクは HTTP の POST メソッドで処理される。よって、タスクのハンドラでは最低限 post() 関数を定義することになる。

また、他のリクエストハンドラと同様に app.yaml にも登録する。以下は今回の実装で追加した定義だ。login: admin はリクエスト実行にアプリ管理者の権限を要求する設定。こうすることで、ユーザがタスクハンドラを直接実行することを防ぐことができる(タスクハンドラを呼び出すタスクキューは管理者権限を持っている)。

- url: /store_pol
  script: store_pol.py
  login: admin

今回、実装したタスクハンドラは以下の通り。リクエストパラメータとして permalink と Blog ID を受け取り、さらにひもづけられた Post ID を memcache から取り出して、データストアに保存している。また、データストアへの書き込みはトランザクションとして実行するようになっている。これはタスクが並列で処理されるからだ。

model.bind_post_id_with_permalink() 関数は以前に示したものから変わっていない。

# store_pol.py: process tasks to store a pair of links to the data store.

from google.appengine.api import memcache
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

import model
import util

class StorePolHandler(webapp.RequestHandler):
    def post(self):
        permalink = self.request.get('permalink')
        blog_id = self.request.get('blog_id')
        post_id = util.get_post_id_from_memcache(permalink, blog_id)
        if post_id:
            def txn():
                model.bind_post_id_with_permalink(permalink, blog_id, post_id)
            db.run_in_transaction(txn)

def main():
    application = webapp.WSGIApplication([('/store_pol', StorePolHandler)],
                                         debug=True)
    run_wsgi_app(application)


if __name__ == '__main__':
    main()
タスクを登録するコードの追加

タスクを登録するには google.appengine.api.labs.taskqueue.add() 関数を使う(デフォルトキューの場合)。今回の実装では、permalink と Post ID の「ひもづけ情報」を登録する関数にこれを組み込んでいる(src/util.py 185 〜 186行目)。

def register_pair_of_links(entry):
    """
    Register a binding of a permalink and a post_id.
    The registration won't change, since the binding must be permanent.
    """
    permalink = entry.get_html_link().href
    blog_id = entry.get_blog_id()
    post_id = entry.get_post_id()
    # save the pair into the memcache, if it hasn't been stored.
    if memcache.get(permalink) is None:
        memcache.add(permalink, (blog_id, post_id))
    # issue a task, if the pair hasn't been stored yet in the data store.
    if model.get_post_id(permalink, blog_id) is None:
        taskqueue.add(url='/store_pol',
                      params={'permalink': permalink, 'blog_id': blog_id})

引数 url がタスクを処理するハンドラの URL 、params はハンドラに渡されるパラメータだ。また、URL は通常のリクエスト URL と同様、app.yaml に定義したタスクハンドラの URL と一致していなければならない。

結果はどう?

上記のバージョンを GAE に配備して動かしてみたところ、「一覧画面」の表示にかかる時間は確かに改善された。管理コンソールの赤文字も消えた。実装を(少し)複雑にしただけの効果はあったようだ。

結局、GAE で実用的なアプリを作るには、memcache - taskqueue - datastore の三段構えは必須なのだ。だからこそ、標準で用意されているのだろう(taskqueue はまだ experimental となっているけど)。

参考文献

Programming Google App Engine
Dan Sanderson
Oreilly & Associates Inc ( 2009-11-15 )
ISBN: 9780596522728

関連リンク

関連記事

twitter より (2010-11-16)

  • 09:42  へえ〜。ついに解禁か。 RT @touch_lab: アップル、今晩24時にiTunesに関する特別な発表を予告〜ついに「ビートルズ」の配信を開始!? - http://bit.ly/bS6EQe
Powered by twtr2src.