@blog.justoneplanet.info

日々勉強

ApacheでSSLを利用する

前回の記事を参考に秘密鍵と証明書を生成する。

■コマンド

以下のファイルを編集する。

vi /etc/httpd/conf/httpd.conf

以下の記述を追記する。

SSLCertificateFile /root/ssl/server.crt
SSLCertificateKeyFile /root/ssl/server.key

バーチャルホストで運用し、特定のサイトのみSSLでも通信を行う場合は以下のようにする。

<VirtualHost *:80>
    SSLEngine On
    SSLCertificateFile /root/ssl/server.crt
    SSLCertificateKeyFile /root/ssl/server.key
    DocumentRoot /var/www/vhosts/domain/httpdocs
    ServerName localhost.localdomain
    ServerAlias www.localhost.localdomain
</VirtualHost>

CentOSにmembaseをインストールする

CentOS 5.3の32bit版にインストールしてみることにした。

■インストール

以下のコマンドでrpmからインストールできる。

wget http://c2978342.ltd.cloudfiles.rackspacecloud.com/membase-server-community_x86_1.6.4.rpm --referer="http://www.membase.org/downloads"
rpm -ivh membase-server-community_x86_1.6.4.rpm

ダウンロードサイトの仕様上、wgetにリファラーをつけた。インストールが完了すると以下のメッセージが表示される。

Starting Membase server[  OK  ]

You have successfully installed Membase Server.
Please browse to http://localhost.localdomain:8091/ to configure your server.
Please refer to http://membase.com and http://membase.org for
additional resources.

Please note that you have to update your firewall configuration to
allow connections to the following ports: 11211, 11210, 4369, 8091
and from 21100 to 21199.

By using this software you agree to the End User License Agreement.
See /opt/membase/LICENSE.txt.

■設定

ブラウザから設定ができるらしいので、以下のコマンドでポート8091番を開ける。ついでに上述のメッセージに従って、11211番、11210番、4369番も同様に対処しておく。

system-config-securitylevel-tui

http://localhost:8091/にアクセスすると悪そうな顔したキャラが表示されるはずだ。後は画面の指示に従えば良い。

■ベンチマーク

上手く書き込みできるかを検証するついでにベンチマークしてみる。

$memcache = new Memcache();
$memcache->connect('localhost', 11211);
$start = microtime(true);
for($i = 0; $i < 1000; $i++){
    $memcache->set("k{$i}", "abcdefghijklmnopqrstuvwxyz");
}
$end = microtime(true);
print($end - $start);// 0.19418982505798

以前にmemcachedのベンチマークで使用したコードをそのまま使うことができるのが素晴らしい。

勝手評価

速度はmemcachedの2~3倍程度かかるようだが、他のKVSよりも数倍高速である。複雑な構造を必要とせずハッシュテーブルとして使用するだけならば、オススメできる気がする。

不具合:保存ができない

