multithreading - Erlang 实现的 Web 服务器的线程池模拟不起作用

标签 multithreading erlang

代码如下:

-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).然后访问http://localhost:8027/时,此代码不起作用。 来自网络浏览器。

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

最佳答案

关于 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来设置一些接受器;如果失败,它基本上会忽略该错误。但问题的关键是:无论成功还是失败,init/1都会结束,因此它产生的进程也会结束,因为这个进程,即监听套接字的控制进程,死亡,监听套接字关闭。任何尝试使用该套接字的接受器都会因此失败,如果您要在 handler/1 函数中打印出错误条件,您会看到这一点。

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

实现此目的的一种方法是让 init/1 将其 pid 传递给 spawn_many(从而将其从 spawn_many/2 更改为 spawn_many/3),让 init/1 在退出前等待 3 条消息,并将 handler/1 更改为 handler/2 code>,将 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).

所有这些都足以使所有生成的接受器的监听套接字保持事件状态。

关于multithreading - Erlang 实现的 Web 服务器的线程池模拟不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32416651/

相关文章:

java - 为什么Java不释放内存

python - Gunicorn:多个后台工作线程

c# - thread.join是否同时启动所有线程

server - RabbitMQ启动失败,TCP连接成功但Erlang分发失败

python - Pygame 多线程

使用 ReentrantLock 时出现 java.lang.IllegalMonitorStateException 错误?

json - 什么是Erlang最成熟的JSON库?

Erlang工作进程

ssl - Erlang ssl 请求的负载均衡

erlang - 在 Elixir 中解析、更改和写回 HTML