ksauzz weblog

technical note....

Cannot Open Shared Object Fileの対処

Linuxでshared objectが見つからない時の対処方法。いい加減メモっとく。

事象

svnを叩いたら libaprutil-1.so が見つからないと起こられた。

svn: error while loading shared libraries: libaprutil-1.so.0: cannot open shared object file: No such file or directory

ちなみにapacheを/usr/local/apacheへインストールしたのにライブラリの検索パスを通してない事が原因。ということでパスを通しましょう。

対応方法

1. lddでリンクしているライブラリを調べる

やらなくても良いけど、とりあえず眺めとく。

$ ldd `which svn`

-- 略 --

libaprutil-1.so.0 => not found
libapr-1.so.0 => not found
libaprutil-1.so.0 => not found
libapr-1.so.0 => not found
libaprutil-1.so.0 => not found
libapr-1.so.0 => not found
libaprutil-1.so.0 => not found
libapr-1.so.0 => not found
libaprutil-1.so.0 => not found
libapr-1.so.0 => not found
libaprutil-1.so.0 => not found
libapr-1.so.0 => not found
libapr-1.so.0 => not found
libaprutil-1.so.0 => not found
libapr-1.so.0 => not found
libaprutil-1.so.0 => not found
libapr-1.so.0 => not found
libaprutil-1.so.0 => not found
libapr-1.so.0 => not found
libaprutil-1.so.0 => not found
libapr-1.so.0 => not found
libaprutil-1.so.0 => not found
libapr-1.so.0 => not found
libaprutil-1.so.0 => not found
libapr-1.so.0 => not found

はい、確かに見つかってないです。

2. ライブラリの検索パス追加

/etc/ld.so.conf.d/apache.conf を作成して以下を書く。

/usr/local/apache/lib

もしくは /etc/ld.so.conf へ直接追記してもOK.

3. リンクとキャッシュ(/etc/ld.so.cache)の更新

ldconfig実行。

$ ldconfig

-v オプションで詳細が出力されます。

4. 動作確認

パスが通ったか確認。

$ ldconfig -p | grep libapr
libaprutil-1.so.0 (libc6,x86-64) => /usr/local/apache/lib/libaprutil-1.so.0
libaprutil-1.so.0 (libc6) => /usr/lib/libaprutil-1.so.0
libaprutil-1.so (libc6,x86-64) => /usr/local/apache/lib/libaprutil-1.so
libapr-1.so.0 (libc6,x86-64) => /usr/local/apache/lib/libapr-1.so.0
libapr-1.so.0 (libc6) => /usr/lib/libapr-1.so.0
libapr-1.so (libc6,x86-64) => /usr/local/apache/lib/libapr-1.so

はい、認識されました。

svnを実行してみます。

$ svn
使用方法を知りたいときは 'svn help' と打ってください。

無事実行できました。

参考

ZeroMQ Rubyバインディングのインストール

そろそろZeroMQを触ろうか、ということでRubyバインディングをとりあえずインストール。

ZeroMQのRubyバインディングはrbzmqffi-rzmqがあります。 rbzmqは公式のRubyバインディングでffi-rzmqはffi版です。 ffiに関しては、さっぱり詳しくないのですが、 MatzRuby使ってる分には基本rbzmqの方で良いのかなと思ってます。 ffi-rzmqをインストールしたのはlearn-ruby-zeromqを動かしたかった、というだけの理由です。

以下、MacOSでのお話…。

ZeroMQのインストール

とりあえずZeroMQをインストールします。 特になにも考えず現在のStable版である2.2.0を入れました。 今回はソースビルドしていて、インストール先は/usr/local/zmqです。

git clone https://github.com/zeromq/zeromq2-x.git
cd zeromq2-x
git checkout v2.2.0
./autogen.sh
./configure --prefix=/usr/local/zmq
make
sudo make install

rbzmqのインストール

–with-zmq-dirでインストールパスを指定します。

gem install zmq -- --with-zmq-dir=/usr/local/zmq

ffi-rzmqのインストール

gem install ffi-rzmq

echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/zmq/lib" >> ~/.zshrc

/usr/local/zmqへインストールしたので、LD_LIBRARY_PATHの指定をしておかないと実行時にコケます。

インストール作業は以上。

Cowboy clientを使う

ErlangのWebフレームワークCowboyにはcowboy_clientというクライアントモジュールが含まれています。 今回はこのcowboy_clientの基本的な使い方を紹介します。

利用方法

cowboy_clientは以下の流れで利用します。

  1. clientレコードの生成 (init/1)
  2. 接続の確立 (connect/4)
  3. リクエスト送信 (request/3, request/4, request/5)
  4. レスポンスヘッダーの取得 (response/1)
  5. レスポンスの取得 (response_body/1)

これを実際のコードで表現すると、こんな感じです。

1
2
3
4
5
{ok, Client1} = cowboy_client:init([]),
{ok, Client2} = cowboy_client:connect(cowboy_tcp_transport, "localhost", 8080, Client1),
{ok, Client3} = cowboy_client:request(<<"GET">>, <<"/contents.html">>, [], Client2),
{ok, Status, Headers, Client4} = cowboy_client:response(Client3),
{ok, Body, Client5} = cowboy_client:response_body(Client4),

以降でinit/1以外の各行を個別に確認してゆきます。

接続の確立 (connect/4)

Transport, Host, Portを指定してコネクションを生成します。 恐らくSSL通信の場合はcowboy_ssl_transportを利用すると思われます。

1
{ok, Client2} = cowboy_client:connect(cowboy_tcp_transport, "localhost", 8080, Client1),

HTTPリクエスト (request/4)

request関数は3種類用意されています。 リクエストヘッダー、リクエストボディ設定の有無でこれらを使い分けます。

  • request(Method, Url, Client)
  • request(Method, Url, Headers, Client)
  • request(Method, Url, Headers, Data, Client)
1
{ok, Client3} = cowboy_client:request(<<"GET">>, <<"/contents.html">>, [], Client2),

なお、Headerは以下のようなリストで指定します。

1
[{<<"connection">>, <<"close">>}]

HTTPレスポンス (response/1, response_body/1)

ヘッダー、ステータスコードの取得(response/1)とボディの取得(response_body/1)は、個別に行う必要があります。 必要に応じてコールします。

1
2
{ok, Status, Headers, Client4} = cowboy_client:response(Client3),
{ok, Body, _Client5} = cowboy_client:response_body(Client4),

以上がリクエスト送信からレスポンス受信までの流れです。

その他

POSTリクエスト

POSTする場合は、request/4を使用してPOSTメソッドの指定、リクエストボディを指定します。

1
2
3
4
5
Method = <<"POST">>,
Url = <<"/post_content">>,
Headers = [],
Data = <<"key=value">>
{ok, Client3} = cowboy_client:request(Method, Url, Headrs, Data, Client2),

raw_request/2

cowboy_clientでは生のHTTPリクエストを送信するraw_request/2関数が用意されています。 テスト目的でイレギュラーなHTTPリクエストを送信するときに便利です。

1
2
3
4
5
{ok, Client1} = cowboy_client:init([]),
{ok, Client2} = cowboy_client:connect(cowboy_tcp_transport, "localhost", 8080, Client1),
{ok, Client3} = cowboy_client:raw_request(<<"GET /contents.html HTTP/1.1\r\nHost: localhost\r\n\r\n">>, Client2),
{ok, Status, Headers, Client4} = cowboy_client:response(Client3),
{ok, Body, _Client5} = cowboy_client:response_body(Client4),

以上、大雑把なcowboy_clientの紹介でした。

そもそもcowboyのテストを書くために書かれたモジュールのようなので、 基本的にはHTTP周りのテストを書く際に利用すると良いかと思います。

Cowboyメモ

CowboyはErlangの軽量Webアプリケーションフレームワークです。 まだドキュメント化されていない部分が多いので軽くメモ。

基本

基本的な利用方法は某氏のブログをどうぞ。

http://d.hatena.ne.jp/Voluntas/20120407/1333784371

リクエストの処理

cowboyではhandle/2の引数に#http_reqが渡ってきます。 基本的にはこのhttp_reqをcowboy_http_reqの関数へ渡し、リクエストデータの取得、レスポンスデータの生成を行います。

レスポンスの送信

レスポンスの送信にはcowboy_http_req:reply/xを使います。 下記ではステータスコード、レスポンスボディ、レスポンスヘッダーをreply関数へ渡し、 レスポンスを送信しています。

1
2
3
4
5
6
handle(Req, State) ->
    StatusCode  = 200,
    Body = <<"Hello world!!">>,
    Header = [{'Server', [<<"Hello Cowboy Server!!!">>]}],
    {ok, Req2} = cowboy_http_req:reply(StatusCode, Header, Body, Req),
    {ok, Req2, State}.

