一个二郎演员小演示

Posted

技术标签:

【中文标题】一个二郎演员小演示【英文标题】: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.

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

我有两个问题:

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

【问题讨论】:

【参考方案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 消息,这会导致接收停止循环,并且工作人员死亡。 ....或者,您可以使用第三方库,如poolboy、poolboy

以上是关于一个二郎演员小演示的主要内容,如果未能解决你的问题,请参考以下文章

逗号和|的区别在二郎

二郎; OTP 应用程序“app.config”

二郎 mysql 例子

Bash sha256不匹配二郎一[重复]

二郎。关于 ?SERVER 和 ? 的区别的问题模块宏

唐代韩愈的《祭十二郎文》