管理画面にアクセスし(http://localhost:8091/index.html#sec=monitor_buckets&zoom=zoom_minute)Data Bucket>Item Countを見ると0のままで保存がされていない。

原因

memcachedが既にポートを使っていた。

対策1

以下のコマンドでmemcachedをstopする。普通の人はこれで良いと思う。

/etc/init.d/memcached stop
対策2

以下のコマンドでmemcachedをアンインストールする。membaseがあればmemcachedは要らないってこともあり得る。

yum remove memcached
確認

以下のコマンドで確認すると良いかもしれない。

lsof -i:11211
悪い例

memcachedがポートを使っている。

COMMAND    PID      USER   FD   TYPE DEVICE SIZE NODE NAME
memcached 2500 memcached   26u  IPv6   8072       TCP *:11211 (LISTEN)
memcached 2500 memcached   27u  IPv4   8073       TCP *:11211 (LISTEN)
memcached 2500 memcached   28u  IPv6   8077       UDP *:11211
memcached 2500 memcached   29u  IPv4   8078       UDP *:11211
良い例

membaseがポートを使っている。

COMMAND  PID    USER   FD   TYPE DEVICE SIZE NODE NAME
moxi    6481 membase   40u  IPv6  38065       TCP *:11211 (LISTEN)
moxi    6481 membase   41u  IPv4  38066       TCP *:11211 (LISTEN)

■特性

memcached互換のNoSQLデータベース「Membase」がオープンソースで登場」を参考のこと。デフォルトでGUI管理ってのはなかなか頑張ってるよね。

Webkitのconsole.logで配列を評価すると値が異なる

以前から知られていた不具合。出力直前でalertさせる以外の解決方法があったので記載しておく。

■再現コード

var ary = ["hoge"];
console.log(ary);
ary[0] = "fuga";
console.log(ary);

Firefox3.6

Firefox3.6のfirebugの場合は以下のように表示される。

["hoge"]
["fuga"]

Chrome8

Chrome8の場合は以下のように表示される。

["fuga"]
["fuga"]

■修正

以下のように文字列として表示するとChrome8でもFirefoxのように表示される。

var ary = ["hoge"];
console.log(ary.toString());
ary[0] = "fuga";
console.log(ary.toString());

もしくは以下のようにJSON.stringifyを使用し、JSON文字列とする。

var ary = ["hoge"];
console.log(JSON.stringify(ary));
ary[0] = "fuga";
console.log(JSON.stringify(ary));

■詳細

console.log object maps are read when you open the treeview on the console, not when they’re output in the console

JavaScriptで二分探索木

二分探索木とは二分木と同じ構造で、左の子<親<右の子となっているものですな。

■二分探索木に対する挿入

以下のようにして、二分探索木(Binary Search Tree)に対する挿入を実装してみた。linked listで再現したほうがイメージしやすい気もするがコード量は増えるので配列を使用した。

Array.prototype.bpush = function(elm){
    var ary  = this;
    var push = function(elm, key){
        if(!ary[key]){
            ary[key] = elm;
        }
        else{
            if(elm < ary[key]){// left child
                push(elm, 2 * key + 1);
            }
            else{// right child
                push(elm, 2 * key + 2);
            }
        }
    }
    push(elm, 0);
}

var bst = [];
bst.bpush(5);
bst.bpush(7);
bst.bpush(8);
bst.bpush(3);
console.log(bst);// [5, 3, 7, undefined, undefined, undefined, 8]// ex1

var bst = [];
bst.bpush(7);
bst.bpush(2);
bst.bpush(5);
bst.bpush(1);
console.log(bst);// [7, 2, undefined, 1, 5]// ex2

var bst = [];
bst.bpush(1);
bst.bpush(2);
bst.bpush(3);
bst.bpush(4);
console.log(bst);// [1, undefined, 2, undefined, undefined, undefined, 3, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 4]// ex3

ソート済みの配列を格納した場合、上述ex3に見られるように線形リストとなり下述の探索性能が下がる。

■探索

上述で生成した二分探索木を探索するアルゴリズムを実装した。

Array.prototype.bpush = function(elm){
    var ary  = this;
    var push = function(elm, key){
        if(!ary[key]){
            ary[key] = elm;
        }
        else{
            if(elm < ary[key]){// left child
                push(elm, 2 * key + 1);
            }
            else{// right child
                push(elm, 2 * key + 2);
            }
        }
    }
    push(elm, 0);
}

Array.prototype.bsearch = function(elm){
    var ary    = this;
    var search = function(elm, key){
        if(!ary[key]){
            return -1;
        }
        if(elm === ary[key]){
            return key;
        }
        else{
            if(elm < ary[key]){
                return search(elm, 2 * key + 1);
            }
            else{
                return search(elm, 2 * key + 2);
            }
        }
    }
    return search(elm, 0);
}

var bst = [];
bst.bpush(5);
bst.bpush(7);
bst.bpush(8);
bst.bpush(3);
console.log(bst);// [5, 3, 7, undefined, undefined, undefined, 8]
console.log(bst.bsearch(3));// 1

var bst = [];
bst.bpush(7);
bst.bpush(2);
bst.bpush(5);
bst.bpush(1);
console.log(bst);// [7, 2, undefined, 1, 5]
console.log(bst.bsearch(3));// -1

var bst = [];
bst.bpush(1);
bst.bpush(2);
bst.bpush(3);
bst.bpush(4);
console.log(bst);// [1, undefined, 2, undefined, undefined, undefined, 3, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 4]
console.log(bst.bsearch(3));// 6

まぁこんな感じ。前述の通りex3は線形リストのシーケンシャル探索と同じとなり性能が低下する。従って、要素の挿入と削除に対して、ツリーの平衡を維持するようなロジックを導入する必要がある。代表的なものとして赤黒木などがある。

■特性

  • 最悪時の計算コストはO(n)。平均時はO(log n)となる

DjangoでFormを作る

前回の記事のコードをベースに解説する。

■Formクラス

以下のようにFormクラスを継承して、ArticleSearchFormクラスを生成する。

# -*- coding: utf-8 -*-
from django import forms

class ArticleSearchForm(forms.Form):
    word = forms.CharField()

■ビュー関数

以下のようにsearch関数を定義する。

from django.http import HttpResponse
from django.http import Http404
from django.template import Context, loader, RequestContext
from models import Article
from forms import ArticleSearchForm

# Create your views here.
def article(request, id):
    try:
        article = Article.objects.get(id = id)
        tpl     = loader.get_template('page/article.html')
        context = Context({'article' : article})
        return HttpResponse(tpl.render(context))
    except Article.DoesNotExist:
        raise Http404

def search(request):
    if request.method == 'POST':
        form = ArticleSearchForm(request.POST)
        if form.is_valid():
            tpl      = loader.get_template('page/search.html')
            articles = Article.objects.filter(title = form.cleaned_data['word'])
            return HttpResponse(tpl.render(RequestContext(
                request,
                {
                    'form'     : form,
                    'articles' : articles
                }
            )))
    else:
        form = ArticleSearchForm()
    tpl = loader.get_template('page/search.html')
    return HttpResponse(tpl.render(RequestContext(
        request,
        {
            'form' : form
        }
    )))

この場合、タイトルが完全一致しないと検索にヒットしないので以下のように__containsを付加する。これはSQLでいうLIKEのような役割だ。

from django.http import HttpResponse
from django.http import Http404
from django.template import Context, loader, RequestContext
from models import Article
from forms import ArticleSearchForm

# Create your views here.
def article(request, id):
    try:
        article = Article.objects.get(id = id)
        tpl     = loader.get_template('page/article.html')
        context = Context({'article' : article})
        return HttpResponse(tpl.render(context))
    except Article.DoesNotExist:
        raise Http404

def search(request):
    if request.method == 'POST':
        form = ArticleSearchForm(request.POST)
        if form.is_valid():
            tpl      = loader.get_template('page/search.html')
            articles = Article.objects.filter(title__contains = form.cleaned_data['word'])
            return HttpResponse(tpl.render(RequestContext(
                request,
                {
                    'form'     : form,
                    'articles' : articles
                }
            )))
    else:
        form = ArticleSearchForm()
    tpl = loader.get_template('page/search.html')
    return HttpResponse(tpl.render(RequestContext(
        request,
        {
            'form' : form
        }
    )))

更に、条件をORで結合させたい場合は以下のようにする。

from django.http import HttpResponse
from django.http import Http404
from django.template import Context, loader, RequestContext
from models import Article
from forms import ArticleSearchForm
from django.db.models import Q

# Create your views here.
def article(request, id):
    try:
        article = Article.objects.get(id = id)
        tpl     = loader.get_template('page/article.html')
        context = Context({'article' : article})
        return HttpResponse(tpl.render(context))
    except Article.DoesNotExist:
        raise Http404

def search(request):
    if request.method == 'POST':
        form = ArticleSearchForm(request.POST)
        if form.is_valid():
            tpl      = loader.get_template('page/search.html')
            word     = form.cleaned_data['word']
            articles = Article.objects.filter(Q(title__contains = word) | Q(content__contains = word))
            return HttpResponse(tpl.render(RequestContext(
                request,
                {
                    'form'     : form,
                    'articles' : articles
                }
            )))
    else:
        form = ArticleSearchForm()
    tpl = loader.get_template('page/search.html')
    return HttpResponse(tpl.render(RequestContext(
        request,
        {
            'form' : form
        }
    )))

この辺はZendの方がSQLチックで直感的な気がする。

■テンプレート

以下のようにテンプレートを記述する。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>search</title>
<head>

<body>
<p>{{error_msg}}</p>
<form action="/search/" method="POST">
{% csrf_token %}
{{form.word}}
<input type="submit" value="search">
</form>
<br>
<table>
<tr>
<th>title</th>
<th>date</th>
</tr>
{% for article in articles %}
<tr>
<td>{{article.title}}</td>
<td>{{article.date}}</td>
</tr>
{% endfor %}
</table>
</body>
</html>

「setting.pyのMIDDLEWARE_CLASSESにdjango.middleware.csrf.CsrfViewMiddlewareが含まれている」かつ「ビュー関数でRequestContextを使用している」場合、{% csrf_token %}にCSRF対策用のトークンが埋め込まれる。

■URL設定

以下のように記述してURLを設定する。

from django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    (r'^admin/', include(admin.site.urls)),
    (r'^article/(?P<id>[0-9]+)/$', 'dj.article.views.article'),
    (r'^search/$', 'dj.article.views.search'),
)

