Techracho

PythonのSQLiteで検索しようとしたらIncorrect number of bindings supplied. The current statement uses 1, 10 supplied.とか言われた

このエントリーをはてなブックマーク Share
2010.07.16    Python, 馬場   タグ: , , , , —    baba   

最近の言語はSQLite3のライブラリが入っていることが多いですね。

Python 2.6でも簡単で、

import sqlite3
db = sqlite3.connect('data.db')
db.execute('create table users (id integer primary key, name text, age integer)')
db.execute('insert into users (name, age) values (?, ?)', ('yamada', 21))

のように簡単に扱えます。

ところが、使い方を間違えると

Incorrect number of bindings supplied. The current statement uses 1, 10 supplied.

のようなエラーが出てしまうことがあります。

原因は単純。
executeの第2引数はタプルにしないといけません。

項目が1個のとき、()で囲んだだけだとダメですね・・・

#ダメな例
db.execute('select * from users where name = ?', ('yamada'))  

#良い例
db.execute('select * from users where name = ?', ('yamada',))

基本的ですが、普段別の言語を書いていると気づくのが遅れます。

Twitter OAuthのrequest_tokenするところで、CallbackURLを指定したときだけ401 Unauthorizedになる場合

このエントリーをはてなブックマーク Share
2010.07.15    Python, 馬場   タグ: , , , , , , , —    baba   

Twitter APIで認証ページに遷移させるときは、http://twitter.com/oauth/request_token にリクエストトークンを発行依頼します。
しかし、設定を間違えるとここで401 Unauthorizedが発生してしまうことがあります。

Tweepyの場合、以下のようなコードになると思います。

consumer_token = "xxxxxxxxxx"
consumer_secret = "xxxxxxxxxxx"
callback = "http://www.bpsinc.jp/"
auth = tweepy.OAuthHandler(consumer_token, consumer_secret, callback)
redirect_url = auth.get_authorization_url()

今回は、auth.get_authorization_url()のところで、401 Unauthorizedが発生しました。

OAuthHandlerの第3引数、callbackを無しにすると、うまく動きます。

なぜかと思ったら・・・

ここが空だった

ここが空だった

Twitterのページでアプリを登録する際に、「ブラウザアプリケーション」を選んで、Callback URLを入力し忘れると、自動的にクライアントアプリケーションになります

クライアントアプリケーションでCallbackは使えないため、401エラーが返っていたようです(400 Bad Requestを返して欲しかったな・・・)。

また、登録したアプリがBANされた場合にも、同じ現象が発生するようなのでご注意下さい。

AndroidにAdMob広告を配置する入門

このエントリーをはてなブックマーク Share
2010.07.14    android, java, 馬場   タグ: , , , , —    baba   

インプレッション表示の収入がないとか、クリック単価が低いとか、なかなか広告が出ないとか、アプリ・Web開発者側からするとお金にしづらいAdMobではありますが、すごく使いやすいので、使っている方も多いと思います。

Androidに完全対応した(少ない手間で組み込める)数少ない広告システムということもあり、色々お世話になります。

今回は、AdMobの登録~Androidで広告を表示してみるまでの簡単な手順と、注意点です。

登録

http://jp.admob.com/から登録します。

アプリケーションを追加

ログインしたら上部タブから「マーケットプレイス」を選択します。
「キャンペーン」は広告主用のものなので、開発者は「サイト及びアプリケーション」を開きます。

「サイト・アプリケーションを追加」ボタンをクリックし、「Android」ボタンをクリックすると、アプリ情報入力画面になります。
ここに入力した内容に間違いがあっても、広告が表示されない、ということはありませんが、なるべく正直に入力しましょう。

SDKのダウンロード

アプリの登録を完了すると、SDKとマニュアルPDFがダウンロードできるようになります。
SDKはダウンロード・解凍して、適当な場所に置いておきます。同フォルダにAdMobSDKのリファレンス(JavaDoc)もあるので、是非参考にしてください。

これ以降の手順は、PDFに従うのが一番です!!

広告が表示されないとき

