Hatena::Groupbugrammer

蟲!虫!蟲!

Esehara Profile Site (by Heroku) / Github / bookable.jp (My Service)
過去の記事一覧はこちら

なにかあったら「えせはら あっと Gmail」まで送って頂ければ幸いです。
株式会社マリーチでは、Pythonやdjango、また自然言語処理を使ったお仕事を探しています

|

2013-06-29

[] サービスのデータベース周りなどをチェックしたらレスポンス速度が格段に上がった話 00:48

はじめに

 で、例によってFXでの失敗ケース | Just another WordPress siteの話なのですが、当然のことながらサービスを運用するにあたって、サービスを動かしているサーバーリソースについて、とりあえずは何かあったときのために監視している人が多いと思います。自分も、例に漏れず、万が一のために、Muninを入れています。Munin自体の解説については、下の記事に詳しいかと思うので、そちらを読んで頂ければ。

 で、このMuninはプラグインが充実していて、その一つにhttpingの応答速度を記録しておくという機能があります。httpingというのは、pinghttpヴァージョンです。このあたりについても、下の記事を読んだほうが早いかと思います。

 で、このプラグインを入れてレスポンス速度を見ていたのですが、だいたい1,600msecくらいのレスポンス速度だったわけですね。

 このあたりのチューニングとかに関しては、正直意識したことがなかったので、「まあこんなものなのかなあ、結構データベース参照多いしなあ」という感じで放ったらかしにしていました。

 たぶん、このサービスは自分が一番のファンであり、一番アクセスしているサービスだと思うのですが、やはりレスポンスが若干遅いのは、使っている身からすれば気になる部分であることは間違いない。機能追加も一段落ついたので、ここは一つ、チューニングしてみるかと思ってチューニングした結果が下の通りです。

f:id:nisemono_san:20130630004546p:image

 1,800msecだったレスポンスをなんとか一秒台におさえることができました。やったね!これでちょっとだけ閲覧が楽になるよ!

 というわけで、その作業メモみたいなのを、ここに記録しておきます。

 ここで書かれることは、たぶん多くの普通のエンジニアであるならば意識していることだと思われるので、「なんでそんなこともやってなかったの」と思って頂ければ幸いです。逆に、始めてサービスを作る人で「どうしたらもう少しレスポンスが早くなるのかなー」と悩んでいる人がいたら、参考にして頂ければ幸いです。

まずはサービスのデータベース構成とURLの関係を整理する

 とはいえ、いざチューニングしよう!という話をするとしても、自分のサービス構成がどういう風になっているかを整理しなければ、何処がボトルネックになっているのか、どういうアプローチを取ればいいのかわからないので、その辺を整理してみる必要があるかと。この辺に関しては隠していても仕方ないので、今回の話に関係ある範囲で図にしてみます。

f:id:nisemono_san:20130630004544p:image

 当然、厳密に書こうと思えば、厳密に書けるのですが、取り合えずこのように書くと伝わりやすいかと思います。

 さて、この図を元に、どういうアプローチが取れるかを考えてみます。以下、データベースMySQLを前提としています。

Indexを適切に割り当てる

 まず最初に考えられることは、データベースのカラムに対してインデックスを作るということです。インデックスについては、このような記事などを参照してもらうとして、インデックスが使われるタイミングというのは、どうやら値を比較したときに使われるらしいので、BookのUserあたりに貼り付けると良さげなので設定(要検証)。

 また、他にもインデックスが使えそうな部分として、「最近クリックされた商品をログとして保っておく部分」である、BookLog。ここは、ログが無ければ作成し、ログがあればupdated_atという、更新日時を記録するカラムを更新する作りになっていて、その日付の新しい順番によって、商品を表示するようになっています。

 とすると、このupdated_atの部分にインデックスを貼ることによって、アクセスが早くなる可能性はありそうです。

 ちなみに、インデックスが使われない可能性があるところに関しては、ちゃんとドキュメントに記述してあります。実は、この記事を書いている間に、インデックスが適切に使われているかどうかについて、ちょっと検証が足りない部分があるので、この部分は課題です。

