├── .gitignore ├── Makefile ├── README.md ├── include └── ssdb.hrl ├── rebar ├── rebar.config └── src ├── ssdb.app.src ├── ssdb.erl ├── ssdb_app.erl ├── ssdb_conn.erl ├── ssdb_demo.erl ├── ssdb_pool.erl └── ssdb_sup.erl /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .rebar/ 3 | deps/ 4 | ebin/ 5 | doc/ 6 | *~ 7 | *.iml 8 | *.dump 9 | *.beam 10 | *.log 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT = ssdb-client 2 | 3 | REBAR = "./rebar" 4 | 5 | all: app 6 | 7 | # Application. 8 | 9 | deps: 10 | @$(REBAR) get-deps 11 | 12 | app: deps 13 | @$(REBAR) compile 14 | 15 | clean: 16 | @$(REBAR) clean 17 | rm -f erl_crash.dump 18 | 19 | docs: clean-docs 20 | @$(REBAR) doc skip_deps=true 21 | 22 | clean-docs: 23 | rm -f doc/*.css 24 | rm -f doc/*.html 25 | rm -f doc/*.png 26 | rm -f doc/edoc-info 27 | 28 | .PHONY: deps 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ssdb-client 2 | 3 | Erlang SSDB Client. 4 | 5 | #How To Use 6 | 7 | make sure ssdb-server is opening. 8 | 9 | compile code use make 10 | 11 | $ make 12 | 13 | $ erl -pa ebin -pa deps/goldrush/ebin/ -pa deps/lager/ebin 14 | 15 | 1> ssdb:start("localhost",8888). 16 | 17 | {ok,<0.36.0>} 18 | 19 | 2> 20 | ssdb:query([set,a,<<"this is a test">>]). 21 | 22 | {ok,1} 23 | 24 | 3> ssdb:query([get,a]). 25 | 26 | {ok,<<"this is a test">>} 27 | 28 | 4> ssdb:query([hset,h,k,<<"test hset">>]). 29 | 30 | {ok,1} 31 | 32 | 5> ssdb:query([hget,h,k]). 33 | 34 | {ok,<<"test hset">>} 35 | 36 | more detail see src/ssdb_demo.erl 37 | 38 | #Api 39 | You can start ssdb client by ssdb:start/0,ssdb:start/2,ssdb:start/3,ssdb:start/4 or ssdb:start/5 40 | 41 | ssdb:start(Host,Port,PoolSize,Password,IsReconnect) 42 | 43 | Host = list() default:"localhsot" 44 | 45 | Port = integer() default:8888 46 | 47 | PoolSize = integer() default:5 48 | 49 | Password = list() default:undefined 50 | 51 | IsReconned = 0/1 default 0 is auto reconnect to server when socket closed 52 | 53 | 54 | use client api ssdb:query(Cmd),pass args by list 55 | 56 | Cmd = atom | ssdb_cmd() 57 | 58 | ssdb_cmd = list() eg. [get,a],[set,a,<<"abc">>],[hget,h,test] ... 59 | -------------------------------------------------------------------------------- /include/ssdb.hrl: -------------------------------------------------------------------------------- 1 | -define(PRINT(MSG), lager:info("~p ~w " ++ MSG ++ " ~n", [?MODULE,?LINE])). 2 | -define(PRINT(MSG,INFO), lager:info("~p ~w " ++ MSG ++ " ~n", [?MODULE,?LINE] ++ INFO)). 3 | 4 | 5 | -define(SSDB_TCP_OPTS,[binary,{active,true},{packet,0},{keepalive,true}]). 6 | 7 | -define(SSDB_STEP_SIZE,0). 8 | -define(SSDB_STEP_DATA,1). 9 | -define(SSDB_STEP_FINISH,2). 10 | 11 | -define(SSDB_RECONNECT,1). 12 | -define(SSDB_RECONNECTING,1). 13 | 14 | -define(SSDB_SERVER,ssdb_pool). 15 | -define(SSDB_MAX_RECONNECT_TIME,(300 * 1000)). 16 | 17 | -record(ssdb_conn,{ 18 | socket, 19 | step = 0, 20 | data = <<>>, 21 | size = 0, %% current data size 22 | reply = [], %% buffer of reply 23 | from_list = [] %% FIFO query 24 | }). 25 | 26 | 27 | -record(ssdb_pool,{conn_pools = {[],[]}, %% {Unused Pools,Used Pools} 28 | pool_size = 5, 29 | host, 30 | port, 31 | password, 32 | is_reconnect = 0, %% 0/1 33 | reconnect_time = 2, %% seconds reconnection 34 | is_reconnecting = 0 35 | }). 36 | -------------------------------------------------------------------------------- /rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kqqsysu/ssdb-erlang/158b5b494794f241eded66a1ad04d97e9303975b/rebar -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [{parse_transform, lager_transform}]}. 2 | 3 | {deps, [ 4 | {lager, ".*", {git, "git://github.com/basho/lager", {tag, "3.1.0"}}} 5 | ]}. 6 | -------------------------------------------------------------------------------- /src/ssdb.app.src: -------------------------------------------------------------------------------- 1 | {application, ssdb, [ 2 | {description, "SSDB erlang client"}, 3 | {vsn, "1.0"}, 4 | {registered, []}, 5 | {applications, [kernel, stdlib]}, 6 | {mod, {ssdb_app, []}} 7 | ]}. 8 | -------------------------------------------------------------------------------- /src/ssdb.erl: -------------------------------------------------------------------------------- 1 | %%% @author kqqsysu@gmail.com 2 | %%% @copyright (C) 2015, kongqingquan 3 | %%% @doc SSDB erlang client 4 | %%% @end 5 | %%% Created : 2015.05.01 6 | %%%------------------------------------------------------------------- 7 | 8 | -module(ssdb). 9 | -author("kqqsysu@gmail.com"). 10 | 11 | -include("ssdb.hrl"). 12 | 13 | -export([start/0,start/2,start/3,start/4,start/5,start/6]). 14 | -export([query/1, query/2, query/3]). 15 | 16 | 17 | 18 | 19 | start()-> 20 | start("localhost",8888). 21 | start(Host,Port) -> 22 | PoolSize = 5, 23 | start(Host,Port,PoolSize). 24 | start(Host,Port,PoolSize)-> 25 | Password = undefined, 26 | start(Host,Port,PoolSize,Password). 27 | start(Host,Port,PoolSize,Password)-> 28 | IsReconnect = 0, 29 | start(Host,Port,PoolSize,Password,IsReconnect). 30 | start(Host,Port,PoolSize,Password,IsReconnect) -> 31 | application:ensure_started(ssdb), 32 | ssdb_sup:start_pool(?SSDB_SERVER,Host,Port,PoolSize,Password,IsReconnect). 33 | start(PoolName, Host, Port, PoolSize, Password, IsReconnect) -> 34 | application:ensure_started(ssdb), 35 | ssdb_sup:start_pool(PoolName, Host, Port, PoolSize, Password, IsReconnect). 36 | 37 | query(Cmd) -> 38 | query(?SSDB_SERVER,Cmd). 39 | query(Pid,[Cmd | _] = CmdList) -> 40 | Res = ssdb_pool:query(Pid,CmdList), 41 | AtomCmd = to_atom(Cmd), 42 | parse_res(AtomCmd,Res); 43 | query(Pid,Cmd) -> 44 | ssdb_pool:query(Pid,[Cmd]). 45 | query(Pid, [Cmd | _] = CmdList, Timeout) -> 46 | Res = ssdb_pool:query(Pid, CmdList, Timeout), 47 | AtomCmd = to_atom(Cmd), 48 | parse_res(AtomCmd, Res); 49 | query(Pid, Cmd, Timeout) -> 50 | ssdb_pool:query(Pid, [Cmd], Timeout). 51 | 52 | parse_res(Cmd,Res) when Cmd == 'zavg' -> 53 | case Res of 54 | [<<"ok">>,Avg] -> 55 | try 56 | {ok,binary_to_float(Avg)} 57 | catch 58 | _:Exception -> 59 | {error,Exception} 60 | end; 61 | _ -> 62 | ?PRINT("Query ~p Error:~p",[Cmd,Res]), 63 | Res 64 | end; 65 | parse_res(Cmd,Res) when Cmd == 'get'; Cmd == 'substr'; Cmd == 'getset'; Cmd == 'hget'; Cmd == 'qget'; Cmd == 'qfront'; Cmd == 'qback' -> 66 | case Res of 67 | [<<"ok">>,Data] -> 68 | {ok,Data}; 69 | [<<"not_found">>] -> 70 | {not_found,null}; 71 | _ -> 72 | ?PRINT("Query ~p Error:~p",[Cmd,Res]), 73 | Res 74 | end; 75 | parse_res(Cmd,Res) when Cmd == 'qpop';Cmd == 'qpop_front';Cmd == 'qpop_back' -> 76 | case Res of 77 | [<<"ok">> | Data] -> 78 | {ok,Data}; 79 | [<<"not_found">>]-> 80 | {not_found,null}; 81 | _ -> 82 | ?PRINT("Query ~p Error:~p",[Cmd,Res]), 83 | Res 84 | end; 85 | parse_res(Cmd,Res) when Cmd == 'keys';Cmd == 'zkeys';Cmd == 'hkeys';Cmd == 'hlist';Cmd == 'zlist';Cmd == 'qslice' -> 86 | case Res of 87 | [<<"ok">> | T] -> 88 | {ok,T}; 89 | _ -> 90 | ?PRINT("Query ~p Error:~p",[Cmd,Res]), 91 | Res 92 | end; 93 | parse_res(Cmd,Res) when Cmd == 'auth';Cmd == 'exists';Cmd == 'hexists';Cmd == 'zexists' -> 94 | case Res of 95 | [<<"ok">>,Data] -> 96 | try 97 | {ok,binary_to_integer(Data)} 98 | catch 99 | _:Exception -> 100 | {error,Exception} 101 | end; 102 | _ -> 103 | ?PRINT("Query ~p Error:~p",[Cmd,Res]), 104 | Res 105 | end; 106 | 107 | parse_res(Cmd,Res) when Cmd == 'multi_exists';Cmd == 'multi_hexists'; Cmd == 'multi_zexists'-> 108 | case Res of 109 | [<<"ok">> | Data] -> 110 | try 111 | Return = parse_mutil_return(Data,[]), 112 | {ok,Return} 113 | catch 114 | _:Exception -> 115 | {error,Exception} 116 | end; 117 | _ -> 118 | ?PRINT("Query ~p Error:~p",[Cmd,Res]), 119 | Res 120 | end; 121 | 122 | parse_res(Cmd,Res) when Cmd == 'scan';Cmd == 'rscan';Cmd == 'zscan';Cmd == 'zrscan';Cmd == 'zrange';Cmd == 'zrrange';Cmd == 'hscan';Cmd == 'hrscan';Cmd == 'hgetall';Cmd == 'multi_hsize';Cmd == 'multi_zsize';Cmd == 'multi_get';Cmd == 'multi_hget';Cmd == 'multi_zget';Cmd == 'zpop_front';Cmd == 'zpop_back' -> 123 | case Res of 124 | [<<"ok">> | Data] -> 125 | case atom_to_list(Cmd) of 126 | ["z" | _] -> 127 | {ok,parse_mutil_return(Data,[])}; 128 | _ -> 129 | {ok,parse_mutil_return2(Data,[])} 130 | end; 131 | _ -> 132 | Res 133 | end; 134 | %%% Cmd: 'dbsize','ping','qset','getbit','setbit','countbit','strlen','set','setx','setnx','zset','hset','qpush','qpush_front','qpush_back','qtrim_front','qtrim_back','del','zdel','hdel','hsize','zsize','qsize','hclear','zclear','qclear','multi_set','multi_del','multi_hset','multi_hdel','multi_zset','multi_zdel','incr','decr','zincr','zdecr','hincr','hdecr','zget','zrank','zrrank','zcount','zsum','zremrangebyrank','zremrangebyscore' 135 | parse_res(Cmd,Res) -> 136 | case Res of 137 | [<<"ok">>] -> 138 | {ok,0}; 139 | [<<"ok">>,Data] -> 140 | try 141 | {ok,binary_to_integer(Data)} 142 | catch 143 | _:Exception -> 144 | {error,Exception} 145 | end; 146 | [<<"not_found">>]-> 147 | {not_found,null}; 148 | _ -> 149 | ?PRINT("Query ~p Error:~p",[Cmd,Res]), 150 | Res 151 | end. 152 | 153 | parse_mutil_return([Key,IsExist | T],Res) -> 154 | NewRes = [{Key,binary_to_integer(IsExist)} | Res], 155 | parse_mutil_return(T,NewRes); 156 | parse_mutil_return([],Res) -> 157 | lists:reverse(Res). 158 | 159 | parse_mutil_return2([Key,Val | T],Res) -> 160 | NewRes = [{Key,Val} | Res], 161 | parse_mutil_return2(T,NewRes); 162 | parse_mutil_return2([],Res) -> 163 | lists:reverse(Res). 164 | 165 | 166 | 167 | to_atom(Cmd) when is_atom(Cmd) -> 168 | Cmd; 169 | to_atom(Cmd) when is_list(Cmd) -> 170 | list_to_atom(Cmd); 171 | to_atom(Cmd) when is_binary(Cmd) -> 172 | binary_to_atom(Cmd,utf8). 173 | 174 | -------------------------------------------------------------------------------- /src/ssdb_app.erl: -------------------------------------------------------------------------------- 1 | -module(ssdb_app). 2 | 3 | -behaviour(application). 4 | -export([ 5 | start/2, 6 | stop/1 7 | ]). 8 | 9 | 10 | start(_, _) -> 11 | ssdb_sup:start_link(). 12 | 13 | stop(_) -> 14 | ok. 15 | 16 | -------------------------------------------------------------------------------- /src/ssdb_conn.erl: -------------------------------------------------------------------------------- 1 | %%% @author kqqsysu@gmail.com 2 | %%% @copyright (C) 2015, kongqingquan 3 | %%% @doc SSDB erlang client. 4 | %%% @end 5 | %%% Created : 2015.05.01 6 | %%%------------------------------------------------------------------- 7 | 8 | -module(ssdb_conn). 9 | -author("kqqsysu@gmail.com"). 10 | 11 | -include("ssdb.hrl"). 12 | 13 | -export([init/1,handle_cast/2,handle_call/3,handle_info/2,terminate/2,code_change/3]). 14 | 15 | -export([start_link/2]). 16 | 17 | start_link(Host,Port) -> 18 | gen_server:start_link(?MODULE,[Host,Port],[]). 19 | 20 | init([Host,Port]) -> 21 | case gen_tcp:connect(Host, Port, ?SSDB_TCP_OPTS) of 22 | {ok, Socket} -> 23 | State = #ssdb_conn{socket = Socket,step = ?SSDB_STEP_FINISH}, 24 | {ok,State}; 25 | Error -> 26 | ?PRINT("Connect Errror:~p",[Error]), 27 | {stop,Error} 28 | end. 29 | 30 | handle_cast(Msg,State)-> 31 | ?PRINT("Not handle msg:~p",[Msg]), 32 | {noreply,State}. 33 | 34 | handle_call({ssdb_query,Cmd},From,State = #ssdb_conn{from_list = FromList})-> 35 | NewState = State#ssdb_conn{from_list = FromList ++ [{From,Cmd}]}, 36 | NewState2 = send_query(NewState), 37 | {noreply,NewState2}; 38 | 39 | handle_call(Msg,From,State)-> 40 | ?PRINT("Not handle msg from:~p ~p",[From,Msg]), 41 | {reply,error,State}. 42 | 43 | handle_info({ssdb_query,From,Cmd},State = #ssdb_conn{from_list = FromList})-> 44 | NewState = State#ssdb_conn{from_list = FromList ++ [{From,Cmd}]}, 45 | NewState2 = send_query(NewState), 46 | {noreply,NewState2}; 47 | 48 | handle_info({tcp,Socket,Data},State = #ssdb_conn{socket = Socket})-> 49 | case parse_recv(State,Data) of 50 | #ssdb_conn{step = ?SSDB_STEP_FINISH,reply = Reply,from_list = [{From,_Cmd} | FromList2]} = NewState -> 51 | gen_server:reply(From,Reply), 52 | NewState2 = NewState#ssdb_conn{from_list = FromList2,reply = []}, 53 | NewState3 = send_query(NewState2), 54 | {noreply,NewState3}; 55 | #ssdb_conn{} = NewState -> 56 | {noreply,NewState}; 57 | error -> 58 | {stop,packet_error,State} 59 | end; 60 | handle_info({tcp_closed,Socket},State = #ssdb_conn{socket = Socket})-> 61 | ?PRINT("ssdb closed"), 62 | {stop,tcp_clised,State}; 63 | 64 | handle_info({tcp_error,Socket,Reason},State = #ssdb_conn{socket = Socket})-> 65 | ?PRINT("ssdb socket error reason:~p",[Reason]), 66 | {stop,tcp_clised,State}; 67 | 68 | handle_info(Msg,State)-> 69 | ?PRINT("Not handle msg:~p",[Msg]), 70 | {noreply,State}. 71 | 72 | terminate(Reason,_State)-> 73 | ?PRINT("trminate reason:~p",[Reason]), 74 | ok. 75 | 76 | code_change(_OldVsn,State,_Extra) -> 77 | {ok,State}. 78 | 79 | %% parse packet 80 | parse_recv(#ssdb_conn{step = ?SSDB_STEP_SIZE,data = Data} = State,InData) -> 81 | NewData = <>, 82 | case binary:split(NewData,<<"\n">>) of 83 | [BinSize,BlockData] -> 84 | Size = erlang:binary_to_integer(BinSize), 85 | NewState = State#ssdb_conn{data = BlockData,size = Size,step = ?SSDB_STEP_DATA}, 86 | parse_recv(NewState,<<>>); %% parse data 87 | _ -> 88 | %% wait for get size 89 | State#ssdb_conn{data = NewData} 90 | end; 91 | parse_recv(#ssdb_conn{step = ?SSDB_STEP_DATA,data = Data,size = Size,reply = Reply} = State,InData) -> 92 | NewData = <>, 93 | case NewData of 94 | <> -> 95 | case Res of 96 | <<>> -> 97 | %% packet finish 98 | NewReply = lists:reverse([BlockData | Reply]), 99 | State#ssdb_conn{step = ?SSDB_STEP_FINISH,size = 0,data = <<>>,reply = NewReply}; 100 | _ -> 101 | ?PRINT("parse packet error"), 102 | error 103 | end; 104 | <> -> 105 | NewReply = [BlockData | Reply], 106 | NewState = State#ssdb_conn{step = ?SSDB_STEP_SIZE,size = 0,data = Res,reply = NewReply}, 107 | parse_recv(NewState,<<>>); 108 | _ -> 109 | State#ssdb_conn{data = NewData} 110 | end. 111 | 112 | 113 | %% send_packet 114 | send_query(#ssdb_conn{step = ?SSDB_STEP_FINISH,socket = Socket, from_list = [{_From,Cmd} | _]} = State) -> 115 | Cmd2 = encode_cmd(Cmd,<<>>), 116 | gen_tcp:send(Socket,Cmd2), 117 | State#ssdb_conn{step = ?SSDB_STEP_SIZE}; 118 | send_query(#ssdb_conn{} = State) -> 119 | State. 120 | 121 | encode_cmd([H | T],Cmd)-> 122 | H2 = to_binary(H), 123 | Size = size(H2), 124 | Cmd2 = <>, 125 | encode_cmd(T,Cmd2); 126 | encode_cmd([],Cmd) -> 127 | <>. 128 | 129 | to_binary(Msg) when is_binary(Msg) -> Msg; 130 | to_binary(Msg) when is_atom(Msg) -> 131 | list_to_binary(atom_to_list(Msg)); 132 | to_binary(Msg) when is_list(Msg) -> 133 | list_to_binary(Msg); 134 | to_binary(Msg) when is_integer(Msg) -> 135 | integer_to_binary(Msg); 136 | to_binary(Msg) when is_float(Msg) -> 137 | float_to_binary(Msg); 138 | to_binary(Msg) when is_tuple(Msg) -> 139 | list_to_binary(tuple_to_list(Msg)); 140 | to_binary(_Msg) -> throw(other_value). 141 | 142 | -------------------------------------------------------------------------------- /src/ssdb_demo.erl: -------------------------------------------------------------------------------- 1 | %%% @author kqqsysu@gmail.com 2 | %%% @copyright (C) 2015, kongqingquan 3 | %%% @doc SSDB erlang client 4 | %%% @end 5 | %%% Created : 2015.05.05 6 | %%%------------------------------------------------------------------- 7 | 8 | -module(ssdb_demo). 9 | -author("kqqsysu@gmail.com"). 10 | 11 | -include("ssdb.hrl"). 12 | 13 | -export([test/0,test/2,test/5]). 14 | 15 | test() -> 16 | test("localhost",8888). 17 | test(Host,Port) -> 18 | PoolSize = 5, 19 | Password = undefind, 20 | IsReconnect = 1, 21 | test(Host,Port,PoolSize,Password,IsReconnect). 22 | 23 | 24 | test(Host,Port,PoolSize,Password,IsReconnect) -> 25 | %% connect to ssdb server 26 | ssdb:start(Host,Port,PoolSize,Password,IsReconnect), 27 | 28 | %% Key Value 29 | {ok,1} = ssdb:query([del,<<"ssdb_get_key">>]), 30 | {not_found,null} = ssdb:query([get,<<"ssdb_get_key">>]), 31 | {ok,1} = ssdb:query([set,<<"ssdb_get_key">>,<<"ssdb_set_value">>]), 32 | {ok,<<"ssdb_set_value">>} = ssdb:query([get,<<"ssdb_get_key">>]), 33 | 34 | %% Hashmap 35 | {ok,_} = ssdb:query([hdel,h,<<"ssdb_hash_key">>]), 36 | {not_found,null} = ssdb:query([hget,h,<<"ssdb_hash_key">>]), 37 | {ok,_} = ssdb:query([hset,h,<<"ssdb_hash_key">>,<<"ssdb_hashset_value">>]), 38 | {ok,<<"ssdb_hashset_value">>} = ssdb:query([hget,h,<<"ssdb_hash_key">>]), 39 | 40 | 41 | %% Sorted Set 42 | {ok,_} = ssdb:query([zdel,z,<<"ssdb_zset_key">>]), 43 | {not_found,null} = ssdb:query([zget,z,<<"ssdb_zset_key">>]), 44 | {ok,_} = ssdb:query([zset,z,<<"ssdb_zset_key">>,100]), 45 | {ok,100} = ssdb:query([zget,z,<<"ssdb_zset_key">>]), 46 | 47 | 48 | %% List 49 | {ok,_} = ssdb:query([qpush_front,q,<<"ssdb_qlist_value1">>,<<"ssdb_qlist_value2">>]), 50 | {ok,[<<"ssdb_qlist_value2">>,<<"ssdb_qlist_value1">>]} = ssdb:query([qpop_front,q,2]), 51 | 52 | ok. 53 | -------------------------------------------------------------------------------- /src/ssdb_pool.erl: -------------------------------------------------------------------------------- 1 | %%% @author kqqsysu@gmail.com 2 | %%% @copyright (C) 2015, kongqingquan 3 | %%% @doc SSDB erlang client 4 | %%% @end 5 | %%% Created : 2015.05.01 6 | %%%------------------------------------------------------------------- 7 | 8 | -module(ssdb_pool). 9 | -author("kqqsysu@gmail.com"). 10 | 11 | -include("ssdb.hrl"). 12 | 13 | -export([init/1,handle_cast/2,handle_call/3,handle_info/2,terminate/2,code_change/3]). 14 | 15 | -export([start_link/6, query/2, query/3]). 16 | 17 | -define(SSDB_QUERY_TIMEOUT,(60 * 1000)). %% 60 seconds 18 | 19 | start_link(PoolName, Host, Port, PoolSize, Password, IsReconnect)-> 20 | gen_server:start_link({local, PoolName}, ?MODULE, [Host, Port, PoolSize, Password, IsReconnect], []). 21 | 22 | query(Pid,Cmd) -> 23 | gen_server:call(Pid,{ssdb_query,Cmd},?SSDB_QUERY_TIMEOUT). 24 | query(Pid, Cmd, Timeout) -> 25 | gen_server:call(Pid, {ssdb_query,Cmd}, Timeout). 26 | 27 | init([Host,Port,PoolSize,Password,IsReconnect]) -> 28 | process_flag(trap_exit,true), 29 | Conns = start_pools(Host,Port,Password,PoolSize), 30 | State = #ssdb_pool{host = Host,port = Port,pool_size = PoolSize,is_reconnect = IsReconnect,conn_pools = {Conns,[]}}, 31 | {ok,State}. 32 | 33 | handle_cast(Msg,State)-> 34 | ?PRINT("Not handle msg:~p",[Msg]), 35 | {noreply,State}. 36 | 37 | handle_call({ssdb_query,Cmd},From,State) -> 38 | case get_conn(State) of 39 | {ok,Conn,NewState} -> 40 | Conn ! {ssdb_query,From,Cmd}, 41 | {noreply,NewState}; 42 | error -> 43 | {reply,error,State} 44 | end; 45 | 46 | handle_call(Msg,From,State)-> 47 | ?PRINT("Not handle msg from:~p ~p",[From,Msg]), 48 | {reply,error,State}. 49 | 50 | handle_info({'EXIT', From, Reason},State = #ssdb_pool{conn_pools = {Unused,Used},is_reconnect = IsReconnect,is_reconnecting = IsReconnecting}) -> 51 | ?PRINT("ssdb_conn exit:From:~p,Reason:~p",[From,Reason]), 52 | NewUnused = lists:delete(From,Unused), 53 | NewUsed = lists:delete(From,Used), 54 | NewState = State#ssdb_pool{conn_pools = {NewUnused,NewUsed}}, 55 | case IsReconnect == ?SSDB_RECONNECT andalso IsReconnecting /= ?SSDB_RECONNECTING of 56 | true -> 57 | self() ! reconnect; 58 | false -> 59 | ok 60 | end, 61 | {noreply,NewState}; 62 | 63 | handle_info(reconnect,State = #ssdb_pool{host = Host,port = Port,password = Password,reconnect_time = ReconnectTime,conn_pools = {Unused,Used},pool_size = PoolSize}) -> 64 | ConnectedSize = length(Unused) + length(Used), 65 | try 66 | NewConns = start_pools(Host,Port,Password,PoolSize - ConnectedSize), 67 | NewConnPools = {NewConns ++ Unused,Used}, 68 | NewState = 69 | case length(NewConns) + ConnectedSize >= PoolSize of 70 | true -> 71 | ?PRINT("reconnect success,poolsize:~w",[PoolSize]), 72 | State#ssdb_pool{conn_pools = NewConnPools,is_reconnecting = 0,reconnect_time = 2}; 73 | false -> 74 | %% reconnect 75 | NewReconnectTime = min(?SSDB_MAX_RECONNECT_TIME,ReconnectTime * 2), 76 | erlang:send_after(NewReconnectTime * 1000,self(),reconnect), 77 | State#ssdb_pool{conn_pools = NewConnPools,is_reconnecting = ?SSDB_RECONNECTING,reconnect_time = NewReconnectTime} 78 | end, 79 | {noreply,NewState} 80 | catch 81 | _Err:_Exp -> 82 | ReconnectTime2 = min(?SSDB_MAX_RECONNECT_TIME,ReconnectTime * 2), 83 | erlang:send_after(ReconnectTime2 * 1000,self(),reconnect), 84 | {noreply,State#ssdb_pool{reconnect_time = ReconnectTime2,is_reconnecting = ?SSDB_RECONNECTING}} 85 | end; 86 | 87 | handle_info(Msg,State)-> 88 | ?PRINT("Not handle msg:~p",[Msg]), 89 | {noreply,State}. 90 | 91 | terminate(Reason,_State)-> 92 | ?PRINT("trminate reason:~p",[Reason]), 93 | ok. 94 | 95 | code_change(_OldVsn,State,_Extra) -> 96 | {ok,State}. 97 | 98 | %% get next conn 99 | get_conn(#ssdb_pool{conn_pools = {[],[]}} = _State) -> 100 | error; 101 | get_conn(#ssdb_pool{conn_pools = {Unused,Used}} = State) -> 102 | case Unused of 103 | [] -> 104 | [Conn | Unused2] = lists:reverse(Used), 105 | NewState = State#ssdb_pool{conn_pools = {Unused2,[Conn]}}, 106 | {ok, Conn,NewState}; 107 | [Conn|Unused2] -> 108 | NewState = State#ssdb_pool{conn_pools = {Unused2,[Conn | Used]}}, 109 | {ok,Conn,NewState} 110 | end. 111 | 112 | start_pools(Host,Port,Password,PoolSize) -> 113 | lists:foldl(fun(_,AccIn) -> 114 | {ok,Pid} = ssdb_conn:start_link(Host,Port), 115 | case Password of 116 | undefined -> 117 | ok; 118 | _ -> 119 | [<<"ok">>,<<"1">>] = gen_server:call(Pid,{ssdb_query,[auth,Password]}) 120 | end, 121 | [Pid | AccIn] 122 | end,[],lists:seq(1,PoolSize)). 123 | -------------------------------------------------------------------------------- /src/ssdb_sup.erl: -------------------------------------------------------------------------------- 1 | %%% @author kqqsysu@gmail.com 2 | %%% @copyright (C) 2015, kongqingquan 3 | %%% @doc SSDB erlang client 4 | %%% @end 5 | %%% Created : 2015.05.01 6 | %%%------------------------------------------------------------------- 7 | 8 | -module(ssdb_sup). 9 | -author("kqqsysu@gmail.com"). 10 | 11 | -include("ssdb.hrl"). 12 | 13 | -export([start_link/0,init/1,start_pool/6]). 14 | 15 | start_link() -> 16 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 17 | 18 | init([]) -> 19 | {ok, {{one_for_one, 3, 10}, []}}. 20 | 21 | start_pool(PoolName, Host, Port, PoolSize, Password, IsReconnect) -> 22 | supervisor:start_child(?MODULE, 23 | {PoolName, {ssdb_pool, start_link, [PoolName, Host, Port, PoolSize, Password, IsReconnect]}, transient, 24 | 5000, worker, [ssdb_pool]}). 25 | --------------------------------------------------------------------------------