gearmanクライアントライブラリ cl-gearman
December 16, 2012 at 05:30 PM | categories: gearman, common lisp |この投稿はCommon Lisp Libraries Advent Calendar 2012の16日目の記事です。
GearmanをCommon Lispから使いたかったのですが、既存のものが見付からなかったのでcl-gearmanというものをつくってみました。というわけで紹介、もとい宣伝をさせてもらおうと思います。
Gearmanとは
今更感はありますが、Gearman自体について簡単に説明しておきます。
Gearmanとは時間のかかる処理を複数のコンピュータに振り分けるように設計されたオープンソースのアプリケーションフレームワークです。Gearmanを利用するアプリケーションでは、client, job server, workerという3つの要素が存在します。 それぞれの役割は以下の通りです。
- job server: clientから受けとったジョブを適切なworkerに渡す
- client: ジョブを生成してjob serverに送信する
- worker: job serverを経由して、clientによってリクエストされたジョブを実行してそのレスポンスを返す
cl-gearmanは、clientとjob server間、及びworkerとjob server間のプロトコルを抽象化したライブラリです。
余談ですが、Gearmanはデータ永続化をしないものだとずっと思っていたのですがmysqlやsqliteに永続化できます。 ジョブキューを使うなら以前はTheSchwartzという選択肢もありましたが、今はGearmanでほとんどの場合に対応できるのではないでしょうか。
インストール
まだquicklispに登録されていないので、githubからソースコードをダウンロードして下さい。(後日申請予定)
https://github.com/taksatou/cl-gearman
cd ~/quicklisp/local-projects
git clone https://github.com/taksatou/cl-gearman.git
使い方
以下サンプルコードです
client
submit-jobはjob serverにジョブを送信し、workerがそれを完了するまで待機します。workerが処理を完了するとその返り値が文字列で返されます。
CL-USER> (cl-gearman:with-client (client "localhost:4730") (format t "~a~%" (cl-gearman:submit-job client "hello")) (format t "~a~%" (cl-gearman:submit-job client "echo" :arg "foo))) hello foo NIL
以下のようにしてバックグランドでジョブを実行することもできます。バックグランドでジョブを実行するとjobオブジェクトが即座に返されます。 そのjobオブジェクトをつかって、ジョブのステータスを取得することができます。以下の例では見辛いですが、priorityを付けることでジョブの実行順を多少は制御できます。
CL-USER> (cl-gearman:with-client (client "localhost:4730") (let* ((job1 (cl-gearman:submit-background-job client "sleep" :arg "1")) (job2 (cl-gearman:submit-background-job client "sleep" :arg "1" :priority :low)) (job3 (cl-gearman:submit-background-job client "sleep" :arg "1" :priority :high)) (jobs `(:medium ,job1 :low ,job2 :high ,job3))) (dotimes (i 5) (loop for (k v) on jobs by #'cddr do (format t "~a: ~a~%" k (cl-gearman:get-job-status client v))) (sleep 1)))) MEDIUM: (JOB-HANDLE H:hostname:109 IS-KNOWN 1 IS-RUNNING 1 PROGRESS NAN) LOW: (JOB-HANDLE H:hostname:110 IS-KNOWN 1 IS-RUNNING 0 PROGRESS NAN) HIGH: (JOB-HANDLE H:hostname:111 IS-KNOWN 1 IS-RUNNING 0 PROGRESS NAN) MEDIUM: (JOB-HANDLE H:hostname:109 IS-KNOWN 0 IS-RUNNING 0 PROGRESS NAN) LOW: (JOB-HANDLE H:hostname:110 IS-KNOWN 1 IS-RUNNING 0 PROGRESS NAN) HIGH: (JOB-HANDLE H:hostname:111 IS-KNOWN 1 IS-RUNNING 0 PROGRESS NAN) MEDIUM: (JOB-HANDLE H:hostname:109 IS-KNOWN 0 IS-RUNNING 0 PROGRESS NAN) LOW: (JOB-HANDLE H:hostname:110 IS-KNOWN 1 IS-RUNNING 0 PROGRESS NAN) HIGH: (JOB-HANDLE H:hostname:111 IS-KNOWN 1 IS-RUNNING 1 PROGRESS NAN) MEDIUM: (JOB-HANDLE H:hostname:109 IS-KNOWN 0 IS-RUNNING 0 PROGRESS NAN) LOW: (JOB-HANDLE H:hostname:110 IS-KNOWN 1 IS-RUNNING 1 PROGRESS NAN) HIGH: (JOB-HANDLE H:hostname:111 IS-KNOWN 0 IS-RUNNING 0 PROGRESS NAN) MEDIUM: (JOB-HANDLE H:hostname:109 IS-KNOWN 0 IS-RUNNING 0 PROGRESS NAN) LOW: (JOB-HANDLE H:hostname:110 IS-KNOWN 0 IS-RUNNING 0 PROGRESS NAN) HIGH: (JOB-HANDLE H:hostname:111 IS-KNOWN 0 IS-RUNNING 0 PROGRESS NAN) NIL
worker
workerはadd-abilityで実行できるジョブとそのハンドラをあらかじめ登録する必要があります。 ハンドラは2つの引数をとる関数で、第一引数はclientからのデータ、第二引数はjobオブジェクトです。 返り値はprincでフォーマットした文字列としてclientに渡されます。workを実行するとジョブが割り当てられるまでブロックし、1つのジョブの実行が完了すると戻ります。
(cl-gearman:with-worker (worker "localhost:4730") (cl-gearman:add-ability worker "hello" #'(lambda (arg job) "hello")) (cl-gearman:add-ability worker "echo" #'(lambda (arg job) arg)) (cl-gearman:add-ability worker "sleep" #'(lambda (arg job) (sleep (parse-integer arg)) (format nil "job:~A finished~%" job))) (loop do (cl-gearman:work worker)))
エラーハンドリング
ジョブキューを使う上で悩ましいことの一つはエラー処理だと思いますが、Common Lispのコンディションシステムを使えば柔軟に対応できます。 workerのハンドラでは、skip-job, abort-job, retry-job の3パターンのリトライをサポートしています。
例えば、
(cl-gearman:with-worker (worker "localhost:4730") (cl-gearman:add-ability worker "error" #'(lambda (arg job) (error "something wrong"))) (loop do (handler-bind ((error #'(lambda (c) (invoke-restart 'cl-gearman:skip-job)))) (cl-gearman:work worker))))
のようにしておけば、エラーが発生したjobを無視することができます。 abort-jobはclientに失敗を通知します。retry-jobは名前の通りハンドラを再度実行します。
対応する処理系
メインに開発を行った環境は ubuntu 12.04, sbcl 1.0.55.0.debian です。 一応以下の処理系で動作確認しました
- sbcl 1.0.55.0.debian
- clisp 2.49
- clozure cl 1.8-r15286M
ffi系のライブラリには依存してないので比較的動かしやすいとは思いますが、動かなければ教えて下さい。
まとめ
以上、拙作のcl-gearmanを紹介しました。Common Lispを始めてまだ半年足らずなのでおかしいところがあるかと思いますが、フィードバックを頂けるとうれしいです。
ウェブエンジニアのためのオンラインツールまとめ
November 20, 2012 at 02:30 AM | categories: web, tips |The Web engineer's online toolboxというまとめ記事が便利そうだったので、実際に試しつつ抄訳してみました。(一部のコメントと体裁は変えています。)
目次
一覧
RequestBin
httpリクエストを保存するエンドポイントを作ってくれる。
Create a RequestBin のボタンをクリックするとURLが表示されるので、そこをHTTPクライアントからたたくとRequestBin側にリクエスト内容が記録される。 ソースも公開されてるのでローカルで立ちあげることもできる。
githubのwebhookのhelpも参考にどうぞ。
Hurl
httpリクエストを実行してくれる。パーマリンクも作ってくれるので、POSTリクエストもコピペで他の人と共有できる。
- 類似サービス: REST test test , Apigee console
httpbin
HTTPリクエスト側でレスポンスのHTTP status codeやレスポンスやリダイレクト、cookieなどを制御できる。HTTPクライアントのテストに便利。
- 類似サービス: UrlEcho
REDbot
HTTPのリソースをチェックしてくれる。問題を検出したら改善案のサジェストもしてくれる。
- 類似サービス: HTTP lint
WebGun
webhookを簡単に作るためのAPIを提供している。現在はまだベータっぽい。
Apify
ウェブページをスクレイピングしてJSON API化してくれる。 CSSセレクタかxpathに名前をつけて設定するだけでよしなにやってくれる。
Unicorn
前述のREDbotはHTTPをチェックしてくれるけど、こちらはHTMLドキュメントがW3Cに準拠してるかをバリデートしてくれる。
- 類似サービス: HTML lint
Feed validator
こちらはRSSとATOMフィードをバリデートしてくれる。
Link checker
リンクを再帰的にたどって、重複やリンク切れをチェックしてくれる。
Host tracker
ウェブサイトのモニタリングサービス。定期的にpingして、問題があった場合はメール通知してくれる。
Pingdom Full page test
ウェブページのロード時間を計測、解析してくれる。リクエストを実行するサーバは米国しかないので国内だとちょっと使いづらい。 類似サービスのWeb page testならTokyoが選べる。
HAR viewer
HTTP tracking toolsで生成されたHTTP Archive (HAR)形式のログをビジュアライズしてくれる。
CORS proxy
クロスドメインでjsを実行できるようにヘッダを追加してレスポンスしてくれるプロキシ。
Browserling
ブラウザ上で動くインタラクティブなクロスブラウザ。メジャーなブラウザは大体サポートされている。 動作は重いけど表示確認くらいになら無料のままでも使えそう。
WebSocket Echo Test
WebSocketをテストできるエコーサーバ。
YQL
ウェブサービスからとってきたデータをSQLっぽい言語で整形できる。
Yahoo Pipes
ウェブサービスをGUIでマッシュアップできる。
Apiary
良い感じのREST APIドキュメントをインタラクティブなインスペクタをつかってジェネレートできる。まだベータ。
- 類似サービス: Swagger
おわりに
- いくつかのツールはサーバに負荷をかけてしまう可能性があるので注意してつかってください。
- 他にもなにかあれば教えてもらえるとうれしいです。
Quicklinks 3
November 14, 2012 at 11:30 PM | categories: quicklinks |最近はUnityを使う機会を得たのでゲーム関連技術についていろいろ調べてました。
Shadow Algorithms for Real-time Rendering
シェーダーを扱った講義資料らしい。この講義を元にした本がでていて日本語翻訳もあるみたいです。 モデリングは絵心がないとちょっと難しそうだけど、こういうのであれば自分で書いてみたいです。
black-tie
ノイズ生成アルゴリズムというものに興味がわきました。残念ながらこのライブラリはあまりメンテはされてない。
物理演算系ライブラリ
2次元剛体のシュミレーションならChipmunkとかBox2Dあたりで気軽にできそうですが、3次元のやわらかいもの表現するにはちょっとハードルがあがるようです。
物理演算を駆使した3次元リアルぷよぷよがやってみたいです。
mixamo
こういう素材がもっと増えるといいですね。
cl-frontcodingをつくってみた
November 10, 2012 at 08:30 PM | categories: algorithms, common lisp |先週くらいに読んだWEB+DB PRESS Vol.42 に載っていたFront Coding という圧縮アルゴリズムを実装してみました。
http://d.hatena.ne.jp/naoya/20080914/1221382329 のパクりです。
ソースはhttps://github.com/taksatou/cl-frontcodingです。
学習目的でつくったので実用的なライブラリではないですが、CLOSやマクロを使いつつパッケージ作成〜テストまで一通りやりました。
CLOSもマクロも基本的な使い方をするだけなら意外と簡単でした。
CLOSとかマクロは本を読んでもいまいちよくわからない上に、深淵なイメージが膨らんで心理的ハードルがあがってしまうだけなので、よくわからないなりになにか作ってみると理解が進んでいいと思います。
Common Lispのdeclareについて
November 09, 2012 at 11:00 PM | categories: common lisp |Common Lispのdeclareについて調べた。 変数の型や式に関することをコンパイラに伝えてやるために使うものらしい。
例えば、
(defun add (x y) (+ x y))
は
(defun add (x y) (declare (fixnum x y)) (the fixnum (+ x y)))
とすれば、引数と戻り値の型を指定できる。
さらに、
(defun add (x y) (declare (optimize (speed 3) (safety 0))) (declare (fixnum x y)) (the fixnum (+ x y)))
とすればさらに最適化される。ここまですると、Cで実装した場合と遜色ないくらいになるらしい。
ためしにdisassembleしてみた。処理系はsbcl 1.0.55.0。
最初のバージョンは以下のようになった。すでにある程度最適化されてる? LEA(load effective address)命令は、srcオペランドのアドレスを計算し、そのアドレスをdestオペランドにロードするというものらしい。(参考)
CL-USER> (defun add (x y) (+ x y)) CL-USER> (disassemble #'add) ; disassembly for ADD ; 03C43D5D: 488B55F8 MOV RDX, [RBP-8] ; no-arg-parsing entry point ; 61: 488B7DF0 MOV RDI, [RBP-16] ; 65: 4C8D1C25E0010020 LEA R11, [#x200001E0] ; GENERIC-+ ; 6D: 41FFD3 CALL R11 ; 70: 480F42E3 CMOVB RSP, RBX ; 74: 488BE5 MOV RSP, RBP ; 77: F8 CLC ; 78: 5D POP RBP ; 79: C3 RET ; 7A: CC0A BREAK 10 ; error trap ; 7C: 02 BYTE #X02 ; 7D: 18 BYTE #X18 ; INVALID-ARG-COUNT-ERROR ; 7E: 54 BYTE #X54 ; RCX
2番目の、型だけを宣言したものは以下のようになった。うーむ、確かに型チェックっぽいコードは増えてるが速くなってるわけではなさそうな感じ。あとで調べる。
CL-USER> (defun add (x y) (declare (fixnum x y)) (the fixnum (+ x y))) CL-USER> (disassemble #'add) ; disassembly for ADD ; 039D3B73: 488BD1 MOV RDX, RCX ; no-arg-parsing entry point ; 76: 48D1FA SAR RDX, 1 ; 79: 488BC7 MOV RAX, RDI ; 7C: 48D1F8 SAR RAX, 1 ; 7F: 4801C2 ADD RDX, RAX ; 82: 48B80000000000000040 MOV RAX, 4611686018427387904 ; 8C: 4801D0 ADD RAX, RDX ; 8F: 48C1E83F SHR RAX, 63 ; 93: 7509 JNE L0 ; 95: 48D1E2 SHL RDX, 1 ; 98: 488BE5 MOV RSP, RBP ; 9B: F8 CLC ; 9C: 5D POP RBP ; 9D: C3 RET ; 9E: L0: 486BC202 IMUL RAX, RDX, 2 ; A2: 710E JNO L1 ; A4: 488BC2 MOV RAX, RDX ; A7: 4C8D1C25B0050020 LEA R11, [#x200005B0] ; ALLOC-SIGNED-BIGNUM-IN-RAX ; AF: 41FFD3 CALL R11 ; B2: L1: 488B1557FFFFFF MOV RDX, [RIP-169] ; 'FIXNUM ; B9: CC0A BREAK 10 ; error trap ; BB: 03 BYTE #X03 ; BC: 1F BYTE #X1F ; OBJECT-NOT-TYPE-ERROR ; BD: 15 BYTE #X15 ; RAX ; BE: 95 BYTE #X95 ; RDX ; BF: CC0A BREAK 10 ; error trap ; C1: 02 BYTE #X02 ; C2: 18 BYTE #X18 ; INVALID-ARG-COUNT-ERROR ; C3: 54 BYTE #X54 ; RCX ; C4: CC0A BREAK 10 ; error trap ; C6: 02 BYTE #X02 ; C7: 08 BYTE #X08 ; OBJECT-NOT-FIXNUM-ERROR ; C8: 95 BYTE #X95 ; RDX ; C9: CC0A BREAK 10 ; error trap ; CB: 04 BYTE #X04 ; CC: 08 BYTE #X08 ; OBJECT-NOT-FIXNUM-ERROR ; CD: FED501 BYTE #XFE, #XD5, #X01 ; RDI
最後の最適化の宣言をつけたものは以下のようになった。たしかに速そうにはなった。
CL-USER> (defun add (x y) (declare (optimize (speed 3) (safety 0))) (declare (fixnum x y)) (the fixnum (+ x y))) CL-USER> (disassemble #'add) ; disassembly for ADD ; 034DD10F: 4801FA ADD RDX, RDI ; no-arg-parsing entry point ; 12: 488BE5 MOV RSP, RBP ; 15: F8 CLC ; 16: 5D POP RBP ; 17: C3 RET
ついでにCで書いた以下の関数もdisassebleしてみた。
int add(int x, int y) { return x + y; }
cc -c a.c && objdump -d a.o
とすると、
a.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <add>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 89 7d fc mov %edi,-0x4(%rbp) 7: 89 75 f8 mov %esi,-0x8(%rbp) a: 8b 45 f8 mov -0x8(%rbp),%eax d: 8b 55 fc mov -0x4(%rbp),%edx 10: 01 d0 add %edx,%eax 12: 5d pop %rbp 13: c3 retq
cc -O3 -c a.c && objdump -d a.o
なら、
a.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <add>: 0: 8d 04 37 lea (%rdi,%rsi,1),%eax 3: c3 retq
まあとりあえず単純に命令数だけでいうと、 素のSBCL > 素のGCC > 最適化したSBCL > 最適化したGCC という感じにはなったので、CよりLispのほうが速い(場合もある)というふれこみは嘘ではなさそうですね。
About Me
![]() |
mojavy |
Recent posts
95/5 Mbps とは
(August 30, 2015 at 04:22 PM)組み込み用プログラミング言語のパフォーマンス比較
(April 21, 2015 at 01:10 AM)最近読んだ本
(April 05, 2015 at 01:23 PM)Phabricatorを使ったワークフローについて
(March 02, 2015 at 08:55 PM)dnsimpleでダイナミックDNSをつかう
(December 23, 2014 at 08:02 PM)www2014のアドテク関連のResearch Trackメモ
(October 06, 2014 at 09:05 PM)flappymacs がMELPAに登録されました
(July 16, 2014 at 01:07 AM)EmacsでFlappy Birdっぽいもの書きました
(July 10, 2014 at 08:01 PM)
Recent Popular posts
Popular posts
Categories
- C (rss) (3)
- R (rss) (1)
- adtech (rss) (1)
- advent calendar (rss) (2)
- algorithms (rss) (2)
- android (rss) (2)
- aws (rss) (1)
- blog (rss) (2)
- blogofile (rss) (3)
- books (rss) (1)
- c++ (rss) (1)
- chef (rss) (4)
- common lisp (rss) (10)
- debian (rss) (2)
- dns (rss) (1)
- elasticsearch (rss) (1)
- elf (rss) (1)
- elisp (rss) (1)
- emacs (rss) (5)
- english (rss) (1)
- game (rss) (2)
- gearman (rss) (1)
- git (rss) (1)
- github (rss) (1)
- gitlab (rss) (1)
- golang (rss) (2)
- history (rss) (1)
- impress.js (rss) (1)
- internet (rss) (1)
- ios (rss) (3)
- jekyll (rss) (1)
- jenkins (rss) (1)
- linux (rss) (4)
- lisp (rss) (2)
- ltsv (rss) (1)
- lua (rss) (1)
- mac (rss) (3)
- mach-o (rss) (1)
- memo (rss) (2)
- mustache (rss) (1)
- note (rss) (1)
- objective-c (rss) (4)
- os (rss) (1)
- osx (rss) (2)
- others (rss) (1)
- paco (rss) (1)
- pdf (rss) (1)
- php (rss) (2)
- postfix (rss) (1)
- programming (rss) (12)
- project management (rss) (1)
- python (rss) (5)
- quicklinks (rss) (6)
- raspberry pi (rss) (2)
- redmine (rss) (1)
- reveal.js (rss) (1)
- ruby (rss) (10)
- sbcl (rss) (2)
- security (rss) (1)
- shell (rss) (2)
- smtp (rss) (1)
- solr (rss) (1)
- statistics (rss) (2)
- tips (rss) (10)
- tmux (rss) (3)
- toml (rss) (1)
- tools (rss) (1)
- twitter (rss) (1)
- ubuntu (rss) (1)
- unix (rss) (5)
- v8 (rss) (1)
- web (rss) (7)
- xcode (rss) (1)
- zeromq (rss) (2)
Archives
- August 2015 (1)
- April 2015 (2)
- March 2015 (1)
- December 2014 (1)
- October 2014 (1)
- July 2014 (3)
- March 2014 (6)
- February 2014 (4)
- November 2013 (3)
- October 2013 (4)
- September 2013 (2)
- July 2013 (2)
- June 2013 (2)
- May 2013 (1)
- April 2013 (6)
- March 2013 (3)
- February 2013 (8)
- January 2013 (5)
- December 2012 (1)
- November 2012 (6)
- October 2012 (7)
- August 2012 (1)
- July 2012 (9)
- June 2012 (1)
- April 2012 (1)
- December 2011 (2)
- November 2011 (2)