このコードはcowboy_http_reqモジュールのset_resp_xxx関数を使って以下の様に書くこともできます。 ここで新しく生成されるReq_x_には、セットしたレスポンスボディ、レスポンスヘッダーがセットされています。

1
2
3
4
5
6
7
handle(Req, State) ->
    StatusCode  = 200,
    Body = <<"Hello world!!">>,
    {ok, Req1} = cowboy_http_req:set_resp_body(Body, Req),
    {ok, Req2} = cowboy_http_req:set_resp_header('Server', <<"Hello Cowboy Server!!">>, Req1),
    {ok, Req3} = cowboy_http_req:reply(StatusCode, Req2),
    {ok, Req3, State}.

URL Queryの取得

Queryはcowboy_http_req:qs_vals/1, qs_val/2, qs_val/3で取得できます。

1
2
3
4
5
6
handle(Req, State) ->
    {QsList, Req1} = cowboy_http_req:qs_vals(Req),
    ...
    {Value, Req1} = cowboy_http_req:qs_val(<<"key">>, Req),
    ...
    {ValueOrDefault, Req1} = cowboy_http_req:qs_val(<<"key">>, Req, DefautVal),

Postデータの取得

postしたデータはcowboy_http_req:body_qs/1で取得できます。 データはproplistsで返ってきます。

1
2
3
4
5
6
7
handle(Req, State) ->
    {QsList, Req1} = cowboy_http_req:body_qs(Req),
    case lists:keyfind(<<"key">>, 1, QsList) of
        false ->
            ...,
        {<<"key">>, Value} ->
            ....

Cookie操作

cookieの操作も同様にcowboy_http_req:cookie/2, set_resp_cookie/3を使います。

Cookieの取得

1
2
3
4
5
6
handle(Req, State) ->
    case cowboy_http_req:cookie(Key, Req) of
        {undefined, Req1} ->
            ...
        {SessionId, Req1} ->
            ...

Cookieのセット

1
2
3
handle(Req, State) ->
    {ok,  Req1} = cowboy_http_req:set_resp_cookie(<<"Key">>, <<"Value">>, [max_age, 1000],  Req),
    ...

静的ファイルの処理(cowboy_http_static)

静的ファイルをレスポンスに利用する場合、 cowboy_http_staticが用意されているので、これを使います。

cowboy_http_staticは2012/05/07現在experimentalです。

Dispatch設定

cowboyのlistener起動時に指定するdispatcher設定でcowboy_http_staticをhandlerとして指定します。 そしてOptionにdirectory, mimetypsを指定します。

下記の例では/static/xxxへのアクセスに対してcowboy_http_staticが使用されます。