DjangoでHTTPレスポンスコードを変更する

前回のままだとURLのidが存在しなかった場合、500番を返すが本来404であって欲しい。以下のように記述することで実現できる。

from django.http import HttpResponse
from django.http import Http404
from django.template import Context, loader
from models import Article

# Create your views here.
def article(request, id):
    try:
        article = Article.objects.get(id = id)
        tpl     = loader.get_template('page/article.html')
        context = Context({'article' : article})
        return HttpResponse(tpl.render(context))
    except Article.DoesNotExist:
        raise Http404

JavaScriptで型付の配列を扱ってみる

JavaScriptにも型付の配列が登場したらしいので使ってみる。((o(^-^)o))ワクワク

■通常の配列と型付の配列の比較

以下のコードで通常の配列と型付の配列の速度を比較した。ちなみにChromeのCanary Buildを使用してテストしている。

var ary1  = new Array(1000000);
var start = new Date();
for(var i = 0, n = ary1.length; i < n; i++){
    ary1[i] = i % 256;
}
console.log(new Date() - start);// 110

var ary2  = new Uint8Array(1000000);
var start = new Date();
for(var i = 0, n = ary2.length; i < n; i++){
    ary2[i] = i % 256;
}
console.log(new Date() - start);// 24

超速です!

The UInt8Array type represents an array of 8-bit unsigned integers.

Uint8Arrayというのは8ビットの符合なし要素の配列らしい!

■符号ありの要素の配列

ということは符号ありの要素の配列もあるはずだ。

var ary3  = new Int8Array(1000000);
var start = new Date();
for(var i = 0, n = ary3.length; i < n; i++){
    ary3[i] = -i % 256;
}
console.log(new Date() - start);// 26

符号ありの方が同じ処理をしても僅かながらに遅い気がした。必要に応じて選択すべし。

ちなみにやってはいけないが符合なしの配列にわざと負の値を入れるとおかしな事になる。

var ary2  = new Uint8Array(1000000);
var start = new Date();
for(var i = 0, n = ary2.length; i < n; i++){
    ary2[i] = -i % 256;
}
console.log(ary2);
//0, 255, 254, ... 

y = 256 – (x % 256)って感じになる。

参考

PHPで並列通信を行う

何年か前のドキュメントが出てきたので一応、書きとめておくよ(「・ω・)「がおー

■コード

以下のようにすることで並列通信を行うことができる。

$i   = 0;
$ch  = array();
$cmh = curl_multi_init();
foreach($urls as $url){
    $ch[$i] = curl_init($url);
    curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch[$i], CURLOPT_FAILONERROR, true);
    curl_setopt($ch[$i], CURLOPT_TIMEOUT, 10);
    curl_multi_add_handle($cmh, $ch[$i]);
    $i++;
}

// execute
$active = null;
do{
    $mrc = curl_multi_exec($cmh, $active);
}
while($mrc === CURLM_CALL_MULTI_PERFORM);
while($active && $mrc === CURLM_OK){
    if(curl_multi_select($cmh) !== false){
        do{
            $mrc = curl_multi_exec($cmh, $active);
        }
        while($mrc === CURLM_CALL_MULTI_PERFORM);
    }
}