ということで、解説を途中で放棄して、「PDF通りにやったのに広告が表示されない」という場合のチェックポイントです。
基本的に、ManifestにパブリッシャーIDを入れて、attrs.xml、表示したいViewのXMLを書けばOKなのですが、以下の場合、広告が表示されません。

・ネットワーク接続できない
この場合、広告は取得できず、何も表示されません。

・表示領域が狭い
AdViewのサイズは、横幅320px以上である必要があります。それより小さいと、何も出てきません。

・ログに「no ads are available」と出る場合
広告がありません。AdMobは広告主に比べて開発者が多いのか、広告が見つからないことが良くあります。
この場合、Webの管理画面から「自社広告」を入れておけば、何も見つからないときに自社広告を表示することができます。

・単に遅い
Viewが表示されてから広告が表示されるまで、数秒かかるのが普通です。
上部に入れる場合は、48pxくらいの高さを確保しておいた方が、レイアウトが変わらなくて良いかもしれません。

動的にキーワードフィルタする

Web管理画面から、表示する広告のジャンル、キーワードフィルタ等を設定することができます。
しかし、アプリのコードから指定すれば、現在表示しているコンテンツにマッチした広告を表示することも可能です。

以下のようなコードで可能です。

AdView ad = (AdView)findViewById(R.id.ad);
ad.setKeywords("music");

とても多機能なAdMob、うまく使いこなしたいですね。

次世代RailsサーバーUnicornを使ってみた

このエントリーをはてなブックマーク Share
2010.07.09    Ruby, Ruby on Rails, 伊藤   タグ: , , , —    tomotaka   

2010.07.20追記: prefixを指定した運用も可能でした。ご指摘頂きありがとうございます。
2010.07.28追記: 関連記事「RailsサーバUnicornを飼いならす! 運用時の便利技」へのリンクを張りました。

伊藤です。

Railsサーバはたくさんあってややこしいですね!
最近さらにUnicornというものが頭角を表してきたようで、Twittergithubも使っているようなので使ってみましたので、特徴や使い方などレポートしてみたいと思います。
このブログの他にもEngine Yardのブログ記事「Everything You Need to Know About Unicorn」やgithubの記事「Unicorn!」が非常に参考になると思いますので、あわせてご覧ください。
(そもそもUnicornは用途をRailsに限定しない汎用のRackアプリケーションサーバです。タイトルは煽り気味ですね。すいません。)

ざっくりと、Unicornのアーキテクチャとそれにまつわるメリットデメリットをリスト形式で。

  • thinやmongrelみたいなマルチプロセスによるclusterではなく、forkを使ったmaster-slave
    • マルチプロセスモデルよりメモリ効率がいいかも?(copy-on-write)
    • ふくれあがったメモリ食い過ぎプロセスを殺しても、サービスにダウンタイムが発生しない
      • Monitとかでふくれあがったプロセスに対してQUITを送ると、そいつは処理中のリクエストを処理したら死ぬ
        • 親がそいつの代わりをすぐrespawnする
    • デプロイが早い
    • デプロイ時のダウンタイムがない
  • apache => app-server-clusterという風なpush requestではなくapache <= unicornというpullリクエスト
    • slaveプロセスが共通のソケットを通じてリクエストを受け取る
    • ひまなプロセスが処理を開始する(ソケットからリクエストを取り出す)
    • 処理中のもっさりアプリケーションサーバにあたることがない
      • もっさりサーバは処理がおわってないのでリクエストを取りにいくことがないから

それでもって、以下が実際に使ってみた印象です。

  • CPUあんまり食わない
  • メモリあんまり食わない
  • 確かにデプロイ(起動/再起動)早い。
  • たまに重いアクションを叩かれるような場合ではそのリクエストを処理しているworkerにあたることがなくなるので、全体のスループットを向上できる?
  • prefixを指定した運用ができない prefixを指定して運用可能です:unicorn_railsの–pathオプションで指定できます。