Memcachedを使う

 で、インデックスが終わったあとに、何が効率化できるか。

 このサービスでは、改善前は、アクセスがあったときに、律儀にデータベースに商品をランダムに取りに行っていました。

 しかし、ここで考えるのは、AとBのユーザーが同時にアクセスしたとして別々の商品をランダムに表示させるのは効率が悪いのではないか。各ユーザーにとって、商品がランダムに表示されればいいのではないか。

 なので、そのつど律儀にデータベースアクセスするのではなく、メモリにしばらくの間キャッシュしておいて、それを使った方がいいのではないかと思ったので、そのように実装しました。Memcachedについては、このあたりの記事を参考にするといいのかなと。

ORMで生成されるSQLとの戦い

 djangoもそうですが、フルスタックフレームワークだと、直にSQLを書くのではなく、何らかの形でデータベースアクセスし、クエリを発行するためのオブジェクトが用意されている筈です。

 ただ淡々と書いていると、あまり気がつかなかったりするのですが、明らかに無駄なSQLが発行されている場合もあったりするので、その辺をリファクタリングしていきます。例えば、djangoであるならば、debug-toolbarというものを使って、下のように実際に発行されているクエリをみることができます。

f:id:nisemono_san:20130630004545p:image

 ちなみに、この場合だと、"ORDER BY RAND()"で少し時間がかかっている印象です。知人に相談した処、「ORDER BY RAND()は使うなボケ、それはプログラムで処理しろ」と言われたので、その辺を改善する必要がありそうです。

まとめ

 というわけで、普通のエンジニアであるならば「当たり前のことを」という感じですが、実際に自分でサービスを作ってみると、本当にさっぱりになるので、やはりデータベース周りを勉強する必要性を痛感するのでした。あとは、状況と判断で適切なチューニングが出来るようにスキルをあげられたら嬉しいところです。

2013-06-28

[]プログラミングにおける毎日の課題を見つけるための課題一覧 22:49

 最近だと意識が高く、定時の一時間前に出社しては、なんらかの余技的な活動を行なっています(地獄のミサワっぽく読んでください)。しかし、最近はその辺がネタ切れになってしまって、「じゃあ何をしようかなー」とちょっとだけ悩んでしまいました。

 そこで、発想を変えて「じゃあ課題として考えられることを列挙してみよう」と思って、今回はブレインストーミング的に、「こういう課題が考えられる」ということを列挙してみようと思います。

「*-99」系の問題集をやってみる

 プログラマーなら、普段使っている言語があると思うのですが、それ以外の、気になるプログラミング言語をさわってみるというのもいいのかなと思います。

 そこで有名なのは「*-99」シリーズで、アスタリスクには各言語の頭文字が入ります。元々はProlog用の課題集だったaのですが、これが評判で、他のVersionもできるようになりました。その辺を、まず列挙してみます。

 上のものを、別の言語(例えばPythonであったり、Rubyであったり)で書き直すのもいいかもしれません。

競技プログラミングやオンラインジャッジに挑戦してみる

プログラミングの世界には、競技プログラミングの世界もあります。要するに、何らかのInputを与えられて、それを解くという奴ですね。

 例えば、その辺の本でいうと、通称・蟻本と呼ばれる『プログラミングコンテストチャレンジブック [第2版] ~問題解決のアルゴリズム活用力とコーディングテクニックを鍛える~』というものがあったり、あるいはTopCoderと呼ばれる競技プログラミングコンテストを対象とした『最強最速アルゴリズマー養成講座 プログラミングコンテストTopCoder攻略ガイド』という本も出ています。

  • no titleを解く
    • Project Eularは、数学的な問題を、機械を使って解答するというゲームです。最初の内は割と簡単なので初学者にとってもやりやすいと思うのですが、段々と難しくなってきて、ある程度アルゴリズムみたいなものがわからないと解けない問題も出てきます。ちなみに、有志が日本語訳をしていたりしています。
  • AtCoderに参加する
    • 日本版TopCoder、とでも呼べばいいのでしょうか。定期的に開催されます。時間制限があるので、発想力と実装力が試されるといったところでしょうか。過去の問題も解くので、競技は苦手だけど、問題を解きたいという人にもいいかもしれません。
  • Sphere Online Judge (SPOJ)に挑戦する
    • とにかく莫大な量の問題数がある。ほとんど英語なので、問題の意図自体がわからないこともままあり、その当たりに関しては根性でなんとかするのがいいでしょう。ちなみに、基本的にshellの知識も必要になる(例えば、終了時はexit 0が吐き出されている必要が有る)ので、その辺もそつなくやるといいかもしれません。

 とりあえず、このあたりに関しては、こちらのwikiにもまとめられているので、その辺も参考にしてみると良いでしょう。

