【问题标题】:Simulation of Thread Pool for a web-server implemented by Erlang doesn't workErlang 实现的 web 服务器的线程池模拟不起作用
【发布时间】:2015-12-01 17:02:08
【问题描述】:

代码如下:

-module(rudy).
-export([init/1,handler/1,request/1,reply/1, start/1, stop/0]).

start(Port) ->
  register(rudy, spawn(fun() ->
    init(Port) end)).

stop() ->
  exit(whereis(rudy), "time to die").

init(Port) ->
  Opt = [list, {active, false}, {reuseaddr, true}],
  case gen_tcp:listen(Port, Opt) of         % opens a listening socket
    {ok, Listen} ->
      spawn_many(3,Listen),
%%       handler(Listen),
      ok;
    {error, _Error} -> error
  end.

handler(Listen) ->
  case gen_tcp:accept(Listen) of            % listen to the socket
    {ok, Client} ->
      request(Client),
      gen_tcp:close(Client),
      handler(Listen);
    {error, _Error} -> error
  end.
%%   gen_tcp:close(Listen).            % close the socket

request(Client) ->
  Recv = gen_tcp:recv(Client, 0),
  case Recv of
    {ok, Str} ->
      Request = http:parse_request(Str),
      Response = reply(Request),
      gen_tcp:send(Client, Response);
    {error, Error} ->
      io:format("rudy: error: ~w~n", [Error])
  end,
  gen_tcp:close(Client).

reply({{get, URI, _}, _, _}) ->
  timer:sleep(40),
  http:ok(URI).

spawn_many(0, _Listen)-> ok;
spawn_many(N, Listen)->
  spawn(rudy,handler,[Listen]),
  spawn_many(N - 1, Listen).

我打算创建 3 个侦听套接字供客户端连接,但是在执行 rudy:start(8027). 然后从 Web 浏览器访问 http://localhost:8027/ 时,此代码不起作用。

罪魁祸首在哪里?非常感谢。

【问题讨论】:

    标签: multithreading erlang


    【解决方案1】:

    关于 Erlang 套接字需要了解的一点是,打开套接字的进程会控制它。当该进程终止时,运行时关闭套接字。

    考虑您的start/1 函数:

    start(Port) ->
      register(rudy, spawn(fun() ->
        init(Port) end)).
    

    它生成init/1 函数,为其注册一个名称,然后返回。这意味着init/1正在一个新进程中运行,那么我们来看看init/1

    init(Port) ->
      Opt = [list, {active, false}, {reuseaddr, true}],
      case gen_tcp:listen(Port, Opt) of         % opens a listening socket
        {ok, Listen} ->
          spawn_many(3,Listen),
    %%       handler(Listen),
          ok;
        {error, _Error} -> error
      end.
    

    运行init/1 的新生成进程首先调用gen_tcp:listen/2。如果成功则调用spawn_many/2设置一些acceptor;如果失败,它基本上会忽略错误。但这是你问题的关键:无论成功或失败,init/1 都会结束,因此产生它的进程也会结束,并且因为这个进程,监听套接字的控制进程,死了,监听套接字是关闭。任何尝试使用该套接字的接受器都会因此而失败,如果您要在 handler/1 函数中打印出错误条件,您会看到这一点。

    解决这个问题的方法是让init/1 进程等待,直到所有使用监听套接字的进程都关闭。

    一种方法是让 init/1 将其 pid 传递给 spawn_many(从而将其从 spawn_many/2 更改为 spawn_many/3),让 init/1 在退出之前等待 3 条消息,然后更改 @987654338 @ 到 handler/2,将 pid 作为附加参数,并在完成时在那里发送消息。让init/1 等待所有消息的最简单方法是让它调用如下递归函数:

    init(Port) ->
      Opt = [list, {active, false}, {reuseaddr, true}],
      case gen_tcp:listen(Port, Opt) of         % opens a listening socket
        {ok, Listen} ->
          Count = 3,
          spawn_many(Count,Listen,self()),
          wait_for_threads(Count);
          %%       handler(Listen),
        {error, _Error} ->
          error
      end.
    
    wait_for_threads(0) ->
      ok;
    wait_for_threads(Count) ->
      receive
        handler_done ->
          wait_for_threads(Count-1)
      end.
    

    然后将handler/1更改为handler/2并让它发送消息:

    handler(Listen, Pid) ->
      case gen_tcp:accept(Listen) of            % listen to the socket
        {ok, Client} ->
          request(Client),
          gen_tcp:close(Client),
          handler(Listen, Pid);
        {error, _Error} ->
          error
      end,
      Pid ! handler_done.
    

    不要忘记接受 spawn_many/3 的附加 pid 参数:

    spawn_many(0, _Listen, _Pid)-> ok;
    spawn_many(N, Listen, Pid)->
        spawn(rudy,handler,[Listen, Pid]),
        spawn_many(N - 1, Listen, Pid).
    

    所有这些都足以让所有派生的接受器的侦听套接字保持活动状态。

    【讨论】:

    • 再次感谢,我是 Erlang 新手,您的详细回答对您有很大帮助:]
    猜你喜欢
    • 2013-11-15
    • 2019-11-18
    • 1970-01-01
    • 2019-09-05
    • 2020-08-23
    • 2011-07-14
    • 2016-02-19
    • 2016-11-17
    • 1970-01-01
    相关资源
    最近更新 更多