RailsアプリをMongrelやThinのクラスターで運用するとメモリをたくさんお食べになられるのが、結構悩みのタネだと思います。
スモールスタートのプロジェクトではフロントのロードバランサ、アプリケーションサーバ、DBサーバも全て1台でやるのが経済的理由からあたり前ですので、ThinやMongrelがメモリを食いまくるからといってサーバをもう1台追加しなくてはならないようでは積極的に使いにくいですよね。
しかしこのメモリ食いまくる現象にも理由はあり、Engine Yardのブログ記事「That’s Not a Memory Leak, It’s Bloat」でもActiveRecordのインスタンスが大量生成され、Rubyがそれを解放しないからふくれあがってしまうという主な理由が説明されていました。
もちろん優れたハッカーが多数いるRailsコミュニティでは対策も当然あり、Monitやそれに似たRubyベースのモニタリング(&再起動)ツールGodなどを駆使してCPUを使いすぎていたり、メモリを使いすぎているインスタンスに対して自動的に再起動をかけるのが一般的です。

しかしこのモデルでは以下のような一般的な構成で、サービスの安定性を追求した際に問題があります。

ロードバランサとThinクラスタ

ロードバランサは重み付けなしの設定を行うと、リクエストごとに、バックエンドのアプリケーションサーバに対して順番にリクエストを行います。ここで3番目(右はじ)のサーバに対して「レスポンスを返すまで時間がかかる地雷アクション」へのリクエストが来たとします。

クラスタの一部が重くなってる状態

このまま6回目のアクセスがくると、6回目のリクエストには3回目のリクエストが終わってないので処理が返せません。
こ6回目のアクセスがくる前に、左側のthinインスタンスと真ん中のthinインスタンスが4回目のリクエストと5回目のリクエストの処理を終えていれば、処理を肩代わりしてほしいところですが、リクエストの振り分けはapacheが上流で行っているので、難しいという状況です。

これと同じことがプロセスの再起動時にもおこります。ここで同様に右端のインスタンスが激重アクションによりメモリ使用量が爆増し、監視しているMonitなりGodなりが再起動をかけているとします。Railsのスタックのロードには1〜2秒程度時間がかかるので、アクセス数の多いサイトだったらこの間にリクエストをapacheから振られる可能性はゼロではありません。

再起動中のインスタンスがある場合

この例では右端のインスタンスが再起動を開始した1秒以内に3つのアクセスがくると、3つ目が準備が終わっていない3つめのインスタンスに来てしまいます。ここでは直感的には1つめの左端のインスタンスが、リクエストが終わり次第処理してほしい気がします。

ここでUnicornのロードバランシングのアーキテクチャを図にして見ました。

Unicornのアーキテクチャ

Unicornでは、MasterとSlave(図ではchildと書いています)がおり、Masterは起動するとSlaveをforkして生産します。mongrelをたくさん立ち上げるのと同じイメージですね。そしてApacheからのロードバランシングでは、Apache(ロードバランサ)が接続する先はMaster1つで、Unicornの内部でMasterとSlaveがリクエストをやりとりし、Slaveが処理した結果をMaster経由でApache(ロードバランサ)に返します。このやりとりというのがミソで、ひまになったSlaveがMasterに対して「次に処理するリクエストをくれ」という風にPULLしにくるのです。Masterは一番最初にpullしてきたSlaveに処理を行わせるだけでよいですね。こうすると、なにがうれしいかというとさっきのような例で「重い処理をしている」「再起動中」といったプロセスがリクエストの処理を担当することが原理的に起こりえません。このような状態では処理すべきリクエストをmasterに対してpullすることができないからですね。

また、forkを使うことによってOSのcopy-on-write機能により実際に使う実メモリ使用量が減る、Railsのコードをロードしなおさなくてよいなどのメリットがあるようです。