1
2
3
4
5
6
7
8
9
Dispatch = [
    {'_', [
        {[<<"static">>, '...'], cowboy_http_static, [
                {directory, {priv_dir, application_name, [<<"www">>]}},
                {mimetypes, [
                        {<<".html">>, [<<"text/html">>]},
                        {<<".css">>,  [<<"text/css">>]},
                        {<<".js">>,   [<<"application/javascript">>]}]]}]},
        ...

directoryオプション

静的ファイルのルートディレクトリ。priv_dirを指定すると指定したアプリケーションのprivディレクトリが参照されます。 直接ディレクトリを指定することも可能です。

以下、cowboy_http_staticのedocより

1
2
3
4
5
6
7
8
9
10
11
  %% Serve files from /var/www/ under http://example.com/static/
  {[<<"static">>, '...'], cowboy_http_static,
      [{directory, "/var/www"}]}

  %% Serve files from the current working directory under http://example.com/static/
  {[<<"static">>, '...'], cowboy_http_static,
      [{directory, <<"./">>}]}

  %% Serve files from cowboy/priv/www under http://example.com/
  {['...'], cowboy_http_static,
      [{directory, {priv_dir, cowboy, [<<"www">>]}}]}

mimetypesオプション

静的ファイルの拡張子毎にConten-Typeを指定します。

Content-Typeのデフォルト値がapplication/octet-streamになっているので、 適切な値を設定する必要があります。

spawngrid/mimetypesを使うことでmimetypesを、 以下の様にも書ける様です。(未検証)

1
{mimetypes, {fun mimetypes:path_to_mimes/2, default}}

Gitメモ

Gitのメモ git-svnがメインかも…

git svn

説明 コマンド
標準ディレクトリ構成にてクローン git svn clone -s –prefix=svn/ url
clone時のリビジョン指定(depth) git svn clone -s –prefix=svn/ -r $REV:HEAD url
svnの変更に追従 git svn rebase
svnへコミット git svn dcommit
svnリポジトリの変更を取得 git svn fetch svn
svn側にブランチ作成 git svn branch branch
svn側にタグ打ち git svn tag version (-n dry-run)
svnブランチに紐付くローカルブランチの作成 git branch local-branch remotes/svn/branch
git checkout -b local-branch remotes/svn/branch

merge時の注意

svnブランチを参照しているブランチからmergeするときは--no-ffオプションを使うこと。fast-fowardだとremoteブランチが切り替わる。 誤って実行した場合はreset等で対処。

ローカルブランチのコミットを履歴を維持したままsvnブランチへコミットする

git branch local_branch
// edit & commit
git svn branch remote_branch
git checkout -b svn_sync_branch remotes/svn/remote_branch
git rebase svn_sync_branch
git svn dcommit

git flow

既存プロジェクトの初期化

git clone url
git checkout --track origin/master
git flow init

これをしないとdevelopの変更がlocalのmasterに入ってしまう。

featureコマンド

説明 コマンド
featureブランチ作成 git flow feature start branch
featureブランチ終了(developへのmerge) git flow feature finish branch
featureブランチをpush git flow feature publish branch
featureブランチのpull git flow feature pull origin branch

その他

commit

git commit -m msg
git commit --amend

checkout

git checkout -b branch_name
git checkout -b branch_name origin/branch

rm

remove from index

git rm --cached item

reset

git reset .
git reset --soft HEAD^
git reset --hard HEAD^

rebase

rebase -i HEAD^^^
rebase --onto base_branch current_branch branch

git reflog

説明 コマンド
rebase前の情報を取得 git reflog show branch

git clean

説明 コマンド
.gitignoreを無視 git clean -x
ディレクトリも削除 git clean -d

change author and commiter

git filter-branch --commit-filter '
if [ "$GIT_COMMITTER_NAME" = "old_name" ]; then
    GIT_AUTHOR_NAME="new_author_name"
    GIT_AUTHOR_EMAIL="new_author_email"
    GIT_COMMITTER_NAME="new_comitter_name"
    GIT_COMMITTER_EMAIL="new_commiter_email"
fi
git commit-tree "$@"
' HEAD

参考:http://d.hatena.ne.jp/idesaku/20090908/1252419890

Erlangと周辺技術のメモ

gistのメモを移行

gen_server

trace debug

1
gen_server:start_link({local, ?SERVER}, ?MODULE, [], [{debug,[trace]}])

rebar

dependencies in rebar.config

rebar.config
1
2
3
{deps, [
  {webmachine, "1.9.0", {git, "https://github.com/basho/webmachine.git", {tag,"1.9.0"}}}
]}.

generate skelton codes

  • ./rebar create-app appid=xxx
  • ./rebar create template=simplemod modid=xxx
  • ./rebar create template=simplesrv srvid=xxx_serve

packaging

1
2
3
4
mkdir rel
cd rel
../rebar create-node nodeid=ememcached
../rebar generate

see: https://github.com/basho/rebar/tree/master/priv/templates

dialyzer

Static analysis tool. http://www.erlang.org/doc/man/dialyzer.html

build plt file

dialyzer --build_plt --apps erts kernel stdlib

extend plt file

dialyzer --add_to_plt --apps mnesia

analyse sources

dialyzer --src src/*.erl

typer

Type anotator.

show type

typer -I include src/*.erl

insert type into the erlang codes.

typer --annotate -I include src/*.erl

Erlang/OTPを利用したAcceptor Poolの実装

サーバを実装する際にはpreforkモデル云々でacceptor pool(worker pool?)を実装する事がある。 これをErlang/OTPで実装するにあたり、いくつかのOSSコードを参考にさせて頂いた。 これはその参考にさせて貰ったMochiweb, Kaiのacceptor pool実装に関するメモ。

Mochiweb

MochiwebはMochi Media, Inc.が公開しているのErlang製の軽量Http Server.というかWeb Framework。

mochiwebではproc_libを使ってacceptor poolを実装している。 acceptorがクラッシュしてもsupervisorに頼らずに直接再起動をする実装がなされている。

MochiwebのCall sequence.拡大図

実際にacceptor poolを生成しているのはmochiweb_socket_server:new_acceptor_pool/2。 ここでmochiweb_acceptor経由でproc_lib:spawn_link/3を連続的に実行することでacceptor poolを生成している。

mochiweb_socket_server:new_acceptor_pool/1
1
2
3
4
5
6
7
8
9
10
new_acceptor_pool(Listen,
                  State=#mochiweb_socket_server{acceptor_pool=Pool,
                                                acceptor_pool_size=Size,
                                                loop=Loop}) ->
    F = fun (_, S) ->
                Pid = mochiweb_acceptor:start_link(self(), Listen, Loop),
                sets:add_element(Pid, S)
        end,
    Pool1 = lists:foldl(F, Pool, lists:seq(1, Size)),
    State#mochiweb_socket_server{acceptor_pool=Pool1}.

acceptorがクラッシュした場合にはmochiweb_server_socketでクラッシュのメッセージを受け取り、 ここでacceptorを起動する仕組みになっている。当然、process_flag(trap_exit, true)は事前に呼ばれている。

Kai

国産のErlangで実装されたKVS. Amazon Dynamoクローン。slideshare

Kaiはsupervisorを利用してacceptor poolを実装している。supervisor:start_linkにacceptorの定義をリストにして渡すことで、 supervisorのworkerとしてacceptorを多数起動し、これによりpoolを実現している。当然、acceptorの再起動もSupervisor任せである。

acceptor定義のListを生成しているのは以下のkai_tcp_sup:acceptor_spec/6である。

kai_tcp_server_sup:acceptor_specs/6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
acceptor_specs(
  ListenSocket, State, {Dest, Name}, MonitorBaseName, Mod, Option
) ->
    #tcp_server_option{
        max_processes = MaxProcesses,
        shutdown      = Shutdown
    } = Option,
    MonitorName = case Dest of
      local   -> MonitorBaseName;
      _Global -> {Dest, MonitorBaseName}
    end,
    lists:map(
        fun (N) ->
            AcceptorName = build_acceptor_name(Name, N),
            {
                AcceptorName,
                {
                    kai_tcp_server_acceptor,
                    start_link,
                    [
                        {Dest, AcceptorName},
                        ListenSocket,
                        State,
                        MonitorName,
                        Mod,
                        Option
                    ]
                },
                permanent,
                Shutdown,
                worker,
                []
            }
        end,
        lists:seq(1, MaxProcesses)
    ).

まとめ

Acceptorの生成

  • Mochiweb: proc_libで個別に起動
  • kai: supervisor任せ

Acceptorの再起動

  • Mochiweb: 自前実装
  • kai: supervisor任せ

なんか他のコードも読んだ気がしたけど、すっかり忘れてしまったので以上2つだけのまとめ。

ちなみに自分が実装する際にはKaiの方式を利用させて貰った。Supervisor便利である。

Sbt

簡単にメモ

simple build tool (version 0.11.x)

scalaのビルドツール。 最近のバージョンでは基本的に対話モードで利用する雰囲気。

よく使うコマンド

コマンド名 説明
update ライブラリ(dependenciew)取得
clean cleanup target directories
run main classの検索、一覧表示からの実行
run-main classname   main classを明示的に指定して実行
console scalaの対話型コンソール起動。:quitでconsoleから抜ける
tasks タスク一覧表示
package パッケージ(jar)の生成

コマンドがよく分からなかったらtasksでコマンド一覧をながめて、help引いて使えばなんとかなる。

usage

  1. sbtを起動(基本起動させっぱなし)
  2. consoleコマンドでREPL起動。コードの動作を確認しつつ、別windowで編集(vim)
  3. 書いたらcompileでビルド
  4. run、もしくはrun-mainで実行

という雰囲気で使ってる。

Vimのビルド

ビルドオプションを忘れるのでメモ。mercurialのメモだったりもする。

hg clone https://vim.googlecode.com/hg/ vim
cd vim
hg update v7-3-487
./configure --prefix=/usr/local/vim-7.3.487 --with-features=huge --enable-multibyte --enable-pythoninterp --enable-rubyinterp
make && make install
cd /usr/local
ln -s vim-7.3.487 vim

misc

tagの確認

hg tags

Setup Perlbrew and Cpanminus.

perlbrew

http://perlbrew.pl/

install perlbrew into $HOME/.perlbrew (default install path is $HOME/perl5)

echo "export PERLBREW_ROOT=$HOME/.perlbrew" >> .zshrc
curl -kL http://install.perlbrew.pl | bash
echo "[[ -s ~/.perlbrew/etc/bashrc ]] && source ~/.perlbrew/etc/bashrc" >> .zshrc

perlbrew install perl-5.15.9
perlbrew switch perl-5.15.9

cpanminus

https://github.com/miyagawa/cpanminus

install cpanm

perlbrew install-cpanm

install module

cpanm module_name