erlang-cn / main

talk is cheap, issue is better than mailist and qq/wx
8 stars 3 forks source link

初学者的一个问题 #6

Open rrr523 opened 4 years ago

rrr523 commented 4 years ago

本人初学 Erlang,对 actor 模型刚刚了解,写了一段代码,做爬虫使用:

-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.

现在的问题是,我执行以后立刻就执行到 start()done 了,主进程立马结束:

image

我有两个问题:

  1. 怎么 hold 住主进程呢,有没有什么办法让主进程等待这些请求完成再结束呢?
  2. 如果 L1 有上万个 id 元素,我要 spawn 几十个 actor么?如果是,怎样决定哪个 id 发送到哪个 actor?

请指教 😢

wu4517519 commented 4 years ago

1.可设置一个请求数量Q,在mian中进行尾递归接收消息,每当一个请求完成时http_post向main发送消息告知其请求完成,Q-1,直至Q为0时main结束。 2.对于上万个id,可以通过创建一个gen_server来代理这些POST请求,所有POST请求通过gen_server来发起请求,当gen_server收到POST请求消息时,为该请求spawn一个进程,当POST请求完成时类似第1点中向main发送完成消息以更新请求进度。

以上为本人拙见

rrr523 commented 4 years ago

@wu4517519 首先谢谢您的回复

我只是想创建一个类似线程池的东西,erlang 中似乎没有这个概念。

只所以需要这个『线程池』,是因为这是个类似爬虫的程序,希望能够控制访问的量,比如我创建 100 个 actor去处理几十万个 id 这种情况。

你说的用 gen_server 来代理,似乎是直接一下子就处理几十万 id 了。