また、プログラムを更新する際にダウンタイムを作らない仕組みをうまく作っているのも面白いです。MongrelやThinクラスターをproductionモードで動かしている場合はクラスタ全体のの再起動が必要で、1〜2秒 * インスタンス数という時間が必要です。慌てて何度もやっているとサービスに影響が出てしまいますし、そもそもアプリケーションサーバがたくさんあるような場合では時間がかかってめんどくさいですね。Unicornではこの辺もエレガントに解決できており、UnicornのマスタープロセスにUSR2シグナルを送ると、もうひとつmasterプロセスを作って引き継ぎを開始します。新しいmasterプロセスは古いmasterプロセスから、上流のロードバランサと通信しているポートの引き継ぎを行います。古いmasterプロセスはUSR2シグナルを送っただけでは死なないのですが、後半に載せてあるgithubに公開されている設定スクリプトのように古いmasterに対してQUITシグナルを送る処理を自動化することも可能です。USR2シグナルを使って新しいmasterを起動するときはだいたい古いmasterはいらないと思われるので、before_forkでQUITシグナルを送ってしまうのがよいでしょう。

$ sudo kill -USR2 (masterのpid)

だけですみます。楽ですね!しかも今までthinインスタンス10個でクライアントの処理が完了するのを待っているのを含めて10秒以上かかっていたのがものの2〜3秒でできてしまいました。

という感じで、非常にいい感じのUnicornくんですが、日本語ではまだあまり情報がないので以下にハウツーとして実際に使えるコマンドや設定などを列挙していきたいと思います。

インストール

$ sudo gem install unicorn

※Windowsではまともに動かないっぽいです

起動

# productionモード(-E), デーモン化する(-D), 詳細をconfigファイルで指定
$ cd RAILS_ROOT
$ cd unicorn_rails -c config/unicorn-config.rb -E production -D

-Dオプションでデーモン化を指定しない場合フォアグラウンドプロセスとして動き、production.logに記録される内容も標準出力として出力されます。フォアグラウンドプロセスとして動いている場合、Ctrl+Cでunicorn masterと全てのunicorn workersの動作を停止させることができます。

設定ファイル(ここではconfig/unicorn-config.rbとして指定されている)の書き方については後述。

停止

そもそもUnicornではダウンタイムなしで新しいコードをproductionモードでデプロイできるので、停止の必要はめったにありませんので注意。
Unicornにおいては設定最読み込みとどうようにhogehoge stopというようなコマンドはなく、INT(TERM)/QUITシグナルを送ることにより、停止させます。QUITシグナルはgraceful shutdownで、全てのworkerがリクエストの処理を終えるのを待ちます。INTまたはTERMシグナルを送った場合は、すぐにworkerプロセスを全て皆殺しにします。(quick shutdown)

# unicornのmasterプロセスのIDを特定する
$ sudo pgrep -f 'unicorn_rails master'
12345

# graceful shutdown: 現在処理中の全てのリクエストの処理が終わるのをまってからシャットダウン
$ sudo kill -QUIT 12345

# quick shutdown: 現在処理中の全てのリクエストの処理を中断し、シャットダウン
$ sudo kill -INT 12345

設定再読み込み

Unicornにおいては設定を再読み込みさせるにはhogehoge reloadというようなコマンドはなく、HUPシグナルを送ることによって実現される。シグナルを送る先はunicornのmasterプロセス。masterのプロセスID(pid)を特定するには以下のようにする。
preload_app(デフォルトfalse)をtrueにしていると、プログラムコードはリロードされません。(逆にいえばpreload_appがfalseのままならmasterにHUPを送るとリロードされる)

# unicornのmasterプロセスのIDを特定する
$ sudo pgrep -f 'unicorn_rails master'
12345

# masterプロセスにHUPシグナルを送る
$ sudo kill -HUP 12345

プログラムのデプロイ

手順は公式のSIGNALのページに詳しく書いてあります。

# unicornのmasterプロセスのIDを特定する
$ sudo pgrep -f 'unicorn_rails master'
12345

# masterプロセスにUSR2シグナルを送る
$ sudo kill -USR 12345

再起動

先にも述べましたが、そもそもUnicornではダウンタイムなしで新しいコードをproductionモードでデプロイできるので、停止の必要はめったにありません。
それでも再起動するなら、停止→起動ですね。

設定ファイルの書き方

まずは、公式で参考として配布されている2つを見てみてフィーリングをつかむのがよさそうです:

githubのブログ記事で公開されているものを改変して、うちで使っているものも公開しちゃいます。

