As Sloth As Possible

可能な限りナマケモノでありたい

タグ:livedoor

あるいは、お遊びチーム2号は一体何をしていたのかについて

ISUCONという大変白熱した楽しいお祭を開催するにあたって、その前夜祭的な環境試験のためのチューニング祭が社内の有志数名で行われていて、そのときに色々学んだことをおまけとして書いておきます。

ISUCONて何?

下記参照。

要するに、「閲覧者視点での振る舞いさえ満たしてくれれば何をしようと構わんからWebアプリのレスポンスを改善しなさい」というお題で、誰が一番速くできるか競うイベント。

最初にある程度環境が整備されてるサーバ4台と、主催者側が用意した参考実装のアプリとテスト用データが渡される。このアプリってのはごくシンプルな個人ブログだと思ってもらえば良くて、最新記事10件が表示されるインデックスページと、記事全文が見られる詳細ページがあり、記事の投稿とコメントの投稿ができて即時反映される。全てのページには「最近コメントが付いた記事10件」が表示されるサイドバーがあり、ヘッダ画像、スタイルシート、JavaScriptが読み込まれる。テストデータにはそれなりに大量の記事データとコメントデータが入っている。フロントのWebサーバはApache、DBはMySQL、アプリはPerlとRubyとNode.jsのものが用意されている。

「お遊び組」って?

ISUCONは見学席が用意されていたのだけど、参加者以外はぶっちゃけ暇なので、空いたサーバ1チーム分を使って好きに遊んでいいことになっていた。で。そのお遊び用サーバで、事前に社内βで散々いじくり倒した俺とhidedenさんが空気読まずに自分のアプリで参加者達に対抗する、という大人気ないことをしていた。(すいません…)

一応ちゃんと言っておくと、中の人なので裏ルールや罠の存在についてある程度聞いていた上、6時間どころか2日3日費して試行錯誤していて、加えてhidedenさんに圧倒的な差を付けられた時は教えを乞うなどしていて、完全なるチートです。正直あの短時間でここまでやってくるって本当凄いな参加者のみなさん、と感動しっぱなしでした。勝手に熱くなって最後の方はムキになってたのは内緒です。

※ あ、でもでも、参加者の条件と対等じゃない「強くてニューゲーム」だった(会場に着くやいなや会社に行ってソース取って来た)という意味での「チート」で、コンテスト向けの実運用ではありえない実装にするとかはしてないです。なるべく突飛なことをせず普通にWAFを使い普通に配置するように心掛けてます。サーバのセットアップとかは手伝って貰ってるけど、アプリの実装は一人でやってるし、戦略そのものも自分で考えてます。

というわけで以下は俺のアプリで実際にやったこと。お遊び組ベストスコアを出した方とは別のアプリです。ちなみに使用言語はRuby。隣に座ったCTOと向かいに座った部長に「うちPerlの会社だからね?」とニヤニヤされながらも100%趣味全開のチョイス。「言語処理系の性能の違いが戦力の決定的差では無いということを教えてやる!」と赤くて速い人気取りで息巻いてみたものの、実際問題別な意味で全くその通りだった

糞クエリ対策とキャッシュ

殆どのチームがまずはクエリの見直しとDBのチューニングに手を付けてた様子。テーブル構造の見直しからMySQLのオプションやストレージエンジンの変更とかをやってたチームもあったみたいだけど、俺は必要なカラムにインデックスを貼る程度に留めて、データをガンガンキャッシュしてなるべくDBまで到達しないようにする戦略にすることにした。

まぁまずみんな真っ先に気付いてたところとしては、アプリ内に非常に残念なクエリがわざと仕込んであるということ。素晴しく分かりやすいお手本のような糞クエリだった。サイドバーのデータを取得するのが次のようなクエリ。

SELECT a.id, a.title
FROM comment c
LEFT JOIN article a ON c.article = a.id
GROUP BY a.id
ORDER BY MAX(c.created_at) DESC LIMIT 10;

記事が数千件、コメントが十数万件あるので、これは大分辛い。しかもサイドバーは全ページに表示されるので、全てのリクエストでこのクエリが発行されるという鬼畜さ。なので、次のように変更する。

  1. キャッシュからサイドバーに表示する記事IDのリスト取得を試みる
    1. なければ、commentテーブルから記事IDのみのリストを取得する
    2. キャッシュする
  2. 記事IDのリストを元に、キャッシュから記事データ10件のリスト取得を試みる
    1. それでも無かった数件をarticleテーブルからWHERE id INで取得する
    2. キャッシュする

あと、サイドバー・インデックスページ・記事詳細ページでそれぞれ必要なカラムだけを取って使ってたけど、これは逆にID・タイトル・本文・投稿日時全てを取得してIDをキーにキャッシュに突っ込むようにした。こうすることでサイドバーとインデックスと記事詳細でキャッシュを共有できるようになる上、コメントの投稿も記事の参照も新しい記事に偏っているので、サイドバーを読み込む時点では記事データはほぼ全てキャッシュに載っていることが期待できる。

commentテーブルから記事IDのリストを取得してるところはまだ重いと思うけど、当初のクエリよりは遥かに速いし、並行してコメントが投稿されたときの整合性の担保とかするのが地味に面倒だったので、とりあえず後回しにする。

コメントはコメントのIDではなく記事のIDでまとめてキャッシュするようにした。ページングや並べかえ、コメントの削除や編集などは仕様になく、コメントのパーマリンクなどもないので記事ページ以外では表示されないため、1回で全部取れるのがよかろうという判断。それらがあるようだったら、記事と同じようにコメントIDをキーにして「リストの順番や内容が変更されてもIDだけを取ってくるようにして、データ自体はキャッシュに載ってるものは使う」みたいにしたかもしれない。でもあんまりDBやmemcachedへのリクエストが増え過ぎるのはアレなので何らかのまとまりでキャッシュするかなぁ、とか色々考えてたけど、複雑になって余計悪化する、みたいなことにもなりかねないし、微妙に難しい。これもとりあえずこれでいいやってことにして後回し。

ノンブロッキングなフレームワーク

若干工夫してみたのは、フレームワークにはGoliathを選んだあたり。EventMachineベースのRubyのWebアプリケーションフレームワークおよびサーバで、これでI/Oを多重化して、同時接続数が増えてきても重いI/O処理でブロックしないで効率的に処理してくれることを期待する。まぁ、ぶっちゃけこれは実際にはそんなに効果なかった。

当初はPOSTのリクエストがばんばん飛んでくるとか、複数のテーブルに跨ってデータを取ってきて複雑な集約をするとか、画像アップロードみたいな長時間コネクション張り続けるようなリクエストがあるかなとか、そういう状況を想定してたんだけど、実際にはGETの比率のが圧倒的に高いしテーブル構造もシンプルで投稿はテキストのみ、みたいなパターンだったので「並列にI/O処理をする」ことがあまりなかった。

「遅延書き込みをするようにしてPOSTの際は即座にレスポンスを返してしまう」みたいなこともちょっとはやろうとしてたのだけど、「POSTリクエスト完了後1秒以内に表示に反映すること」ってルールにひっかかってテスト通らないことが頻繁にあったりして地味に嫌な感じになったので方針転換した。ここはむしろ同期処理にして「終わったら即書き込み即キャッシュ破棄」するようにして(実際には反映までにかかる時間に大差は無いはずなんだけど、こう振る舞った方がクライアント側からは速く反映されてるように見える)、POSTでは若干の時間をかけてしまってもいい、それよりもGETリクエストが来たときに既に準備が整ってるようにここで再取得再キャッシュまでやってしまう。

同時接続数の方はどうかというと、そもそもベンチマークスクリプトの並列数が最大10とかだったしコネクションも一瞬で切断されるし、じゃあワーカプロセスを10個立ち上げちゃえばいいじゃん、というオチが付いた。CPUもメモリもスッカスカでアプリサーバが遊んでたし、RubyやPythonみたいな言語を使う分には、1プロセス辺りの並列処理の効率化みたいなとこよりもUnicornの割り切りっぷりの方が現状理にかなってると思う。てことで言えばぶっちゃけ最初の素のSinatraで別に問題なかったような…どうしてもEventMachine使いたいならRainbows!Async Sinatraとかでも良かったような…まぁもう書いちゃったしいいか…。

余談だけどGoliath採用に併せて関連ライブラリも選び直すことになったので、MySQLクライアントにはMysql2を、mechachedクライアントにはremcachedを、ついでに趣味でテンプレートエンジンにSlimを使った。Mysql2やSlimは大分良かったので何か機会があれば使っていきたいところ。