何かしらの講義を受けてみる

 以上のは、問題を解くというのに特化した形ではありますが、現在は「オンラインで勉強できる」という環境が整いつつあるので、淡々とその辺りを消化するのもいいかもしれません。

  • ドットインストール
    • もはや有名といえば有名になりすぎた教育サイト。RubyPythonの他に、ScratchやD3AWKなど、逆に幅広く提供し過ぎて、それってどうなのという気持ちもなくはないですが、逆に「これってどういうものなんだろう」というのを理解する上においては、いい感じかもしれません。
  • TED Talks
    • もはやあえて紹介しなくてもいいくらい有名になってしまったプレゼンテーションの場であるTEDの、日本語で字幕が見れるサイトです。なにかのひらめきやきっかけとして、プレゼンテーションを見ていくと、何か得るものがあります。

Newsを消化する

 もう少し、別の方向から。アウトプットするためには、まず何らかの形でインプットする必要もあると思うのですが、そのあたりのリソースなんかもちょっとだけ紹介します。

  • Stack Overflow - Where Developers Learn, Share, & Build Careersをひたすら読む
    • 何か困った時に検索するとぶち当たり、そして解決し、その存在に感謝するサイト。別に問題にあたらずとも、とにかくスコアの高い記事をひたすら読んでいくのも、いろいろと勉強になることが多いように感じます。

一日一章、何らかの技術書を読む

 いま、ちょっと統計向けに作られたRをちょこちょこと読んでいますが、一日に一章だけ、技術書を読んでみるのもいいかもしれません。さすがに一日一冊だと気が滅入ってしまうのと、あと暴力的に硬い本であると、その一章の内容を理解するだけでも、非常に時間がかかってしまうことが多いので、そのあたりはゆっくりと時間をかけて理解していくのも一つの手なのかなとか、考えてます。

写経する

 良くプログラミングを覚える時に、写経するといいという話があります。もし何か覚えたいものがあれば、写経するというのは一つの手であるように感じます。ただ、写経するといっても、一から写経するのもつかれてしまうタイプなので、自分の場合だと、下のような写経の方法を考えたりしています。

  1. まず、ソースコードの構成を考える
  2. 自分が手を付けるだろうところから書き写していく
  3. 全体が完成したら、実行してみて、タイプミスなどをチェックしていく
  4. 次に、値や変数名を変えていってみる。その上でどのように挙動が変わるのかを見る

有名なフレームワークソースコードを読む

 ただプログラムをするだけだと、限界がある部分もあるので、他のソースコードを読み続けてみるのも一つの手です。自分の場合だと、良く使うフレームワークdjangoで、何かの問題にぶち当たるたびに、ソースコード自体を読みにいくのですが、それなりに使われるフレームワークは、それなりによく考えられた構成になっている場合が多い気がします。その辺を踏まえて、「なぜこの構成だと便利なのか」であったり、「なぜこういう書き方になったのだろう」と想像しながら読んでいくと、面白い発見をすることが時々あります。

快適な開発環境を目指す

 開発において、快適な開発環境というのは重要だと思います。そこで、開発環境のメンテナンスを少しずつ進めるというのも一つの手かなと思います。最近だとGitHubGistに、開発環境の設定ファイルを投稿している人がいるので、そういう人の設定ファイルを検索して、参考にしながら、自分だけのとっておきの環境を構築していくのは、普段の開発を進める上においてもスムーズになるかと思います。

 また、その他にも便利そうなスニペットがあったら、それを導入していってみるというのもいいかもしれません。

自分のサービスを一日一回機能改善する

 何かしらの、自分のサービスであったり、プラグインがあるとするならば、それらをメンテナンスすることは非常に重要なことであることというのも確かでしょう。バグがなかったり、追加する機能がなかったりしても、テストを追加したり、リファクタリングするという習慣は、きっと何かの役に立つかもしれません。

まとめ

 ということで、つらつらと書き連ねてみましたが、どうでしょうか。他にも「こういう方法があるよ!」という方法があれば教えてくだされば幸いです。

 ちなみに、現在自分が作成している、ブックマークされている本をランダムに表示したり、キーワードでつらつらと眺めてることができるFXでの失敗ケース | Just another WordPress siteも地味に毎日改良を加えているので、もしよろしければアクセスしてください(最近、こればっかりだな)。

 最近の改良は、本をキーワードごとに散策するという機能が追加されています。

2013-06-26

[]頑張る企業の皆さんに「バタリ制度」を提案します 19:16

はじめに

 一度ぶっ倒れてから、今日にかけて、昼休みの間に20分程度の仮眠を取るようにしているのですが、いつの間にか周りの人も10分間の仮眠であったり、机での仮眠を取るようになり始め、仮眠文化みたいなものが生まれつつあります。もしかしたら、他の人は激務であったり、そういうことで疲れているのかもしれませんが、自分の場合は、他のバイトをやっていたころから、仮眠を取らないと、体力が持たない、という欠点があります。

 似たような文化として、例えば「シエスタ」というのがあげられるかと思います。元々スペイン語であるこのシエスタは、どうやらWikipediaによれば長時間の昼休みのことを言うらしいのですが、「昼寝」という意味でも使われることがあるそうで、自治体でもシエスタ制度を取り入れているところがあったりします。

 ただ、「シエスタ制度」というのは、自分からすると、如何にも「海外から輸入された文化」という感じがして、余り馴染まない印象を持ちます。自分は昼寝が大好きですが、「シエスタ」と呼ばれると、なんだか違うかなーという印象を持ったりします。

 そこで、もうちょっと自然な形に出来ないものか、と思って考えたのが「バタリ制度」というものです。おれがかんがえたさいきょうの「バタリ制度」の概要は下の通りです。 

概要

  • 10分を1単位とし、各人6単位の範囲内で「バタリ」することができる。
  • 「バタリ」する期間として、1単位のみのバタリをすることも可能であり、また6単位全てを使った「長時間バタリ」も可能。
  • バタリを酷使する時間帯は自由。というのも、「ちょっと休まないと無理」という時間はいつだって訪れるものだから。
  • バタリする場所に関しては、「常識の範囲内」で自由。

なぜ「バタリ制度」なのか

 「バタリ」は、いわゆる「倒れるとき」の擬音からきているのですが、どうして上のようなことを考えたのか、というと、それは労働時間と生産性の問題です。

 何かの記事で読んだのですが、例えば、工場内で休憩時間を与えずに闇雲に働かせた場合と、休憩時間を間間に挟んだ場合、前者よりも後者のほうが、ミスが少なく、また作業効率も上がったという話がありました。直接の記事ではないですが、時々5分間休むと効果的、という記事をみつけました。

 つまり休憩時間を与えるということは、経営者の「慈悲」ではなく、むしろ経営的に合理的な判断であるとも言えるようです。自分が昔、派遣先で働いていた工場でも、3時間に一回、10分程度の小休憩を挟むなどをして、リフレッシュをおこなっていました。

 また集中力に関しても、集中できる時間というのは、50分から90分のようで、それ以上は脳が集中できなくなったりするみたいです。実際に、心身の都合によって病院にかかったときに、医者にアドバイスとして、「一時間に一回くらいは、仕事のことを忘れる時間を作ったほうがいいですよ」という助言も頂きました。

根強いファンのいるポモドーロテクニック(25分だけそのタスクに集中し、5分休むというのを繰り返す。ポモドーロはトマト型キッチンタイマーから来たらしい)にしても、休憩を細かく挟むことによって、安定した作業をできるようにしています。

 以上をまとめると、細かい休憩というのは、悪いことではなく、むしろ作業効率や、作業の精度を高めるために必要なことだと結論できるかと思います。

 当然のことながら、集中して作業に取り組み、作業が一段落したあとだったりすると、頭がぼんやりしたり、あるいはちょっとフラフラとしたりします。そこで、気兼ねなく「バタリ」することができれば、次の作業にリフレッシュできる、という理屈なわけです。

 また、休憩を取ることに慣れていない人でも、「バタリ」という言葉を使うことによって、「休憩を取らざるを得ない」というニュアンスが生まれて、罪悪感無しに休憩が取れる気がします(本当に、そういう気がするだけですが)。

