由Erlang实现的Web服务器的线程池的模拟不起作用 [英] Simulation of Thread Pool for a web-server implemented by Erlang doesn't work

查看:156
本文介绍了由Erlang实现的Web服务器的线程池的模拟不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

代码如下:

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

I intend to create 3 listening socket for client to connect to, but this code doesn't work when executing rudy:start(8027). and then accessing http://localhost:8027/ from web browser.

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

Where's the culprit? Many thanks.

推荐答案

有关Erlang套接字的一件事是打开一个进程控制它;

One thing to know about Erlang sockets is that a process that opens one controls it; when that process dies, the runtime closes the socket.

考虑你的 start / 1 function:

Consider your start/1 function:

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

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

It spawns the init/1 function, registers a name for it, and then returns. This means init/1 is running in a new process, so let's look at 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 函数中打印出错误条件,那么您将看到。

The newly-spawned process running init/1 first calls gen_tcp:listen/2. If it succeeds, it calls spawn_many/2 to set up some acceptors; if it fails, it essentially ignores the error. But here's the key to your problem: regardless of success or failure, init/1 ends, and therefore so does the process in which it was spawned, and because this process, the controlling process of the listen socket, dies, the listen socket is closed. Any acceptors trying to use that socket fail because of that, which you would see if you were to print out the error condition in the handler/1 function.

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

The way to fix this is to make the init/1 process wait until all processes using the listen socket die off.

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

One way to do this is to make init/1 pass its pid to spawn_many (thus changing it from spawn_many/2 to spawn_many/3), have init/1 wait for 3 messages before exiting, and change handler/1 to handler/2, taking the pid as an additional argument and sending a message there when it's finished. Th easiest way to have init/1 wait for all the messages is to have it call a recursive function like the one below:

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 并发送消息:

Then change handler/1 to handler/2 and have it send the message:

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.

不要忘记接受额外的pid参数到 spawn_many / 3

Don't forget to accept the additional pid argument to spawn_many/3:

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

所有这一切都足以保持所有产生的接受者的监听套接字。

All this is enough to keep the listen socket alive for all the spawned acceptors.

这篇关于由Erlang实现的Web服务器的线程池的模拟不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