Web「アプリ」って何?

とにかくもうDBに複雑なことさせたら負け、それ以前にDBに行ったら負け、と来たら次に来るのは「ていうかアプリに行ったら負け」。この段階ではフロントのWebサーバはアプリサーバ2台のロードバランサとしての仕事しかしていなくて、静的ファイルもアプリ側でファイル読んで返してたし、更新してないページも毎回アプリにリクエストが来ていた。バックエンドのアプリケーションサーバがどんなに速くなってもフロントエンドのWebサーバの処理速度とは文字通り桁が違うので、できればなるべくバックエンドに行って欲しくない。ならばということで、フロントエンドでページ丸ごとキャッシュしてGETリクエストは全部そっちで返してしまうことにする。

VCLの記述力やキャッシュ管理のしやすさ、ESI機能、あと名前のかっこよさなどが魅力的だったので、最初はVarnishを使ってみた。directorでアプリサーバ2台をまとめて、GET以外のリクエストは素通りするようにして、GETのレスポンスはページ単位でキャッシュするようにする。このままだと当然「投稿は1秒以内に反映されること」というルールが満たせないので、HTTPでパージできるように設定して、POSTのリクエストを処理したらアプリ側からVarnishにPURGEリクエストを送るようにした。

そうなると今度はキャッシュの破棄のタイミングが問題になってくる。記事の投稿はまぁいい。記事が投稿されて内容が更新されるのはインデックスページだけなので、1ページ破棄してやればいい。が、問題はコメントの方。コメントが投稿されるとまず記事ページが更新されて、「最近コメントが付いた記事10件」が表示されるサイドバーも更新される。が、このサイドバーは全てのページで表示されている。どこかの記事に1件コメントされる度に全ページのキャッシュが破棄されてたのでは殆どキャッシュの意味を為さない。なのでサイドバーは各ページのレンダリング時には生成せず、ESIでVarnishの段階でincludeさせるようにした。こうすれば、コメントが投稿されたときにパージするのは該当する記事ページとサイドバーだけになる。

そこまでやると最初の数回とPOST直後の数回以外は全部Varnishが返してくれるようになるので、アプリサーバの方は殆ど遊んでいる状態になる。前の段で「リソース余ってるからガンガンプロセス増やしちまおうぜ」って言えたのはこのおかげ。数万リクエスト処理しても数百とか数千くらいしか後ろに到達しなくて、さらにその後ろのDBまで行くのはもっと絞られてくる。この段階で初期状態から10倍くらいパフォーマンスが向上している。

ところがこの辺りで地味に嫌な罠を踏む。VarnishでESIを使うとContent-Lengthヘッダを返さなくなるので、Keep-Aliveで接続してるクライアントがいつレスポンスが終わったのかよくわからなくてタイムアウトするまで一部のクライアントできちんと扱えないのにKeep-Aliveのリクエストを送ってきたときにいつレスポンスが完了したのか判断できず、コネクションが切れるまで待ち続けてしまう。設定でどうにかできそうな気がするけど不慣れなVarnishに四苦八苦してなかなかうまくいかず、前段にもう一段Nginxを立ててリクエスト/レスポンスをいじってみたりするも今度は多段にしたのが祟ってそのオーバーヘッドで大分パフォーマンスが落ちてしまう。

結局Keep-Alive問題は真面目に対応するのをやめていかなる場合でも無効にしてしまおうかーとか考えてるあたりで、hidedenさんのNginx/SSI+SCGI構成にダブルスコアをつけられてしまって、NginxマジはえーほんとパネェつかVarnishってぶっちゃけNginxより遅いのに何で選んだの、みたいな声に負けてVarnishと戯れるのを放棄することにした。いや多分、俺のVarnish力の低さのせいで真の実力を発揮できてなかっただけでそこまでオワコンでは無いと信じたいのだけど、と一応擁護しておく。でもしばらくはNginx一択だけど。

