2007'11.14 (Wed)
ocaml memcached client 半端バージョン
Ocaml で memcached client を作ってみようと思って作りかけていたのですが、似たような仕組みでOcaml純正の素晴らしげなものがリリースされてしまったので、多分終了。悔しいので、コードだけ晒しておきます。
あんまり良くわかっていないのですが、get/set だけできたらいいよねーというノリで作ったものです。
C#のクライアントとかを眺めると、結構面倒くさそうなことをしてるのですが、きちんとしようとすると色々あるんでしょうね。
socket はリンクするアプリケーションで管理してもらうことになるのですが、C#のクライアントをみると、ライブラリがソケット管理も吸収する作りになっているみたい。
あんまり良くわかっていないのですが、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#のクライアントをみると、ライブラリがソケット管理も吸収する作りになっているみたい。
この記事のトラックバックURL
この記事へのトラックバック
| HOME |