$default_env = "production" # デフォルトRailsEnv
$unicorn_user = "bps" # slaveの実行ユーザ
$unicorn_group = "bps" # slaveの実行グループ

$dev_processes = 4 # dev環境用子プロセス数
$prod_processes = 16 # 本番環境用子プロセス数

$timeout = 75 # タイムアウト秒数。タイムアウトしたslaveは再起動される

# String => UNIX domain socket / FixNum => TCP socket
#$listen = "/home/bps/tmp/unicorn.sock"
$listen = 5000

# ---- end of config ----

# Main Config for Unicorn
rails_env = ENV['RAILS_ENV'] || $default_env
worker_processes (rails_env == 'production' ? $prod_processes : $dev_processes)
preload_app true
timeout $timeout
listen $listen, :backlog => 2048

# For RubyEnterpriseEdition: http://www.rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
if GC.respond_to?(:copy_on_write_friendly=)
  GC.copy_on_write_friendly = true
end

# workerをフォークする前の処理
before_fork do |server, worker|
  ##
  # When sent a USR2, Unicorn will suffix its pidfile with .oldbin and
  # immediately start loading up a new version of itself (loaded with a new
  # version of our app). When this new Unicorn is completely loaded
  # it will begin spawning workers. The first worker spawned will check to
  # see if an .oldbin pidfile exists. If so, this means we've just booted up
  # a new Unicorn and need to tell the old one that it can now die. To do so
  # we send it a QUIT.
  #
  # Using this method we get 0 downtime deploys.

  old_pid = RAILS_ROOT + '/tmp/pids/unicorn.pid.oldbin'
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      # 古いマスターがいたら死んでもらう
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us
    end
  end
end

# workerをフォークしたあとの処理
after_fork do |server, worker|
  ##
  # Unicorn master loads the app then forks off workers - because of the way
  # Unix forking works, we need to make sure we aren't using any of the parent's
  # sockets, e.g. db connection

  ActiveRecord::Base.establish_connection
  #CHIMNEY.client.connect_to_server
  # Redis and Memcached would go here but their connections are established
  # on demand, so the master never opens a socket

  ##
  # Unicorn master is started as root, which is fine, but let's
  # drop the workers to git:git

  begin
    uid, gid = Process.euid, Process.egid
    user, group = $unicorn_user, $unicorn_group
    target_uid = Etc.getpwnam(user).uid
    target_gid = Etc.getgrnam(group).gid
    worker.tmp.chown(target_uid, target_gid)
    if uid != target_uid || gid != target_gid
      Process.initgroups(user, target_gid)
      Process::GID.change_privilege(target_gid)
      Process::UID.change_privilege(target_uid)
    end
  rescue => e
    if RAILS_ENV == 'development'
      STDERR.puts "couldn't change user, oh well"
    else
      raise e
    end
  end
end

nginxをロードバランサにした場合の設定例

upstream backend-unicorn {
        server 192.168.xxx.xxx:3000;
}

server {
        listen   80;
        server_name hogehoge.bpsinc.jp;

        access_log  /var/log/nginx/hogehoge.bpsinc.jp.access.log;

        location / {
                proxy_pass_header Server;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-Host $host;
                proxy_set_header X-Forwarded-Server $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                #proxy_set_header X-Geo $geo;
                proxy_read_timeout 75; # unicorn設定ファイルのtimeoutも忘れずに
                proxy_pass http://backend-unicorn; # upstreamで定義したバックエンド

                # 通常と違うポートでフロントサーバ(ロードバランサ)を動かしているときはこれが必要
                #proxy_redirect http://hogehoge.bpsinc.jp/ http://hogehoge.bpsinc.jp:8080/;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
                root   /var/www/nginx-default;
        }
}

参考リンク

どうでしたでしょうか?
BPSでもまだUnicornはノウハウが十分に蓄積されていませんが、積極的に使っていってノウハウをためていこうと思います。

運用時のTipsについて、続編を書きましたので合わせてごらんください:RailsサーバUnicornを飼いならす! 運用時の便利技