まとめ

 というわけで、偉そうに「提案」という書き方をしてしまいましたが、少なくとも我が社(株式会社マリーチ)においては「制度的」なものとして定着しているわけではありませんので、採用してくれたら嬉しいなあ、とか思ったりする今日この頃です。

2013-06-25

[] DCIもどきみたいなものをPythonで実装してみる 21:51

 たまには、サービス改善はお休みして、プログラミングネタを。

 一時期、ScalaからRuby界隈で、「DCI」について議論されていた印象があります。で、自分がよく使う言語はPythonなのですが、Pythonでは、あんまりDCI的なアプローチを試験的に実装してみよう、という試みは余り見なかったように思われます。

 DCIについては、自分の下手な解説より、下のドキュメントを読んだほうが、正確な理解が得られるでしょう。

DCIアーキテクチャ - Trygve Reenskaug and James O. Coplien - Digital Romanticism

 自分の(たぶん間違っているであろう)理解によれば、何かしらの「オブジェクト」の振舞を「状況」によって、「役割」を与えることで決定しよう、というところにあると思っている。また、「そのオブジェクトが必要であるが、しかしそれがどのような役割を持ちうるかということが決定しずらいとき」にも有効なのかな、と思うけれど、その辺は詳しくないので、識者に頼みたいところ。

 また、現実的な実務としては、オブジェクトに対して「ありうるメソッド」を組み込んだり、継承で深くしたりするよりも、「役割」という単位で分割したほうが、ソースの見通しが良くなるというメリットも存在するとも考えれるようです。

 そういう感じで、自分はDCIを理解しているわけですが、今回の目的は、DCIを正確に理解することではなく、とりあえず上記の「ゆるふわな」理解を元に、ではPythonで実装するとするならば、どのようになるか、ということを考えていきたいと思います。

その前に具体的なイメージを

 比喩というのは、往々にして間違った理解を生みやすいのですが、とりあえず、今から実装されるストーリを考えます。

 目の前に二人の村人がいます。この村人は、とりあえずいることはわかっているのですが、その人たちがどんな人かはわかりません。しかし、その中の一人が風呂敷を広げて商売を始めました。彼は「商人」として振舞い始めたわけですね。もう一人が商人として振る舞ったわけですから、もう一人はお客さんとして、財布を見て、いくらくらいなら買えるのか確かめる、といった感じです。

まずベースになるclassを定義する

 そこで、最初にDCI的な機能をもつベースのclassを定義してみましょう。

# -*- coding: utf-8 -*-
import re


class MethodProvider(object):

    not_use_method = re.compile('^_')

    @classmethod
    def bind(cls, target):
        instance = cls()
        use_methods = instance._find_method()
        print use_methods
        for method in use_methods:
            setattr(
                target,
                method,
                getattr(instance, method))

    def _find_method(cls):
        return_method = []
        method_list = dir(cls)
        for method in method_list:
            if cls.not_use_method.match(method) is None:
                return_method.append(method)
        return return_method

 個人的には、かなり「黒いコード」だとは思うのですが、解説していくと、targetが「何らかのメソッド組み込みたい対象のインスタンス」であり、そしてそのクラスの属性/メソッド一覧に関しては、dir関数で取れるので、そこから、Python紳士協定であるところの先頭アンダーバープライベートメソッドをのぞき、あとは、それらをsetattrとgetattrでとにかく組み込んでいくという形になります。setattrとgetattrを使っているのは、文字列を使ってメソッドを引っ張って来れるからですね :)。

 さて、このclass継承した形で、次のような役割を与えるclassを作ってみましょう。

class Trader(MethodProvider):

    def sell(self):
        for goods in self.has_goods:
            print goods[0], goods[1]


class Visiter(MethodProvider):

    def buy(self):
        print "i have %d money." % self.has_money

 さらに、これらの役割が与えられる「人間」を定義してみましょう。