ちなみに、ベンチマークスクリプトはそもそもKeep-Aliveに対応してないその「一部のクライアント」と同じ挙動をしていたわけではないので、この時点ではこの問題は割り切って無視するという選択肢もあった。が、本番当日の講評の際にkazeburoさんが恐しい罠を仕込んでたことを知らされる(っていうか社内では普通に話してたらしいけど聞いてなかった)。なんと、HTTP1.1じゃない持続的接続ができないのにそうできるかのように偽装してKeep-Aliveって付けた嫌がらせリクエストを3%程混ぜており、これに律儀に応えると、ベンチマークスクリプトはコネクション切れるまで待ち続けてしまって致命的に遅くなる、というもの。Nginxが素晴らしいのは周知の事実なので本番でも使ってくるチームが多いことを予想して、「Nginx(や、他の高速Webサーバやキャッシュサーバ)をチューニングせずにただ設置するとハマる罠」を仕掛けたんだとか。そうと知らずにそれを回避することに成功していて怪我の功名だった。本当運営の人達悪魔や…。

Nginxのチューニング

気を取り直してNginxの設定。まずNginxはWebサーバなので、餅は餅屋ということで静的なファイルはアプリサーバからフロントサーバに全部持ってきてNginxに返させてしまう。これでアプリから静的ファイルの配信機能を取っ払うのに成功して、本当に若干だけど無駄な処理を減らせる。リバースプロキシの設定は簡単なのでこれも普通に設定してしまう。それからVarinishのESI同様NginxでもSSIを有効にする。

それからキャッシュ。Nginxのキャッシュの方法はいくつかある。まず直感的なのはファイルキャッシュ。キャッシュファイルの置き場所を決めておいて、upstreamに飛ばすlocationのところでcacheを使うよって指定してあげれば、upstreamからのレスポンスを自動的にそこに貯めてってくれて、二回目以降は勝手にそのファイルから返すようにしてくれる。Varnishのときはキャッシュストレージにメモリキャッシュとファイルキャッシュが指定できて、ファイルの方を指定すると格段に遅くなるので、Nginxもそうなるんじゃないかと思ったけどこれが驚く程高速でびっくりする。むしろVarnishのメモリキャッシュの時より速いくらいだった気がする。ちゃんとは検証してない。ただ、このファイルキャッシュはuriをハッシュしてディレクトリに配置してしまうため、外からはどれが何のキャッシュなのか分からなくて、Varnishに比べるとキャッシュの管理が難しい、と思ってたら、ちゃんとこういうモジュール作ってる人がいた。これならVarnishのときに作ったHTTP越しのパージの仕組みがそのまま使える。

もう一つ、memcachedをまるでアプリサーバのように見立てて、pathをキーにしてmemcachedにあるデータをそのままレスポンスボディにして返してしまうという驚きのモジュールもある。こっちの利点は、アプリ側からもキャッシュが扱い易いということ。普通にアプリからmemcachedに繋いで、Nginxにレスポンスを返すときに同時にmemcachedにも書き込んでおくと、次はそっちから読んでくるようにしてくれる。破棄するのも普通にdeleteすればいい。HTTPリクエストを投げてパージするよりは分かりやすいし、memcachedプロトコルのがHTTPよりは速そうだ。が、問題は、前述のファイルキャッシュより遥かに遅いということ。これはmemcachedが遅いというよりmemcachedに毎回コネクションを張り直すコストがファイルキャッシュからの読み込み(これはおそらくかなり内部で最適化されているはず)のコストよりも高いせいらしい。ほぼ同じ状態でファイルキャッシュをmemcachedに切り換えたら、スコアが半分以下になってしまって絶望的な気分になった。仮にHTTP越しのパージよりもmemcachedのがアプリ側からは扱い易くてコストも低かったとしても、大半はNginxの段階で完結するキャッシュ済みGETリクエストなので、そっちのオーバーヘッドの方がもろに結果に影響する。ので最初はファイルキャッシュを採用した。

けど、hidedenさんの方はmemcachedを使っててそれでも俺のやつよりパフォーマンス出てるのでなんでだろうと聞いてみたら、upstreamとのコネクションを繋ぎっぱなしにしていたからだったらしい。試しにkeepaliveを設定してみたら、アプリ側何もせずに一気に3倍くらいのスコアになってhidedenさんのスコアに肉迫するレベルになった。同じことを当日もやらかした。hidedenさんが毎分11万リクエストというハイスコアを出した一方、前日まで大差はついてなかった俺の方は3万程度しか出てなくて焦ったのだけど、nginx.confを見直してkeepalive付け忘れに気付いて再起動したらちゃんと動いた。

で、結果はというと、ギリギリ100000req/minを越えるくらい。「お前はチートしてそれかよショボいな」と言われないくらいの結果は出せたと思うのでちょっとホッとした…。