※7/13追記: USR2シグナルをmasterプロセスに送ったときの挙動の記述について、一部間違いがあったので修正。古いmasterは自動では死にませんね… あとnginxでの設定例を追加してみました。

Rubyでマルチスレッドプログラミング

このエントリーをはてなブックマーク Share
2010.07.05    Ruby, 伊藤   タグ: , —    tomotaka   

伊藤です。

RubyはWeb界隈でよく使われてるスクリプト言語の中では比較的簡単に割と本格的なマルチスレッドプログラムを書くことができます。
うまく使うとI/O待ちで遅くなっているが、必ずしも順番に行う必要のないプログラムの実行時間を短縮したりすることができます。(たくさんのURLにアクセスしてレスポンスを取得してくるクロウラーなど)
しかし現在最も広く使われているRuby1.8ではスレッドの実装はいわゆるグリーンスレッドという実装で、OSによるスレッドを使えないため、マルチコアの恩恵を受けることができないので、大量のデータをマルチコアで処理する目的などで使っても効果が薄い(どころかまったくない)可能性が高いです。
Ruby1.9ではRubyでスレッドの動きを制御しているものの、ネイティブスレッドベースでの動作となっているため、マルチコアの恩恵を受けることができます。

それではさっそく、配列の各要素を並列処理できるルーチンを貼付けてみます。
これは一番シンプルな実装で、配列に含まれる要素分だけスレッドを新規に立ち上げて、ガンガン処理させるというものです。

def multi_process(ary)
  threads = []
  ary.each_with_index do |e, i|
    threads << Thread.start(e, i){|tle, tli| yield(tle, tli) }
  end
  threads.each{|t| t.join }
end

# --- sample
a = [1, 2, 3]
multi_process(a) do |item, index|
  sleep 0.1*rand(10)
  puts "[#{index}]" + (item * 2).to_s
end

しかし、この実装では配列の個数の数だけスレッドが走ってしまうので、1つずつの要素に対する処理が重くて個数が多い場合、大量の要素を処理する場合Rubyが固まったようになってしまうでしょう。それにスレッドの切り替えや、実行チェックなどに多くのCPUタイムを取られてしまって実際の処理が思ったより進まないと思われます。この場合マルチスレッドプログラムにおけるProducer-Consumerパターンを適用して、Consumer(Worker)スレッドの数を限定することで同時にN個のスレッドしか動作させないことを保証することで並列性と処理効率のバランスを図ることができそうです。

Producer-Consumerパターンについて軽く解説すると、実際に処理を行うConsumer(Worker)スレッドをN個走らせて、これらに与えるタスクはブロッキングキューを使ってProducerスレッドから提供されます。ブロッキングキューは同期処理されたキューで、同時にアクセスされてもデータ構造が壊れることなく、正確に1つエンキュー(キューに追加)したり正確に1つデキュー(キューから取り出し)することができます。バグのないブロッキングキューを実装するのは思い出したり調べたりしないといけなくて意外と面倒だったりするのですが、rubyでは標準ライブラリthreadをrequireすることでQueueやSizedQueueといったクラスで利用することができます。SizedQueueではキューの最大の長さを指定することができ、キューが指定された大きさの状態でエンキューしようとすると他のスレッドからデキューされるまでエンキューメソッドはブロックします。

require "thread"

def multi_process2(ary, concurrency = 10, qsize = nil)
  q = (qsize) ? SizedQueue.new(qsize) : Queue.new

  producer = Thread.start(q, concurrency){|p_q, p_c|
    ary.each_with_index do |item, index|
      q.enq [ item, index, true]
    end

    p_c.times{ q.enq [nil, nil, false] }
  }

  workers = []
  concurrency.times do
    workers << Thread.start(q){ |w_q|
      task, index, flag = w_q.deq
      while flag
        yield task, index
        task, index, flag = w_q.deq
      end
    }
  end

  producer.join
  workers.each{|w| w.join }
end

# --- sample
a = [1, 2, 3]
multi_process2(a) do |item, index|
  sleep 0.1*rand(10)
  puts "[#{index}] #{item*2}"
