论坛首页 综合技术论坛

erlang轻松实现memcached binary协议

浏览 8931 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-06-04   最后修改:2009-06-04
简单实现了下memcached binary protocol的 get和 set command,体验了下erlang binary语法的强大和方便
代码:

-module(binary_server).
-export([start/0]).

start() ->
	{ok,Listen}=gen_tcp:listen(7777,[binary,{packet,0},{reuseaddr,true},{active,true}]),
	register(kvs,spawn(fun() -> dict() end)),
	accept(Listen).

accept(Listen) ->
	{ok,Socket}=gen_tcp:accept(Listen),
	spawn(fun() -> accept(Listen) end),
	inet:setopts(Socket,[binary,{packet,0},{active,true}]),
	process(Socket,<<>>).

process(Socket,Left) ->
	receive
		{tcp,Socket,Bin} ->
			Buffer=list_to_binary(binary_to_list(Left) ++ binary_to_list(Bin)),
			case regonized(Buffer) of
				{save_ok,RealRemaning} ->
					Resp=[16#81,16#1] ++ fill_all(0,22,[]),
					gen_tcp:send(Socket,list_to_binary(Resp)),
					process(Socket,RealRemaning);

				{get_ok,undefined,Remaining} ->
					Value= <<"not_found">>,
					BodyLen=length(binary_to_list(Value))+4,
					Resp=[16#81] ++ fill_all(0,3,[]) ++[16#4] 
                                        ++ fill_all(0,3,[]) ++ 
					binary_to_list(<<BodyLen:32>>) ++ 
                                        binary_to_list(<<0:128>>) ++ 
                                        binary_to_list(Value),
					gen_tcp:send(Socket,list_to_binary(Resp)),
					process(Socket,Remaining);

				{get_ok,{ok,Value},Remaining} ->
					BodyLen=length(binary_to_list(Value))+4,
					Resp=[16#81] ++ fill_all(0,3,[]) ++[16#4]
                                         ++ fill_all(0,3,[]) ++ 
					binary_to_list(<<BodyLen:32>>) ++ 
                                        binary_to_list(<<0:128>>) ++ 
                                        binary_to_list(Value),
					gen_tcp:send(Socket,list_to_binary(Resp)),
					process(Socket,Remaining);

				{get_timeout,Remaining} ->
					process(Socket,Remaining);

				{not_engouh_streams} ->
					process(Socket,Buffer)
			end;
		{tcp_closed,Socket} ->
			io:format("peer closed~n")
 	end.

regonized(Buffer) ->
	case Buffer of
		%%set command
		<<16#80:8,16#1:8,KeyLen:16/big,
                ExtraLen:8/big,DataType:8,Reserved:16/big,
		BodyLen:32/big,Opaque:32/big,Cas:64/big,
                Extras:64/big,Key:KeyLen/big-binary-unit:8,
                Remaning/binary>> ->
			ValueLen=BodyLen-KeyLen-ExtraLen,
			case Remaning of
				<<Value:ValueLen/binary-unit:8,
                                 RealRemaning/binary>> ->
					%% got completed packet,deal with set command
				 	kvs ! {self(),{store,Key,Value}},	
					{save_ok,RealRemaning};
				<<_/binary>> ->
					%% not enough streams
					{not_enough_streams}
			end;
		%%get command
		<<16#80:8,16#0:8,KeyLen:16/big,16#0:8,
                DataType:8,Reserved:16/big,BodyLen:32/big,
                Opaque:32/big,Cas:64/big,Key:KeyLen/big-binary-unit:8,
                Remaining/binary>> ->
			kvs ! {self(),{lookup,Key}},
			receive 
				{lookup,Value} ->
					{get_ok,Value,Remaining}
		    after 1000 ->
			    {get_timeout,Remaining}		
		    end;
		%% no match
		<<_/binary>> ->
			{not_engouh_streams}
	end.

dict() ->
	receive
		{From,{store,Key,Value}} ->
			put(Key,{ok,Value}),
			dict();
		{From,{lookup,Key}} ->
			From ! {lookup,get(Key)},
			dict()
	end.
debug_print([H|T]) ->
        io:format("~w,",[H]),
        debug_print(T);
debug_print([])->
        ok.

fill_all(C,0,L)->
        L;
fill_all(C,N,L)->
        N1=N-1,
        L1=[C|L],
        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的协议包





   发表时间:2009-06-04  
大牛啊,,,

erlang的语法好怪,,以后一定要学学```
0 请登录后投票
   发表时间:2009-06-04  
willko 写道
大牛啊,,,

erlang的语法好怪,,以后一定要学学```


大牛是yufeng,litaocheng他们,我也准备基础好点了,再去骚扰他们,呵呵
0 请登录后投票
   发表时间:2009-06-04  
我们组的项目核心是用erlang做的,可惜我不会,有空一定要去学学。
0 请登录后投票
   发表时间:2009-06-05  
binary确实很强大 还有bianry list comprehension 不要忘记了哦
0 请登录后投票
   发表时间:2009-06-05  
前段时间也要用Erlang和memcached通信,在网上找了个,写的好复杂,
就放弃了,改成和PHP socket server通信 来解决

你这段代码也没有说明如何散列访问,我们那些memcache都是散列在多台的
0 请登录后投票
   发表时间:2009-06-05  
mryufeng 写道
binary确实很强大 还有bianry list comprehension 不要忘记了哦


好的,回头好好研究下
0 请登录后投票
   发表时间:2009-06-05  
美洲豹 写道
前段时间也要用Erlang和memcached通信,在网上找了个,写的好复杂,
就放弃了,改成和PHP socket server通信 来解决

你这段代码也没有说明如何散列访问,我们那些memcache都是散列在多台的


我就是想试试binary的语法而已,整好拿memcached protocol做个试验而已

这个程序离一个真正的memcached client可差远了,估计有很多地方都是 Don't do this的典范,呵呵
0 请登录后投票
   发表时间:2009-06-06  
andrew913 写道
我们组的项目核心是用erlang做的,可惜我不会,有空一定要去学学。


用于实现什么功能?
0 请登录后投票
   发表时间:2009-06-06  
willko 写道
大牛啊,,,

erlang的语法好怪,,以后一定要学学```

以后我也学学 ~!~
0 请登录后投票
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics