【问题标题】:a erlang actor little demo一个二郎演员小演示
【发布时间】:2020-04-11 09:59:51
【问题描述】:

我是Erlang的菜鸟,写蜘蛛的代码很累:

-module(http).
-compile([export_all]).

init() ->
  ssl:start(),
  inets:start(),
  register(m, spawn(fun() -> loop() end)),
  register(fetch, spawn(fun() -> x() end)),
  ok.

start() ->
  L1 = [114689,114688,114691,114690], % detail page id

  lists:map(fun(Gid) ->
    io:format("~p ~n", [Gid]),
    fetch ! {go, Gid}
  end, L1),
  m ! done,
  done.

main(_) ->
  init(),
  start().

loop() ->
  io:fwrite("this is in loop!!"),
  receive
    {no_res, Gid} ->
      io:format("~p no res ! ~n", [Gid]),
      loop();
    {have_res, Gid} ->
      io:format("~p have res ! ~n", [Gid]),
      loop();
    done ->
      io:format("wowowow", [])
  end.

x() ->
  receive
    {go, Gid} ->
      http_post(Gid);
    _ ->
      ready
  end.

http_post(Gid) ->
  URL = "https://example.com", % url demo 
  Type = "application/json",
  ReqArr = ["[{\"id\": \"", integer_to_list(Gid), "\"}]"],
  ReqBody = string:join(ReqArr, ""),

  case httpc:request(post, {URL, [], Type, ReqBody}, [], []) of
    {ok, {_, _, ResBody}} ->
      if
        length(ResBody) =:= 0 ->
          io:format("Y: ~p ~n", [Gid]);
          m ! {no_res, Gid};
        true ->
          io:format("N: ~p ~n", [Gid])
          m ! {have_res, Gid}
      end;
    {error, Reason} ->
      io:format("error cause ~p~n", [Reason]);
    _ ->
      io:format("error cause ~n", [])
  end.

现在,当我执行代码时,进程将立即终止,记录:

我有两个问题:

  1. 我该如何解决这个问题?
  2. 如果我在L1有几万个id,怎么解决?产生几十个演员?如果是,你如何决定哪个演员receive哪个id?

【问题讨论】:

    标签: erlang elixir actor erl


    【解决方案1】:

    1) 而不是在loop() 周围包装匿名函数:

    register(m, spawn(fun() -> loop() end)),
    

    您可以拨打spawn/3:

    register(m, spawn(?MODULE, loop, []) ),
    

    这里也一样:

    register(fetch, spawn(fun() -> x() end)),
    

    不。在 escript 中调用 spawn/3 不起作用 - 除非您使用以下命令预编译脚本:

    escript -c myscript.erl
    

    2) escript 创建一个执行您定义的main/1 函数的进程。您的 main/1 函数如下所示:

    main(_) ->
      init(),
      start().
    

    init() 函数不会循环,因此它会在它调用的所有函数返回后结束,即ssl:start()inets:start()register()。你的start() 函数也不会循环,所以在start() 返回之后,main() 返回并且因为执行main() 函数的进程没有什么可做的,所以它结束了。

    3)

    我如何解决这个问题?

    Http post 请求在计算机处理速度方面是永恒的,并且涉及等待,因此您可以通过同时执行多个 post 请求而不是顺序执行它们来加速您的代码。在 erlang 中,您同时执行事物的方式是产生额外的进程。在您的情况下,这意味着为每个发布请求生成一个新进程。

    您的主进程可以是一个无限循环,位于接收等待消息中,如下所示:

    main(_) ->
      init(),
      loop().
    

    init() 看起来像这样:

    init() ->
      ssl:start(),
      inets:start(),
      register(loop, self()),
      ok.
    

    然后您可以创建一个类似start() 的用户界面函数来生成发布请求:

    start() ->
      L1 = [114689,114688,114691,114690], % detail page id
    
      lists:foreach(fun(Gid) ->
        io:format("~p ~n", [Gid]),
        spawn(?MODULE, http_post, [Gid])
      end, L1).
    

    ---回复评论---

    这是一个例子:

    %First line cannot have erlang code.
    main(_) ->
        init(),
        start().
    
    init() ->
        ssl:start(),
        inets:start().
    
    start() ->
        L1 = [1, 2, 3, 4],
        Self = self(),
    
        Pids = lists:map(fun(Gid) ->
            Pid = spawn(fun() -> http_post(Gid, Self) end),
            io:format("Spawned process with Gid=~w, Pid=~w~n", [Gid, Pid]),
            Pid
        end, L1),
    
        io:format("Pids = ~w~n", [Pids]),
    
        lists:foreach(
            fun(Pid) ->
                receive
                    {no_res, {Gid, Pid} } ->
                        io:format("no response! (Gid=~w, Pid=~w)~n", [Gid, Pid]);
                    {have_res, {Gid, Pid, Reply}} ->
                        io:format("got response: ~p~n(Gid=~w, Pid=~w)~n~n", 
                                  [Reply, Gid, Pid]);
                    {Pid, Gid, Error} ->
                        io:format("Error:~n(Gid=~w, Pid=~w)~n~p~n", [Gid, Pid, Error])
                end
            end, Pids).
    
    http_post(Gid, Pid) ->
      URL = "http://localhost:8000/cgi-bin/read_json.py", % url demo 
      Type = "application/json",
      ReqArr = ["[{\"id\": \"", integer_to_list(Gid), "\"}]"],
      ReqBody = string:join(ReqArr, ""),
    
      case httpc:request(post, {URL, [], Type, ReqBody}, [], []) of
        {ok, {_, _, ResBody}} ->
          if
            length(ResBody) =:= 0 ->
              io:format("Y: ~p ~n", [Gid]),
              Reply = {no_res, {Gid, self()} },
              Pid ! Reply;
            true ->
              io:format("N: ~p ~n", [Gid]),
              Reply = {have_res, {Gid, self(), ResBody} },
              Pid ! Reply
          end;
        {error, _Reason}=Error ->
            Pid ! {Gid, self(), Error};
        Other ->
            Pid ! {Gid, self(), Other} 
      end.
    

    如果我在 L1 有数万个 id,如何解决?

    同样的方法。十万个进程在erlang中不算很多。

    【讨论】:

    • 是的,您的解决方案没问题。但是我必须打开两个erlang shell。如果我只想使用 escript 执行代码来获得像 Python 或 JavaScript 这样的脚本语言的结果?如果 escript 不能,其他 Erlang 程序如何在主要进展中获得演员结果。
    • @riskers,请参阅我添加到答案底部的完整示例。
    • 好吧,我以前在 Golang 中也做过同样的事情:创建 500 个通道来处理十万个 http 请求。现在我在 Erlang 中生成了 500 个 Actor,如何在确定性 Actor 中分​​发十万个 http 请求。
    • @riskers,我认为您不需要在 erlang 中为 http 请求创建线程池,但是 erlang 中的线程池的编程方式与任何其他语言中的线程池类似——逻辑是一样的。 erlang 工作进程将在接收中等待描述需要完成的工作的消息,例如一个 url,当没有更多工作要做时,您向工作进程发送 done 消息,这会导致接收停止循环,并且工作人员死亡。
    • ....或者,您可以使用第三方库,如poolboypoolboy
    猜你喜欢
    • 1970-01-01
    • 2016-10-20
    • 2014-09-30
    • 1970-01-01
    • 2013-02-18
    • 1970-01-01
    • 2022-11-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多