この戦略の意味

POSTよりGETが圧倒的に多く、大半のリクエストが新しいデータに集中し、一度書き込まれた投稿はその後はあまり書き換えが起こらない、というのは、「大部分がほとんど書き換わらない動的コンテンツ」ではなくて「一部分が書き換わることがある静的コンテンツ」だと思っちゃえば少し話が簡単になる。

GETリクエストなんか静的なHTMLファイルを自動生成するためのトリガー、くらいに考えて、ただそれがファイル書き出しじゃなくてメモリ上のキャッシュに突っ込む方が扱い易いよねって発想で行けば、どんなミドルウェアが欲しいかとか、アプリは何をするべきなのかとか、どこで一番頑張るんだそうかフロントのWebサーバか、みたいなことでやることが決まる。てか、どっか1チームくらい本当にHTMLファイル書き出してデプロイしちゃうとこ無いかなーとか思いながら見てた。多分それはそれで速い。絶対面倒なのであまりやりたくは無いけど。

もっとぶっちゃけ話をすると、ライブドアブログの閲覧側チューニング戦略が(実現してる方法は違うけど)大体この形なので、ページのキャッシュと更新の局所化は最初からやる予定でいた。アプリエンジニアの性でついついアプリいじりに時間を割きたくなっちゃうとか、ミドルウェアやサーバ管理の知識経験が致命的に不足していたので時間ばっかり食ってしまったとか、ってのが「時間内には終わらなかったけど最終的には出来た」の実情だったりします。アプリエンジニアは常日頃からそういう知識をちゃんと収集しておけ、あとインフラチームと仲良くしておけ、色々捗るぞ、というお話でした。

上手く行かないケースと使い回せるテクニック

ブログ型の単純な表示系のリクエストが多いお題だったからWebサーバの性能が結果に直結してたけど、更新系のリクエストが多くて条件によって表示要素の個数や種類が大きく変わる、みたいな場合だと今度はアプリやDBの方に比重が移ってくる。例えばTwitterのサブセットみたいなのがお題だったらまた全然結果が違ったはず。

データのキャッシングやSSIみたいな仕組みはその場合でも有効だろうけど、「Nginx置いたらパフォーマンス20倍になったwwwwマジうめぇwwwwアプリ関係ないwwww」みたいなシンプルなことにはならないので、そっちの場合はアプリの実装力を鍛えてないと死ぬ。ISUCONに「部門別」とかあったら面白いかもねーとか思ったけど準備する側が死にそうなので軽々しく言うのはやめときます。僕お手伝いって名目なのに普通に遊んでただけでほんとごめんなさい。

反省点とか

DBロクに見てない。上位陣の方々見てるとMySQLバリバリチューニングしてるので、もっとちゃんといじればもう少し速度出るはず。一番効果が高いところを優先したと言えばそうだけど、あんま詳しくないところを放置しただけだったりもするので(アプリの全面書き直しとかマジで要らんかった)、ちゃんと勉強する。

みんなもやってみるといいよ

ISUCON運営チーム謹製のベンチマークツールと各言語の参考アプリは公開されてるので、是非触ってみてくだしあ。ゲーム感覚で楽しいし、各参考実装や意図的に仕込まれてる罠は、新人教育なんかにもうってつけだと思う。ええはい。自分の実力不足をガチで痛感した次第です。いや本当勉強しよう。ちゃんと。

もう、こなったら、iphone用livedoorアプリの出力するXML見て、自前でXML組み立てるかな。

誰か助けてください。(切実)

俺日記 : [HELP ME!]perlでlivedoorに投稿するときのカテゴリ設定方法

誰か助けてください、とのことなのでお助けします。…と、その前に一つ注釈というか弁明というかしておくと、実はそのAPI、AtomPubの仕様が完全に固まる前に実験的に作られたもので(まだAtomPPとか呼ばれてたはず)、色んなところがすごく古いです。

結果は惨敗。生成されたXMLを見てみると、ちゃんとcategory要素が追加されてるんだけどなー。

<?xml version="1.0" encoding="utf-8"?>

<entry xmlns="http://www.w3.org/2005/Atom">
  <title>ちょっとテスト(すぐ消します)</title>
  <content type="xhtml">
    <div xmlns="http://www.w3.org/1999/xhtml">いっぱいのつぶやき</div>

  </content>
  <category term="カテゴリ"/>
