|
|
- -module(agTcpAgency).
- -include("agHttpCli.hrl").
-
- -compile(inline).
- -compile({inline_size, 512}).
-
- -export([
- %% 内部行为API
- start_link/3,
- init_it/3,
- system_code_change/4,
- system_continue/3,
- system_get_state/1,
- system_terminate/4,
- init/1,
- handle_msg/4,
- terminate/2
- ]).
-
- -record(srvState, {
- initOpts :: initOpts(),
- ip :: inet:ip_address() | inet:hostname(),
- name :: serverName(),
- pool_name :: pool_name(),
- port :: inet:port_number(),
- reconnect_state :: undefined | reconnect_state(),
- socket :: undefined | inet:socket(),
- socket_options :: [gen_tcp:connect_option()],
- timer_ref :: undefined | reference()
- }).
-
- -record(cliState, {
- initOpts :: initOpts(),
- ip :: inet:ip_address() | inet:hostname(),
- name :: serverName(),
- pool_name :: pool_name(),
- port :: inet:port_number(),
- reconnect_state :: undefined | reconnect_state(),
- socket :: undefined | inet:socket(),
- socket_options :: [gen_tcp:connect_option()],
- timer_ref :: undefined | reference()
- }).
-
- -type state() :: #srvState {}.
-
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genActor start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
- -spec start_link(module(), atom(), term(), [proc_lib:spawn_option()]) -> {ok, pid()}.
- start_link(ServerName, Args, SpawnOpts) ->
- proc_lib:start_link(?MODULE, init_it, [ServerName, self(), Args], infinity, SpawnOpts).
-
- init_it(ServerName, Parent, Args) ->
- case safeRegister(ServerName) of
- true ->
- process_flag(trap_exit, true),
- moduleInit(Parent, Args);
- {false, Pid} ->
- proc_lib:init_ack(Parent, {error, {already_started, Pid}})
- end.
-
- %% sys callbacks
- -spec system_code_change(term(), module(), undefined | term(), term()) -> {ok, term()}.
- system_code_change(MiscState, _Module, _OldVsn, _Extra) ->
- {ok, MiscState}.
-
- -spec system_continue(pid(), [], {module(), atom(), pid(), term()}) -> ok.
- system_continue(_Parent, _Debug, {Parent, SrvState, CliState}) ->
- loop(Parent, SrvState, CliState).
-
- -spec system_get_state(term()) -> {ok, term()}.
- system_get_state({_Parent, SrvState, _CliState}) ->
- {ok, SrvState}.
-
- -spec system_terminate(term(), pid(), [], term()) -> none().
- system_terminate(Reason, _Parent, _Debug, {_Parent, SrvState, CliState}) ->
- terminate(Reason, SrvState, CliState).
-
- safeRegister(Name) ->
- try register(Name, self()) of
- true -> true
- catch
- _:_ -> {false, whereis(Name)}
- end.
-
- moduleInit(Parent, Args) ->
- case ?MODULE:init(Args) of
- {ok, SrvState, CliState} ->
- proc_lib:init_ack(Parent, {ok, self()}),
- loop(Parent, SrvState, CliState);
- {stop, Reason} ->
- proc_lib:init_ack(Parent, {error, Reason}),
- exit(Reason)
- end.
-
- loop(Parent, SrvState, CliState) ->
- receive
- {system, From, Request} ->
- sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {Parent, SrvState, CliState});
- {'EXIT', Parent, Reason} ->
- terminate(Reason, SrvState, CliState);
- Msg ->
- {ok, NewSrvState, NewCliState} = ?MODULE:handleMsg(Msg, SrvState, CliState),
- loop(Parent, NewSrvState, NewCliState)
- end.
-
- terminate(Reason, SrvState, CliState) ->
- ?MODULE:terminate(Reason, SrvState, CliState),
- exit(Reason).
-
- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genActor end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
- -spec init(clientOpts()) -> no_return().
- init(ClientOpts) ->
- self() ! ?MSG_CONNECT,
-
- %%ok = shackle_backlog:new(Name),
-
- InitOptions = ?GET_FROM_LIST(initOpts, ClientOpts, ?DEFAULT_INIT_OPTS),
- Protocol = ?GET_FROM_LIST(protocol, ClientOpts, ?DEFAULT_PROTOCOL),
- Ip = ?GET_FROM_LIST(ip, ClientOpts, ?DEFAULT_IP),
- Port = ?GET_FROM_LIST(port, ClientOpts, ?DEFAULT_PORTO(Protocol)),
- ReconnectState = agAgencyUtils:initReconnectState(ClientOpts),
- SocketOptions = ?GET_FROM_LIST(socketOpts, ClientOptions, ?DEFAULT_SOCKET_OPTS),
- {ok, #srvState{initOpts = InitOptions, ip = Ip, port = Port, reconnect_state = ReconnectState, socket_options = SocketOptions}, undefined}.
-
- -spec handleMsg(term(), {state(), client_state()}) -> {ok, term()}.
- handleMsg({_, #request{} = Cast}, #srvState{socket = undefined, name = Name} = SrvState, CliState) ->
- agAgencyUtils:agencyReply(Name, {error, no_socket}, Cast),
- {ok, {SrvState, CliState}};
- handleMsg({Request, #request{timeout = Timeout} = Cast},
- #srvState{name = Name, pool_name = PoolName, socket = Socket} = State,
- ClientState) ->
- try agNetCli:handleRequest(Request, ClientState) of
- {ok, ExtRequestId, Data, ClientState2} ->
- case gen_tcp:send(Socket, Data) of
- ok ->
- Msg = {timeout, ExtRequestId},
- TimerRef = erlang:send_after(Timeout, self(), Msg),
- shackle_queue:add(ExtRequestId, Cast, TimerRef),
- {ok, {State, ClientState2}};
- {error, Reason} ->
- ?WARN(PoolName, "send error: ~p", [Reason]),
- gen_tcp:close(Socket),
- agAgencyUtils:agencyReply(Name, {error, socket_closed}, Cast),
- close(State, ClientState2)
- end
- catch
- ?EXCEPTION(E, R, Stacktrace) ->
- ?WARN(PoolName, "handleRequest crash: ~p:~p~n~p~n",
- [E, R, ?GET_STACK(Stacktrace)]),
- agAgencyUtils:agencyReply(Name, {error, client_crash}, Cast),
- {ok, {State, ClientState}}
- end;
- handleMsg({tcp, Socket, Data},
- #srvState{name = Name, pool_name = PoolName, socket = Socket} = SrvState,
- CliState) ->
-
- try agNetCli:handleData(Data, CliState) of
- {ok, Replies, ClientState2} ->
- agAgencyUtils:agencyResponses(Replies, Name),
- {ok, SrvState, ClientState2};
- {error, Reason, ClientState2} ->
- ?WARN(PoolName, "handleData error: ~p", [Reason]),
- gen_tcp:close(Socket),
- close(State, ClientState2)
- catch
- ?EXCEPTION(E, R, Stacktrace) ->
- ?WARN(PoolName, "handleData crash: ~p:~p~n~p~n",
- [E, R, ?GET_STACK(Stacktrace)]),
- gen_tcp:close(Socket),
- close(State, ClientState)
- end;
- handleMsg({timeout, ExtRequestId}, {#srvState{
- name = Name
- } = State, ClientState}) ->
-
- case shackle_queue:remove(Name, ExtRequestId) of
- {ok, Cast, _TimerRef} ->
- agAgencyUtils:agencyReply(Name, {error, timeout}, Cast);
- {error, not_found} ->
- ok
- end,
- {ok, {State, ClientState}};
- handleMsg({tcp_closed, Socket}, {#srvState{
- socket = Socket,
- pool_name = PoolName
- } = State, ClientState}) ->
-
- ?WARN(PoolName, "connection closed", []),
- close(State, ClientState);
- handleMsg({tcp_error, Socket, Reason}, {#srvState{
- socket = Socket,
- pool_name = PoolName
- } = State, ClientState}) ->
-
- ?WARN(PoolName, "connection error: ~p", [Reason]),
- gen_tcp:close(Socket),
- close(State, ClientState);
- handleMsg(?MSG_CONNECT, {#srvState{
- client = Client,
- initOpts = Init,
- ip = Ip,
- pool_name = PoolName,
- port = Port,
- reconnect_state = ReconnectState,
- socket_options = SocketOptions
- } = State, ClientState}) ->
-
- case connect(PoolName, Ip, Port, SocketOptions) of
- {ok, Socket} ->
- ClientState2 = agHttpProtocol:bin_patterns(),
- ReconnectState2 = agAgencyUtils:resetReconnectState(ReconnectState),
-
- {ok, {State#srvState{
- reconnect_state = ReconnectState2,
- socket = Socket
- }, ClientState2}};
- {error, _Reason} ->
- reconnect(State, ClientState)
- end;
- handleMsg(Msg, {#srvState{
- pool_name = PoolName
- } = State, ClientState}) ->
-
- ?WARN(PoolName, "unknown msg: ~p", [Msg]),
- {ok, {State, ClientState}}.
-
- -spec terminate(term(), term()) ->
- ok.
-
- terminate(_Reason, {#srvState{
- client = Client,
- name = Name,
- pool_name = PoolName,
- timer_ref = TimerRef
- }, ClientState}) ->
-
- agAgencyUtils:cancel_timer(TimerRef),
- try agNetCli:terminate(ClientState)
- catch
- ?EXCEPTION(E, R, Stacktrace) ->
- ?WARN(PoolName, "terminate crash: ~p:~p~n~p~n",
- [E, R, ?GET_STACK(Stacktrace)])
- end,
- agAgencyUtils:agencyReplyAll(Name, {error, shutdown}),
- shackle_backlog:delete(Name),
- ok.
-
- %% private
- close(#srvState{name = Name} = State, ClientState) ->
- agAgencyUtils:agencyReplyAll(Name, {error, socket_closed}),
- reconnect(State, ClientState).
-
- connect(PoolName, Ip, Port, SocketOptions) ->
- case inet:getaddrs(Ip, inet) of
- {ok, Addrs} ->
- Ip2 = agMiscUtils:randomElement(Addrs),
- case gen_tcp:connect(Ip2, Port, SocketOptions,
- ?DEFAULT_CONNECT_TIMEOUT) of
- {ok, Socket} ->
- {ok, Socket};
- {error, Reason} ->
- ?WARN(PoolName, "connect error: ~p", [Reason]),
- {error, Reason}
- end;
- {error, Reason} ->
- ?WARN(PoolName, "getaddrs error: ~p", [Reason]),
- {error, Reason}
- end.
-
- reconnect(State, undefined) ->
- reconnect_timer(State, undefined);
- reconnect(#srvState{
- client = Client,
- pool_name = PoolName
- } = State, ClientState) ->
-
- try agNetCli:terminate(ClientState)
- catch
- ?EXCEPTION(E, R, Stacktrace) ->
- ?WARN(PoolName, "terminate crash: ~p:~p~n~p~n",
- [E, R, ?GET_STACK(Stacktrace)])
- end,
- reconnect_timer(State, ClientState).
-
- reconnect_timer(#srvState{
- reconnect_state = undefined
- } = State, ClientState) ->
-
- {ok, {State#srvState{
- socket = undefined
- }, ClientState}};
- reconnect_timer(#srvState{
- reconnect_state = ReconnectState
- } = State, ClientState) ->
-
- ReconnectState2 = shackle_backoff:timeout(ReconnectState),
- #reconnect_state {current = Current} = ReconnectState2,
- TimerRef = erlang:send_after(Current, self(), ?MSG_CONNECT),
-
- {ok, {State#srvState{
- reconnect_state = ReconnectState2,
- socket = undefined,
- timer_ref = TimerRef
- }, ClientState}}.
|