//read the data from the result in handler
if($mrc === CURLM_OK){
    for($s = 0; $s < $i; $s++){
        if(curl_error($ch[$s]) == ''){// success
            $contents = curl_multi_getcontent($ch[$s]);
        }
        else{
            var_dump(curl_error($ch[$s]));
        }
    curl_multi_remove_handle($cmh, $ch[$s]);
    curl_close($ch[$s]);
    }
}
curl_multi_close($cmh);

外部のAPIを利用しAPIのレスポンスが低速である場合に特に有用である。但し、同時に開けるソケット数には限りがあるので、接続数がソケット上限数(定数:SOMAXCONN)を超える場合は、上述のロジックを改修する必要がある。

androidに登録されているアカウント情報を取得する

■取得

パーミッション

AccountManagerActivity.java

package info.justoneplanet.android.sample.accountmanager;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class AccountManagerActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Account[] accounts = AccountManager.get(this).getAccounts();
for (Account account : accounts) {
Log.e(“account – ” + account.name, account.type + “[” + account.describeContents() + “]”);
}
}
}

account.nameに実際の値が入っている感じ。

CentOSにMongoDBをインストールする

■インストール

yumリポジトリの追加

以下のコマンドでリポジトリを追加する。

vi /etc/yum.repos.d/mongo.repo

以下のようにファイルの中身を記述する。

[10gen]
name=10gen Repository
baseurl=http://downloads.mongodb.org/distros/centos/5.4/os/i386/
gpgcheck=0

保存したら以下のコマンドを実行する。

yum install mongo-stable*

起動

以下のコマンドで起動してみる。

/etc/init.d/mongod start

以下のコマンドで起動したか確認する。

lsof -i:27017

以下のコマンドで自動起動を設定する。

chkconfig mongod on
chkconfig mongod --list

参考

CentOS and Fedora Packages

amazon linuxにインストールする

wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.0.2.tgz
tar xvzf mongodb-linux-x86_64-2.0.2.tgz
mv mongodb-linux-x86_64-2.0.2 /var/lib/mongodb
mkdir /mnt/db
/var/lib/mongodb/bin/mongod --fork --logpath ~/mongod.log --dbpath /mnt/db/

■PHPから扱う準備

以下のコマンドでpecl_mongoをインストールする。

pecl clear-cache
pecl install mongo

以下のコマンドで設定ファイルを生成する。

cp /etc/php.d/curl.ini /etc/php.d/mongo.ini
vi /etc/php.d/mongo.ini

設定ファイルは以下のような記述だ。

; Enable mongo extension module
extension=mongo.so

設定が終わったらhttpdをreloadする。

/etc/init.d/httpd reload

参考

PHP & MongoDB Sitting in a Tree: Part 1

英語だが非常に参考になるドキュメントだ。

■ベンチマーク

以下のコードを使用してベンチマークしてみた。

$dbh = new Mongo();
$dbh = $dbh->testdb;
$col = $dbh->bench;
$start = microtime(true);
for($i = 0; $i < 1000; $i++){
    $col->insert(
        array(
            'key'   => "key{$i}",
            'value' => "abcdefghijklmnopqrstuvwxyz"
        ),
        true
    );
}
$end = microtime(true);
print($end - $start);0.66766786575317
$dbh->close();

但し、2回目以降は0.2秒以下になる。

データを取り出す

本当にデータが挿入されているのか不安になったので、以下のコードを使用してデータを覗いてみた。

$res = $col->find();
$s = 0;
foreach($res as $doc){
    print("<pre>{$s}");
    var_dump($doc);
    print('</pre>');
    $s++;
}
$dbh->close();

ベンチマークをしておいて言うのもオカシイが、データストアにおいて全く同じ条件というものは再現できないので単純に比較してはならない。用途に応じた選択が必要である。

amazon Linuxにインストールする

amazon Linuxにインストールする場合は以下のようにする。

curl -O  http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.0.2.tgz
tar xzf mongodb-linux-x86_64-2.0.2.tgz
mv mongodb-linux-x86_64-2.0.2 /var/lib/mongodb
cd /var/lib/mongodb/bin
mkdir /mnt/db
./mongod --fork --logpath /var/log/mongod.log --dbpath /mnt/db/