Erlang 实现的 Web 服务器的线程池模拟不起作用
Simulation of Thread Pool for a web-server implemented by Erlang doesn't work
代码如下:
-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
函数,为它注册一个名称,然后 returns。这意味着 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
,将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).
所有这些足以让所有派生的接受器的侦听套接字保持活动状态。
代码如下:
-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
函数,为它注册一个名称,然后 returns。这意味着 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
,将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).
所有这些足以让所有派生的接受器的侦听套接字保持活动状态。