</entry>
俺日記 : [HELP ME!]perlでlivedoorに投稿するときのカテゴリ設定方法

AtomAPIの仕様を確認したら、atom:categoryじゃなくてdc:subjectを見ていた。どうも当時、2004年くらいはカテゴリーを表す仕様がなかったようで、dc:subjectで代用していたらしい。良く見たらXML::Atom::EntryのUsageにもそんな例が書いてある。ということで、dc:subjectのエレメントを作ってつっこんでやればカテゴリが追加されます。

です、が。AtomAPIはもともと正式にサポートしているものでないブログのQ&A:APIのURLは? - livedoor ヘルプ)上、見ての通り古いものなのでそのうちふさがれます。livedoor Blogで正式に公開してるAPIは今現在はAtomPubAPIの方なので、今AtomAPIでうまく動かないとか、今後Blogと連携するアプリを作りたいとかの場合は、こっちを使って下さい。ちなみにiPhoneアプリはこれを使ってる。

AtomPub APIの方を使って元記事のコードを書くとこんな感じ。あと、AtomPub APIは認証方式がWSSEなので、XML::Atom::ClientじゃなくてAtompub::Clientを使う。

#!/usr/bin/env perl

use strict;
use warnings;
use Atompub::Client;
use XML::Atom::Entry;

my $client = Atompub::Client->new;
$client->username('username');
$client->password('apikey');

my $service     = $client->getService("http://livedoor.blogcms.jp/atom/");
my $article_url = $service->workspace->collection->href;

# ブログが一個しかない場合は↑のコードでarticle collectionのURLが取れるけど、
# (workspaceの一個目がメインブログで、collectionの一個目がarticle collectionだから)
# ちゃんとやる場合は以下のように全部列挙したりして必要なのを選んでください。
# my @workspaces = $service->workspaces;
# for my $ws (@workspaces) {
#     my @collections = $ws->collections;
#     for my $c (@collections) {
#         print $c->title . "\n";
#         print $c->href . "\n";
#     }
# }

my $entry    = XML::Atom::Entry->new;
my $category = XML::Atom::Category->new;
$category->term('つぶやき');
$entry->title('これは、訓練ではない。');
$entry->content('繰り返す、これは訓練ではない。');
$entry->category($category);
$client->createEntry( $article_url, $entry );

あと他にもいくつか独自要素があるとか、実はlivedoor以外のBlogger Allianceのブログでも使えるとか、iPhoneアプリの絡みでときどきアップデートされるとか、あんま使い過ぎるとスパム判定されるのでほどほどにしてね、とかあるけど、その辺は追い追いどっかにちゃんと書きますんで、Wikiとか見てて貰えると。

あちこちで告知記事が出てるころだと思うけど、livedoorブログのCMSをリニューアルしました。

今回ブログモバイルの担当エンジニアとして開発に携わった立場上、開発の苦労とかとか書くべきかなと思ったけど、なにしろ肝心のモバイルの方は新CMSのお披露目が済んでないし、実のところこの後まだ一つ大きな山を越えなきゃいけなかったりするし、なによりユーザが「さぁブログ書こう」と思ったときに、他のどこかではなく「とりあえずlivedoorブログ」「やっぱりlivedoorブログ」って言ってくれるまで「終わり」にしていい仕事じゃないので、それまでは「俺、やったよ!やり遂げたよ!」って自分で言うのはおあずけにします。

…誰かが言ってくれる分には遠慮しないんだからっ。なんかこう、「どや!?」って顔してても生温い気持ちで接してください。

ついでに

さり気なくサブブログを作ってみたりしてます。ASAP::Mobile。多分こっちはとりとめのないことしか書かないとは思うけど、まぁこんな風にでもいいんでサクサクいくつもブログを作ってくれるといいと思うんだ、livedoorブログユーザのみんなが。

sasakillさんへ

不覚にもsasakillさんのスタッフロールに少し目が汗をかきました。なんもいえねぇ!

もひとつついでに

何故かメインスタッフの中で一人だけハンドル表記だけど別に本名を隠してるわけではなかったり。なんか妙に目立ってるけどおきになさらず。あと、僕のハンドルは「faultier」なのでそこのところよろしくお願いします。それとなく強調しとこう。

クリップの開発日誌で告知したけど、livedoor クリップのiPhoneアプリが出たよ。マイクリップ、ウォッチリスト、人気のページ、人気のニュース、人気のブログの新着を一覧表示ができて、それぞれのクリップされたページをWebViewで表示、コメントをオーバーレイして表示、等ができるアプリでございます。

それにしても出るまで長かった。βテスター募集したのが10月の頭、それからアプリ提出するところまでに2週間、一度Appleに提出したバイナリを消され(向こうの手違いらしい)、一度審査落ちて再提出し、審査が通ったのは11月の頭、そこからPending Contractで(ry。審査通ってから一ヶ月待たされたのはいかんともしがたいのだけど、まぁそれを置いといてももっとサクサク開発できたらなぁと痛感する次第。…頑張ろう。それからβテスターの方々、本当にご協力ありがとうございました。

現状では実はアプリ側では認証とかしてなくて、言ってしまえばまだiPhoneちっくなUIのクリップ専用のRSSリーダでしかないんですが(プライベートで使ってる人がこのアプリを使えないのはそのせい)、次期バージョンアップでプライベートモードへの対応と、アプリからのクリップ編集くらいを対応しようと思ってるところ。それから、新着だけでなく遡って過去のクリップを見るとか、オススメクリップの一覧を出すとかはする予定。次期に取り込めるかはわからないけど。

バグとかUIの改善とか要望とか、何かあったら言っておくれやす。俺にでも、クリップのサポートにでもいいので御気軽に是非!

今日はlivedoor Authのメンテなんですが…

メンテ画像

思わずタイトルのようなことを言いたくなった。くそう。この子は卑怯だ。ちなみにブログの404でもこの子が出てます。アイドル猫。でも今は…げふんげふん。何でもない。とっても可愛いぬこ画像に癒されるがいいさ。

livedoor Blogでタグ関連の新機能をリリースしたのでお知らせしまう。あれ。しまうー。噛んだ。

clipで指摘されてるように、先週から実はあったので既に使われてたりしてるのですけども。俺も木曜から入れてるしね。ちなみに、タグクラウドの表示数の上限と記事数の下限が設定できるようになったので、今まで「タグクラウドを付けたら大変なことになった!」って人も調整できるようにしました。というわけで使っていただけると嬉しいです。

それはそうと、ガイドページのカスタマイズ例…「よつばと」とか「電脳コイル」とか、思いっきり誰かさんの趣味が反映されちゃってるんですが、これ、いいのか?いいんだよね、多分。どうかつっこまれませんように。

サポートや開発日誌へのコメントの他に、ここでも受け付けることになります。で、バグ報告や要望を挙げると中の人が「なんとかします」とか「それ実は実装中」とか答えてくれるらしい。らしい、というか、既に「もうすぐ公開されます」なんてレスがついててニヤニヤしたりヒヤヒヤしたりしてるんですが。がりごり書いてますですよ。今まさに。

fixdap自体の要望受け付けもfixdapでプロジェクトになってたりしてるし、多分fixdapやBlogのチーム以外のlivedoorの中の人もかなり見てるので、いろいろ書いてくれるとありがたいですね。

それにしても次々いろんなプロジェクトが立ちあがってて面白い。保健室だより2月号とか、BTSで管理するんだ!とちょっと関心してしまった。エンジニアにしてみればBTSって馴染深いのだけど、昨日の反応とか見る限り「こんなのあったんだ」と思った人も多かったみたいで、fixdapをきっかけに色んなタスクをこういう形で管理する文化があちこちに広まると面白いなぁ。

RubyCocoaを極めるプロジェクトも地味に盛り上がってますぜ。主にるこたんの話題で。

追記

うは、RSpecを極めるプロジェクトにさらっと角谷さんが入ってる!

シンプルな共有BTSってところかな。出たばかりなのとコンセプト上機能を大分絞ってあるのとあって、BTSとしてはちょっと寂しいし、かといってソーシャル機能が強いとか個人タスク管理に向いてるとか、そういうわけでもない。livedoorのサービスだけどlivedoor IDでのログインじゃないから、特に他サービスとも連携してない。まぁ本格的にタスク管理したいならRTMとか使えばいいんだろうなぁ。

ただ、とっつきやすいので気軽に使える感じがいい。実際すごいぬるーいプロジェクトがぽこぽこ立てられたりしててネタで使えるし、オフ会のタスク管理とか趣味で作ってる小さいフリーソフトのBTSとかにもちょうどいいかも。自前でBTS立てるとか、関係者全員にRTM覚えさせるとか、流石にそれは大掛りだよなってときにはコレだな。

というわけで早速RubyCocoaを極めるに参加してみた。なんか作る。なんか作るのだそろそろ。

livedoor Reader 開発日誌:iPhone/iPod touch対応の「livedoor Reader lite」 - livedoor Blog(ブログ)”

早速試してみました。そう言えばiPod touchでまともにWebを見るの初めてだ。確かにiPhone/touch用に最適化されてる。おおお。凄く綺麗だ。


シンプルだから綺麗なのか

第一印象として、流石にiPod touch対応と銘打つだけあって、(シンプルでほとんど飾りっ気がないのに)見栄えがすごく良く見える*1。機能面ではできることはそんなに多くないなというところ。新着チェック用に最適化してあるとのことなので、フル機能のFeedリーダーではなくてPC版LDRの補助ツール的な位置付けなのかな。さしずめ俺専用ニュースヘッドライン、出勤途中に新聞のかわりにLDR liteを、ってのはいいかもしれない。これで、購入以来全く使ってなかったtouchのSafariと、しばらく前に停止してたlivedoor Wirelessの使い道が見つかってちょっと嬉しい。

あと、個人的には、liteで読んだ記事も既読にしてしまっても良いかなぁと思う。朝歩きながら読んだニュースを会社に着いてからまた整理しなおすとか、帰りに駅で読んだブログを自宅でもまた見ちゃったりとか、結構面倒な気がする。ピン機能は今後搭載されるみたいだし、iPod touchやW-ZERO3とかで見る人であればbookmarkletで簡単にclipやはてブに入れられるし。

iPod touch Safariの使い道

上に書いた通りtouchを買ってからSafariは全然使ってなかったんだけど、使ってみたら結構面白い。面白いんだけど、これ何に使おう。入力デバイスとしては全く使う気にならないしなぁ。さんざん言ってるけどキーボードがあまりにストレスフル*2なので、入力デバイスとしての性能は俺にとってはケータイ以下だな。加えてtouchの場合公衆無線LANが必要っていう制約があるから、それだったブログ書くにしてもMacBook持ちあるいてWILLCOMで繋ぐか、あるいは直接ケータイ使うほうがマシ。結局Feedとかtwitterとかに使うんだろうなぁ。あるいは、Safariならタブ機能やAjaxをフルに使えるので、Webベースのオンラインゲームがもしかしたら流行るかもしれない*3

あと、もう一つtouch Safariの面白い欠点に気付いた。タッチパネルというインターフェースのせいで、リンクを含む文字列が沢山あるようなページを操作するのが結構大変ということ。はてブを試しに見てみたんだけど、スクロールさせようと画面に触れるたびにリンクに指があたってしまい、意図せずにリンク先に飛んでしまうというのが何度もあった。これは、マウスを使うPCや、他にボタンがある携帯端末ではまず起きないことだけど、touchやiPhoneだとどうしても起こること。リンクが大量に含まれるページ…例えばfeed readerやSBMなんかは、iPod touchを対象とする場合何かスクロールに代わる操作方法を考えるのも面白いかも。それこそiPodじゃないけどホイールを模したインターフェースとか。

*1livedoor Reader lite の iPhone/iPod touch 用設定を読むによると、文字サイズを大き目にしてあるのが効いてるみたい。iPod touchのSafariの場合、PCで一般的に使われてる文字サイズ…10~15くらいかなぁ、あれが拡大しないと判読できないサイズとして表示されてしまう。レイアウトなんかはきちんとしてくれるけど、画像まで縮小されてたりして、せっかくデザインが良くてもiPod上ではあまり美しくない。ところが、ほとんどデザイン的な要素入れずに「文字大き目、表示領域幅固定、表示倍率は等倍のまま拡大縮小不可」って設定にしてやると、Safari上で丁度綺麗に見せてくれる。しかも、なんとなくiPodネイティブの機能みたいに見えるから不思議

*2:URLやパスワードを入れる段階でiPodぶん投げたくなった

*3:ただ、人狼BBSみたいのは絶対やりたくない。touchで人狼なんかやったらストレスでハゲるに違いない

↑このページのトップヘ