关于用Erlang编写的推送器客户端

时间:2015-06-01 10:25:27

标签: erlang otp

我想编写一个模块,需要能够在Erlang中将消息推送到pusher。我发现这个仓库https://github.com/bradfordw/pusherl已经不再维护了,但我认为我可以适应它。我跟着README,我成功运行了所有的Rebar命令。然后我通过命令

打开钢筋控制台
./rel/pusherl/bin/pusherl console

所以服务器应该启动。但是当我试着打电话时

gen_server:call(pusherl_server, {push, {"ChannelName", "EventName", "Payload"}}).

然后它抛出错误:

exception exit: {noproc,{gen_server,call,
                                   [pusherl_server,
                                    {push, 

    {"ChannelName","EventName","Payload"}}]}}
     in function  gen_server:call/2 (gen_server.erl, line 182)

我对Erlang和OTP很新,我花了一半的时间让它工作但没有成功。请帮我解决这个问题。我真的很感激。

顺便说一句,如果您认识任何其他推送客户,请建议我。非常感谢。

以下是gen_server回调的代码:

-module(pusherl_server).
-behaviour(gen_server).
-define(SERVER, ?MODULE).
-define(JP, fun(K,V) -> string:join([K,V],"=") end).

-record(state,{app_id, key, secret}).

-export([start_link/0]).

-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

start_link() ->
  gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

init(_) ->
  {ok, PusherAppId} = application:get_env(pusher_app_id),
  {ok, PusherKey} = application:get_env(pusher_key),
  {ok, PusherSecret} = application:get_env(pusher_secret),
  {ok, #state{app_id=PusherAppId, key=PusherKey, secret=PusherSecret}}.

handle_call({push, {ChannelName, EventName, Payload}}, _From, State) ->
  case http_request(ChannelName, EventName, Payload, State) of
    {ok, _} -> {reply, ok, State};
    {error, _} -> {reply, error, State}
  end;
handle_call(_Request, _From, State) ->
  {noreply, ok, State}.

handle_cast({push, {ChannelName, EventName, Payload}}, State) ->
  case http_request(ChannelName, EventName, Payload, State) of
    {ok, _} -> {noreply, ok, State};
    {error, _} -> {noreply, error, State}
  end;
handle_cast(_Msg, State) ->
  {noreply, State}.

handle_info(_Info, State) ->
  {noreply, State}.

terminate(_Reason, _State) ->
  ok.

code_change(_OldVsn, State, _Extra) ->
  {ok, State}.

http_request(ChannelName, EventName, Payload, Config) when is_list(ChannelName), is_record(Config, state) ->
  {ok, ReqProps} = http_request_props(Payload, EventName, ChannelName, Config),
    httpc:request(post, ReqProps, [], []).

http_request_props(Payload, EventName, ChannelName, #state{app_id=AppId, key=AppKey, secret=AppSecret}) ->
    Md5String = lists:flatten([io_lib:format("~2.16.0b",[N]) || <<N>> <= crypto:md5(Payload)]),
  ToSign = ["POST",
                lists:flatten(["/apps/", AppId, "/channels/", ChannelName, "/events"]),
                string:join([?JP("auth_key", AppKey),
        ?JP("auth_timestamp", get_time_as_string()),
        ?JP("auth_version", "1.0"),
        ?JP("body_md5", Md5String),
                ?JP("name", EventName)
                ],"&")
  ],
    AuthSignature = signed_params(ToSign, AppSecret),
    QueryParams = [
        ?JP("auth_key", AppKey),
      ?JP("auth_timestamp", get_time_as_string()),
      ?JP("auth_version","1.0"),
      ?JP("body_md5", Md5String),
        ?JP("auth_signature", AuthSignature),
        ?JP("name", EventName)
    ],
  Url = http_api_url(AppId, ChannelName, QueryParams),
  {ok, {Url, [], "application/x-www-form-urlencoded", Payload}}.

http_api_url(AppId, ChannelName, QueryParams) ->
  QueryString = string:join(QueryParams,"&"),
  lists:flatten(["http://api.pusherapp.com/apps/",AppId,"/channels/",ChannelName,"/events?", QueryString]).

get_time_as_string() ->
  {M, S, _} = now(),
  integer_to_list(((M * 1000000) + S)).

signed_params(Params, Secret) ->
    lists:flatten([io_lib:format("~2.16.0b",[N]) || <<N:8>> <= sha2:hmac_sha256(Secret, string:join(Params,"\n"))]).

1 个答案:

答案 0 :(得分:0)

noproc表示您尝试调用的进程未运行。你需要用:

开始
pusherl_server:start_link().

请注意,这会将pusherl_server进程链接到调用进程。如果您在shell中运行它,然后执行导致错误的操作,则错误将通过链接传播到pusherl_server进程并将其终止,因此您必须再次启动它。

为避免这种情况,您可以在启动后取消链接:

{ok, Pid} = pusherl_server:start_link().
unlink(Pid).

或向模块添加start功能,与start_link的功能相同,只是它调用gen_server:start而不是gen_server:start_link