end

こんなかんじになりました。
やはり多少コードは増えてしまいましたね。
multi_process2メソッドの第2引数では並列度としてConsumerスレッドの数を指定できるようにし、第3引数ではキューの長さを指定できるようにしました。キューの長さは明示的に指定しない場合無限(Queue.new)にするようになっています。

こんな感じで、案外簡単にマルチスレッド動作するプログラムが書けてしまいます。
ここで最後に、マルチスレッド初心者の方向けではありますが、基本的な落とし穴をご紹介しておきます。

以下の1〜10000の数をマルチスレッドで全て合計するプログラムを作ってみました。
数学的には1..nの合計はn(n+1)/2で求められることがわかっていますので、50005000が答えになるはずです。
しかし何回かこのプログラムを走らせると答えがおかしくなることがあります。

a = (1..10000).to_a
total = 0
multi_process2(a) do |item, index|
  total += item
end
puts total

これはtotalを読み書きしているdo〜endの部分で、total += itemという1文の命令が、実際には
1. totalを読み出す
2. (1)で読み出したtotalの値にitemの値を足す
3. (2)で計算した値をtotalに代入する
という分割されたフェーズで実行されることが原因です。
これが各スレッドで、まとまった固まりとして実行されず、互い違いに実行されるとおかしくなります。
これを防ぐには、一定の文の固まりを互い違いに実行させない仕組みが必要です。
RubyではMutexクラスのオブジェクトを使って、排他制御を行うことでこれを実現できます。

a = (1..10000).to_a
total = 0
lock = Mutex.new
multi_process2(a) do |item, index|
  lock.synchronize{ total += item }
end
puts total

これで答えがおかしくなることはなくなりました。
マルチスレッドから変数を操作する際には排他制御に気を配りましょう!
しかし、このスレッドのように並行実行しようとしている全てのコードが排他制御されているようでは本質的にマルチスレッドで実行する意味がまったくなくなってしまうので注意ですね。

カタぞうの画像表示高速化

このエントリーをはてなブックマーク Share
    CakePHP, PHP, カタぞう, 馬場   タグ: , , , , —    baba   

Webプログラミングの超基本ですが、画像表示の最適化です。

カタぞうでは、画像を動的に出力(サイズを変えたりなど)するために、いったんPHPを通しています。

投稿時に縮小しておくやりかたもありますが、ディスク容量を圧迫するのと、今後違うサイズが求められる可能性があるため、基本的に最大サイズで保存しておいて出力時に縮小画像を生成する仕組みを採用しています。

ところでこの仕組み、CakePHPのアクション内で実行すると、恐ろしく時間がかかります。

単純に画像をfpassthruするだけのコードで、600ミリ秒程度もかかりました。

主な原因は、Routingやフレームワークの初期処理が300msec程度、DBへのDESCRIBEが100msec程度、各種コンポーネントの初期化が200msec程度でした。

さすがに苦しいため、ここだけ生PHPで書いたところ、平均5msecに短縮。
これなら、画像をwebrootにキャッシュしてApacheのキャッシュを使わせる・・・ 等の処理をしなくても、遜色ありません。

今後のスケールに向けてもう少し練らないといけませんが、やっぱり基本は、不要な複雑処理を省くところですね。

(Railsの高速化テクを見ていて、「とにかくRailsに到達させるな」という方針で悲しくなりましたが、それと同じ)

Android 他のアプリケーションにファイルを渡す

このエントリーをはてなブックマーク Share
2010.07.01    android, java, 芝原      shibachan   

Androdではインテントという仕組みによって、アプリケーション間でデータを渡すことがきるのはご存じのとおり。
インテントに画像ファイルを詰めて、Gmailに渡せば新規メールの添付ファイルとなるし、
Twitterクライアントに渡せば画像をつぶやいてくれたりできます。

では具体的にどう書けばいいのかというと、

File file = new File(ファイルパス);  // 他アプリに渡すファイル