class Person(object):

    def __init__(self):
        self.has_goods = [
            (u'ひのきのぼう', u'10G'),
            (u'やくそう', u'5G')]
        self.has_money = 100

 では、実際にPersonに対してTraderの役割を与える関数も定義してみましょう。

def test():
    person = Person()
    Trader.bind(person)
    person.sell()

 これらを組み合わせたものは、gistにアップしてあるので、実際に実行してみるといいでしょう。

https://gist.github.com/esehara/5857997#file-dci_test_error-py

 しかし、これを実行してみるとエラーが起きてしまいます。

Traceback (most recent call last):
  File "test_dci_error.py", line 57, in <module>
    test()
  File "test_dci_error.py", line 54, in test
    person.sell()
  File "test_dci_error.py", line 32, in sell
    for goods in self.has_goods:
AttributeError: 'Trader' object has no attribute 'has_goods'

 これはいったいどういうことなのでしょうか。

他のインスタンスから引っ張ってきたメソッドは、引っ張ってきた元のコンテキストを保持する

 これは一つの罠(というより多分仕様なのですが)、他のインスタンスからメソッドを引っ張ってきても、selfのコンテキストは変わりません。つまり、この場合のselfはMethodProviderを参照にしているわけです。証拠に、下のように改造してみましょう。

class MethodProvider(object):
    not_use_method = re.compile('^_')
    has_goods = (
        (u'てつのけん', '500G'),
        (u'はがねのけん', '1000G'))
    @classmethod
    def bind(cls, target):
        instance = cls()
        ....

 has_goodsをMethodProviderに追加するとどうなるか。

https://gist.github.com/esehara/5857997#file-dci_badfix-py

$ python test_dci_badfix.py 
てつのけん 500G
はがねのけん 1000G

 確かに、selfがMethodProviderのほうに向いています。これはよろしくない。我々の目的は、メソッドをはやして、あたかも「Person」のメソッドのように取り扱うことです。つまり、selfの参照先はMethodProviderではなく、Personであるべきなのです。

解決

 これで、どうしたものかと考えていたら、d:id:nishiohirokazu さんがチャットで解決方法を教えてくれました(Thanks!!)。要するに下のようにするといいとのことです。

class MethodProvider(object):

    not_use_method = re.compile('^_')

    @classmethod
    def bind(cls, target):
        instance = cls()
        use_methods = instance._find_method()
        for method in use_methods:
            setattr(
                target,
                method,
                getattr(instance, method).im_func.__get__(target))

    def _find_method(cls):
        return_method = []
        not_use_method = ['bind', 'not_use_method']
        method_list = dir(cls)
        for method in method_list:
            if (not method in not_use_method and
                    cls.not_use_method.match(method) is None):
                return_method.append(method)
        return return_method

 どうやら、このim_funcという属性がポイントになるようです。どういうことか。そこで、あえてテストコードを下のように書いてみます。

# -*- coding: utf-8 -*-

class TestClass(object):

    def test(self):
        pass

    def func_print(self):
        print self.test
        print self.test.im_func

if __name__ == "__main__":
    test = TestClass()
    test.func_print()

 この結果として得られる出力が下の通りになります。

$ python im_func_test.py
<bound method TestClass.test of <__main__.TestClass object at 0x7f46af202e50>>
<function test at 0x7f46af206a28>

 つまり、self.testの場合は、それがどのクラスのメソッドとして束縛されているのか、というのを示すのに対して、im_funcを使うと、そのメソッド関数として取り出すことが可能になるっぽいです。(ちなみに、これはPython2.x系の実装らしく、Python3になると__func__になるとかならないとか)

 さて、メソッド関数として取り出せたのはわかったのですが、じゃあどうやってそれを他のクラスに埋め込むのか。そこで、使われるのが__get__メソッドのようです。上記のコードを次のように改造してみましょう。

# -*- coding: utf-8 -*-


class BoundTest(object):
    pass


class TestClass(object):

    def __init__(self):
        self.bound_test = BoundTest()
        self.bound_test.test = self.test.im_func.__get__(self.bound_test)
    def test(self):
        pass

    def func_print(self):
        print self.test
        print self.test.im_func
        print self.bound_test.test

if __name__ == "__main__":
    test = TestClass()
    test.func_print()

 そうすると、下のような出力が得られます。

