erlang轻松实现memcached binary协议

简单实现了下memcached binary protocol的 get和 set command,体验了下erlang binary语法的强大和方便
代码:

  1. -module(binary_server).  
  2. -export([start/0]).  
  3.   
  4. start() ->  
  5.     {ok,Listen}=gen_tcp:listen(7777,[binary,{packet,0},{reuseaddr,true},{active,true}]),  
  6.     register(kvs,spawn(fun() -> dict() end)),  
  7.     accept(Listen).  
  8.   
  9. accept(Listen) ->  
  10.     {ok,Socket}=gen_tcp:accept(Listen),  
  11.     spawn(fun() -> accept(Listen) end),  
  12.     inet:setopts(Socket,[binary,{packet,0},{active,true}]),  
  13.     process(Socket,<<>>).  
  14.   
  15. process(Socket,Left) ->  
  16.     receive  
  17.         {tcp,Socket,Bin} ->  
  18.             Buffer=list_to_binary(binary_to_list(Left) ++ binary_to_list(Bin)),  
  19.             case regonized(Buffer) of  
  20.                 {save_ok,RealRemaning} ->  
  21.                     Resp=[16#81,16#1] ++ fill_all(0,22,[]),  
  22.                     gen_tcp:send(Socket,list_to_binary(Resp)),  
  23.                     process(Socket,RealRemaning);  
  24.   
  25.                 {get_ok,undefined,Remaining} ->  
  26.                     Value= <<"not_found">>,  
  27.                     BodyLen=length(binary_to_list(Value))+4,  
  28.                     Resp=[16#81] ++ fill_all(0,3,[]) ++[16#4]   
  29.                                         ++ fill_all(0,3,[]) ++   
  30.                     binary_to_list(<<BodyLen:32>>) ++   
  31.                                         binary_to_list(<<0:128>>) ++   
  32.                                         binary_to_list(Value),  
  33.                     gen_tcp:send(Socket,list_to_binary(Resp)),  
  34.                     process(Socket,Remaining);  
  35.   
  36.                 {get_ok,{ok,Value},Remaining} ->  
  37.                     BodyLen=length(binary_to_list(Value))+4,  
  38.                     Resp=[16#81] ++ fill_all(0,3,[]) ++[16#4]  
  39.                                          ++ fill_all(0,3,[]) ++   
  40.                     binary_to_list(<<BodyLen:32>>) ++   
  41.                                         binary_to_list(<<0:128>>) ++   
  42.                                         binary_to_list(Value),  
  43.                     gen_tcp:send(Socket,list_to_binary(Resp)),  
  44.                     process(Socket,Remaining);  
  45.   
  46.                 {get_timeout,Remaining} ->  
  47.                     process(Socket,Remaining);  
  48.   
  49.                 {not_engouh_streams} ->  
  50.                     process(Socket,Buffer)  
  51.             end;  
  52.         {tcp_closed,Socket} ->  
  53.             io:format("peer closed~n")  
  54.     end.  
  55.   
  56. regonized(Buffer) ->  
  57.     case Buffer of  
  58.         %%set command  
  59.         <<16#80:8,16#1:8,KeyLen:16/big,  
  60.                 ExtraLen:8/big,DataType:8,Reserved:16/big,  
  61.         BodyLen:32/big,Opaque:32/big,Cas:64/big,  
  62.                 Extras:64/big,Key:KeyLen/big-binary-unit:8,  
  63.                 Remaning/binary>> ->  
  64.             ValueLen=BodyLen-KeyLen-ExtraLen,  
  65.             case Remaning of  
  66.                 <<Value:ValueLen/binary-unit:8,  
  67.                                  RealRemaning/binary>> ->  
  68.                     %% got completed packet,deal with set command  
  69.                     kvs ! {self(),{store,Key,Value}},     
  70.                     {save_ok,RealRemaning};  
  71.                 <<_/binary>> ->  
  72.                     %% not enough streams  
  73.                     {not_enough_streams}  
  74.             end;  
  75.         %%get command  
  76.         <<16#80:8,16#0:8,KeyLen:16/big,16#0:8,  
  77.                 DataType:8,Reserved:16/big,BodyLen:32/big,  
  78.                 Opaque:32/big,Cas:64/big,Key:KeyLen/big-binary-unit:8,  
  79.                 Remaining/binary>> ->  
  80.             kvs ! {self(),{lookup,Key}},  
  81.             receive   
  82.                 {lookup,Value} ->  
  83.                     {get_ok,Value,Remaining}  
  84.             after 1000 ->  
  85.                 {get_timeout,Remaining}       
  86.             end;  
  87.         %% no match  
  88.         <<_/binary>> ->  
  89.             {not_engouh_streams}  
  90.     end.  
  91.   
  92. dict() ->  
  93.     receive  
  94.         {From,{store,Key,Value}} ->  
  95.             put(Key,{ok,Value}),  
  96.             dict();  
  97.         {From,{lookup,Key}} ->  
  98.             From ! {lookup,get(Key)},  
  99.             dict()  
  100.     end.  
  101. debug_print([H|T]) ->  
  102.         io:format("~w,",[H]),  
  103.         debug_print(T);  
  104. debug_print([])->  
  105.         ok.  
  106.   
  107. fill_all(C,0,L)->  
  108.         L;  
  109. fill_all(C,N,L)->  
  110.         N1=N-1,  
  111.         L1=[C|L],  
  112.         fill_all(C,N1,L1).  



说明:
1. 16#80:8  
   16#代表16进制,80是memcached协议头的第一个字节 80代表request
   具体协议内容memcached官网上有很详尽的解释
2. KeyLen:16/big 
   协议中key的长度是占2个字节 ,16是bit数 .
   因为多于一个字节的数据在存储和传输时就会涉及字节序问题,
   这里big代表的是大端/网络字节序,
   因为我的测试client在传输数据前已经将数据转成网络字节序,所以这里接收必须是big
3. Key:KeyLen/big-binary-unit:8 ,
   这里Key具体的字节数是由之前得到的KeyLen指定的,所以表示为Key:KeyLen,
   因为这里的单位应该是字节,所以需要指定为unit:8,其实这里指定了binary
   binary类型默认的unit就是8,即 实际的size = KeyLen * unit 个 bit
4. lookup没有找到指定key对应value时,这里没有按协议处理,以not_found为value返回给client,实际协议是返回一个status为非0的协议包

原文地址:http://www.iteye.com/topic/401737

posted on 2013-06-02 11:20  应无所住而生其心  阅读(254)  评论(0)    收藏  举报

导航