Intent intent = new Intent(Intent.ACTION_SEND);  // データーを送信するインテント
intent.setType("image/png");                                // データタイプの指定
intent.putExtra(Intent.EXTRA_SUBJECT, "件名");
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
startActivity(intent);

でいけます。

Android RejectedExecutionException

このエントリーをはてなブックマーク Share
    android, java, 馬場   タグ: —    baba   

開発中は問題ないのに、いざリリースしてデータをたっぷりと詰め込んだら、RejectedExecutionExceptionが発生して強制終了、ということがあります。

これは、AsyncTaskにたくさん詰め込みすぎの場合などに発生します。

AsyncTaskでは内部的にキューを持っていますが、このキューサイズを超えるタスクをexecuteすると、ブロックされずに例外が発生します。

ということで、

・catchして後でやりなおす(すぐにやり直したら意味が無い)
・catchして無視する(リスト表示程度なら、無視してユーザにリロードさせた方が良いかも)

などの対策をすれば良さそうです。

CakePHPで簡単Basic認証

このエントリーをはてなブックマーク Share
2010.06.30    CakePHP, PHP, 芝原      shibachan   

CakePHPで簡単な管理画面を作ったので、認証をかけたいけど、UserテーブルつくってAuthComponentっていうのはめんどくさい。
複数人のアカウント制御できる必要もないし、そんなのオーバースペックだ。

つい先ほど、まさにこの状態になって、簡単に認証を設定できないかと
ネットの海をさまよっていると、やはりありました。
CakePHP 1.2 の Basic 認証設定があまりにも簡単すぎる

SecurityComponentを使ってController#beforeFillterでベーシック認証を設定するという方法です。

class HogeController extends AppController {

    public $components = array('Security');

    public function beforeFilter() {
        $this->Security->loginOptions = array('type'=>'basic');
        $this->Security->loginUsers = array('username'=>'password');
        $this->Security->requireLogin('*');  // 全アクションを指定(特定アクションも設定できる)
    }
}

確かにすごい簡単でした。
パスワードが平文というのが少し気になりますが、簡単な認証でしたらこれで十分ですね。

Androidでリリース時にデバッグログをオフにする方法

このエントリーをはてなブックマーク Share
2010.06.28    android, java, 芝原      shibachan   

久々の技術ネタです。

Androidでデバッグ用のログを出力する際には

Log.d(String tag, String msg);

としますが、これで出力しているログは、このまま何もしなければリリース時にユーザーさんからも見えてしまします。

やはりデバッグ用のログが見えてしまうのは都合が悪いと思うので、

リリース時に少しのコストでこれを解消するTIPSをご紹介します。

方針としましては、

マニフェストファイルのis_debuggableがtrueの際にデバッグログの出力をオフにすることとします。

public class DeployUtil {

    /**
     * マニフェストファイルを読んでデバッグモードかどうかを取得
     */
    public static boolean isDebuggable(Context context) {
        PackageManager manager = context.getPackageManager();
        ApplicationInfo appInfo = null;
        try {
            appInfo = manager.getApplicationInfo(context.getPackageName(), 0);
        } catch (NameNotFoundException e) {
            return false;
        }
        if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == ApplicationInfo.FLAG_DEBUGGABLE) {
            return true;
        }
        return false;
    }
}
public class Katazou extends Application {

    private static boolean isDebuggable;

    @Override
    public void onCreate() {
        // デバッグモードか調べる
        isDebuggable = DeployUtil.isDebuggable(getApplicationContext());
    }
}
public class LogUtil {

    /**
    * デバッグログを出力する マニュフェストファイルでデバッグモードになっていなければ出力しない
    */
    public static final void d(String tag, String msg) {
        if (Katazou.isDebuggable()) {
            Log.d(tag, msg);
        }
    }
}

あとは、ソースコード中のLog.dを一括置換でLogUtil.dとすれば、
リリース時にis_debuggableをtrueにするだけで簡単にデバッグログをオフにすることができます。

参考

Android で自動オフできるログ出力

« 新しい投稿古い投稿 »

COPYRIGHT [C] 2009 BEYOND PERSPECTIVE SOLUTIONS LTD. ALL RIGHTS RESERVED.