$ python im_func_test.py
<bound method TestClass.test of <__main__.TestClass object at 0x7fdcd2a65e90>>
<function test at 0x7fdcd2a69b18>
<bound method ?.test of <__main__.BoundTest object at 0x7fdcd2a65f10>>

 というわけで、最終的なコードは下のようになります。

https://gist.github.com/esehara/5857997#file-dci_goodfix-py

 これを実行すると、無事、下のような出力が得られます。

$ python test_dci_goodfix.py
[Trader]
ひのきのぼう 10G
やくそう 5G

[Visitor]
i have 100 money.

 こうして、クラスとは別に、メソッドのselfの参照を、個別のインスタンスへと向け直すことが出来ました。

まとめ

 正直、このようにコードを書いてみましたが、いまいちDCIのメリットがどのようなものなのか、あるいはDCIが目指しているものとは何なのか、ということについては、まだまだ曖昧なところではありますが、このアプローチは便利だなあと思うところもある一方で、ちょっと「黒いかな?」という気持ちも拭えません。とはいえ、便利そうな雰囲気はあるので、機会があれば使ってみたいなと思うところです。

謝辞

 この方法を試している最中に、下の方々から助言を頂きました。改めてお礼を言いたいと思います。

2013-06-24

[]nginx + Webalizerを使って、定量的なログ解析を行なう 02:51

 ここ一週間くらいは、Bookableというサービス改善を行なっていました。

 とはいえ、普通、「サービスを改善する」といった場合、その改良が「改善」というための指標が必要になります。単純に「機能を追加する」というだけでも、一つの「改善」なのかもしれませんが、それが自分のサービスへの「目標」に近づいているのか、という点からも検証される必要があります。

 以前に、このサービスを立ち上げるにあたって、知人から「なんらかのアクセス解析ツールを入れないの?」ということを言われたことがあって、そういえばそういうことに思い至らなかったな、ということで、さっそくアクセス解析ツールらしきものを探したところ、Webalizerというツールを発見しました。

 既にGoogle Analizerなどのサービスはありますが、まだ大規模な分析が必要なほど大きなサービスではないので、とりあえず、簡易なアクセス解析が出来ればいいかな、と思って入れました。

 元々はApacheのログを解析したりするものだったらしいのですが、自分はフロントとしてnginxをたてているため、ログの形式を揃える必要がありそうです。自分は、下の記事を参考にログ形式を揃えました。

nginx + webalizer でアクセス解析 - Folioscope

 一応、ログの形式を揃えることで、ログ解析はできるようになります。ただし、継続的に解析するとなると、設定のところの

Incremental yes

 にしておくと、継続的にログを貯めてくれるのでよいでしょう。これをnoにしておくと、単体のログ解析のみが残るので、ログがローテートしたときに、過去の解析が全部無くなってアウアウしてしまうことになります(というか自分はなりました)。

 で、解析をかますと、下のような静的なHTMLが生成されます。

f:id:nisemono_san:20130625022602p:image

 特に、ポイントになる部分としては、PageとVisitになるのかなと思います。Pageが、あるページにアクセスされた回数であり、Visitは、30分以内に同一のIPで接続していないときは記録するという形になります。

 つまり、ページを閲覧している回数はPageで見て、Visitで、実際に来てくれたユーザーを見ると言うことになります。

 で、このデータについて面白い見方Webalizerの見方|ahref.orgに書いてありました。

平均ページ閲覧数 = 総Pages /総Visits

 自分のページだと、autopagerizeで、自動的に次のページを更新するようにしているので、そのレスポンス数が、そのサイトに興味をもってくれている度合いになるのかな、というので、この指標を参考にしています。

 で、ただデータを取るだけではなく、下のような形で、Google Spread sheetにまとめています。

f:id:nisemono_san:20130625025020p:image

 Spread sheetに、さらに自分が改善した内容を含めて、それがどういう風に影響しているのか、というのを定期的にデータにしています。

 ただ、このデータを見る限りだと、Page/Visit比は、平均10Pageほど見ているので、それなりに閲覧して頂いていることはわかるのですが、問題はやはりVist自体の数が少なくなりつつあることがあり、その辺をどのように増やしていくかが、今後の課題になりそうです。

|