2010年02月 / 01月≪ 12345678910111213141516171819202122232425262728≫03月

2007'11.14 (Wed)

ocaml memcached client 半端バージョン

 Ocaml で memcached client を作ってみようと思って作りかけていたのですが、似たような仕組みでOcaml純正の素晴らしげなものがリリースされてしまったので、多分終了。悔しいので、コードだけ晒しておきます。

 あんまり良くわかっていないのですが、get/set だけできたらいいよねーというノリで作ったものです。

 C#のクライアントとかを眺めると、結構面倒くさそうなことをしてるのですが、きちんとしようとすると色々あるんでしょうね。


open Unix

type operation_result =
| Timeout of float
| Failed of string
| Connected
| SetOK
| GetOK of string
;;

let (@@) f x = f x
;;

let flush () =
flush Pervasives.stdout
;;

let spf = Printf.sprintf
;;

let primary_buffer = String.create 8192
;;

let input_buffer = Buffer.create 8192
;;

let add_crlf data =
spf "%s\r\n" data
;;

let set_cmd key flgs exp len =
add_crlf @@ spf "set %s %d %d %d" key flgs exp len
;;

let get_cmd key =
add_crlf @@ spf "get %s" key
;;

let read_line sock =
let s1 = String.create 1 in
let buff = Buffer.create 1024 in
let rec iter () =
let nr = Unix.read sock s1 0 1 in
match nr, s1 with
| 1, "\r" ->
iter ()
| 1, "\n" ->
Buffer.contents buff
| 1, other ->
Buffer.add_string buff s1;
iter ()
| _, _ ->
Buffer.contents buff in
iter ()
;;

let read_bytes sock bytes =
let () = Buffer.clear input_buffer in
let rec iter rest =
if rest > 0 then
let nr = Unix.read sock primary_buffer 0 rest in
let () = Buffer.add_substring input_buffer primary_buffer 0 nr in
iter (rest - nr)
else
Buffer.contents input_buffer in
iter bytes
;;

let start_connect sock addr =
let ch = Event.new_channel () in
let _ =
Thread.create
(fun () ->
Unix.connect sock addr;
Event.sync @@ Event.send ch Connected)
() in

Event.receive ch
;;

let start_set sock key data =
let ch = Event.new_channel () in
let _ =
Thread.create
(fun () ->
let cmd = set_cmd key 0 0 (String.length data) in
let _ = Unix.write sock cmd 0 (String.length cmd) in
let data_crlf = add_crlf data in
let _ = Unix.write sock data_crlf 0 (String.length data_crlf) in
match read_line sock with
| "STORED" -> Event.sync (Event.send ch SetOK)
| line -> Event.sync (Event.send ch @@ (Failed (spf "set(invalid rsp %s)" line))))
() in

Event.receive ch
;;

let start_get sock key =
let ch = Event.new_channel () in
let _ =
Thread.create
(fun () ->
let cmd = get_cmd key in
let _ = Unix.write sock cmd 0 (String.length cmd) in
let line = read_line sock in
match Str.split (Str.regexp " ") line with
| ["VALUE"; k; flags; bytes] ->
let data = read_bytes sock @@ int_of_string bytes in
let _ = read_line sock in (* \r\n *)
let _ = read_line sock in (* END *)
Event.sync (Event.send ch (GetOK data))
| _ ->
Event.sync (Event.send ch (Failed(spf "get(invalid rsp %s)" line))))
() in

Event.receive ch
;;

let start_wdt second =
let ch = Event.new_channel () in
let _ =
Thread.create
(fun () ->
Thread.delay second;
Event.sync (Event.send ch (Timeout second)))
() in

Event.receive ch
;;

let wait_event time channel =
Event.sync @@ Event.choose [channel; start_wdt time]
;;

let connect ?(timeout=3.00) sock addr =
wait_event timeout (start_connect sock addr)
;;

let set ?(timeout=3.00) sock ~key ~value =
wait_event timeout (start_set sock key value)
;;

let get ?(timeout=3.00) sock key =
wait_event timeout (start_get sock key)
;;


socket はリンクするアプリケーションで管理してもらうことになるのですが、C#のクライアントをみると、ライブラリがソケット管理も吸収する作りになっているみたい。
10:00  |  プログラミング  |  TB(0)  |  CM(0)  |  EDIT  |  Top↑

Comment

コメントを投稿する


 管理者だけに表示  (現在非公開コメント投稿不可)

▲PageTop

Trackback

この記事のトラックバックURL

→http://tategakibunko.blog83.fc2.com/tb.php/170-a648c2c9

この記事にトラックバックする(FC2ブログユーザー)

この記事へのトラックバック

▲PageTop

 | HOME |