我在Windows下创建了一个带有Perl的服务器(ActivePerl 5.10.1 build 1006),该服务器在连接时分叉,接受一些JSON数据,并将其写入数据库。我在64个客户端连接到服务器后遇到问题,尝试fork时错误消息“资源不可用”。
在Linux下运行此代码我发现许多已解散的子进程,这是通过在父进程上添加wait()
调用来解决的。然而,这并没有解决问题。在Linux下运行代码可以通过Windows中允许的64个调用。
我还启动了虚拟Windows服务器,以防它受到服务器的限制,但全新安装的Perl导致相同的64连接限制。
感谢任何想法。
use IO::Socket;
use Net::hostent;
use JSON;
use DBI;
use Data::Dumper;
my $port=shift || 9000;
my $clients_served = 0;
while(1){
my $server = IO::Socket::INET->new( Proto => 'tcp',
LocalPort => $port,
Listen => 1,
Reuse => 1);
die "can't setup server" unless $server;
print "[Server $0 is running]\n";
####
# wait for a client to connect
# once it has, fork to a seperate thread and
# retrieve the JSON data
####
while (my $client = $server->accept()) {
my $pid = fork();
if ($pid != 0) {
print ("Serving client " . $clients_served++ . "\n");
}else{
$client->autoflush(1);
my $JSONObject = JSON->new->ascii->pretty->allow_nonref();
my $hostinfo = gethostbyaddr($client->peeraddr);
my $client_hostname = ($hostinfo->name || $client->peerhost);
printf "connect from %s\n", $client_hostname;
print " $client_hostname connected..\n";
syswrite($client, "Reached Server\n", 2048);
if (sysread($client, my $buffer, 2048) > 0) {
foreach my $tasks($JSONObject->decode($buffer)){
foreach my $task (@$tasks){
insert_record($client_hostname, $task); #empty method, contents does not affect result
}
}
}
print " $client_hostname disconnected..\n";
close $client;
exit 0;
}
}
$server->close();
}
exit 0;
答案 0 :(得分:2)
尝试从已完成的交易中获取僵尸进程。我可以获取您的示例代码 如果我包含更多行,请继续运行:
use POSIX ':sys_wait_h';
if ($pid != 0) {
print ("Serving client " . $clients_served++ . "\n");
1 while waitpid -1, WNOHANG > 0;
如果您可能有64个同时连接,则可能需要考虑其他事项 - 在Windows上安装SIGCHLD
处理程序并不合适。
答案 1 :(得分:1)
这是一个躲闪,因为它没有直接回答你的问题,但有时删除错误的最好方法是编写更少的代码 - 为什么不让别人为你做进程管理和套接字处理 - 即{ {3}}? Net::Server个性提供与您现在写的相同的行为,但我个人会考虑Net::Server::Fork。
使用Net :: Server,您的应用程序将如下所示:
use strict;
use warnings;
use base 'Net::Server::Fork';
sub process_request {
my $self = shift;
# Do JSON stuff here.
# The client is attached to *STDIN and *STDOUT.
}
# You might omit this and the arguments to run() because
# Net::Server has command-line and config-file handling too.
my $port = shift || 90000;
__PACKAGE__->run(
proto => "tcp",
port => $port,
);
我不得不说,这真的很整洁。
答案 2 :(得分:1)
我在Windows上挖掘了perl的fork问题(ActiveState Perl 5.10)。问题是,在子进程退出后,作为线程模拟的分叉“进程”的HANDLE未关闭。并且似乎有64个线程句柄的限制。由于我没有找到如何关闭这些线程句柄,我找到了另一个解决方案,发现使用真实线程超过64限制(这来自http://www.perlmonks.org/?node_id=722374):
#! perl -slw
use strict;
use IO::Socket;
use threads;
our $ADDR ||= 'localhost:35007';
my $listener = IO::Socket::INET->new(
LocalAddr => $ADDR,
Listen => 5,
Reuse => 1,
) or die $^E;
while( my $client = $listener->accept ) {
async {
my($port, $iaddr) = sockaddr_in( getpeername( $client ) );
printf "From %s:%d: %s", join('.',unpack 'C4', $iaddr ), $port, $_
while <$client>;
close $client;
}->detach;
}
这确实有效,至少在Windows XP上是这样。任何警告,任何人?
答案 3 :(得分:1)
我花了很长时间来解决这个问题。
每个Windows perl程序都有64个线程限制,不支持POSIX。
在分叉子进程后,父进程需要调用waitpid($ childPID,0)来释放线程,但是这会导致父进程被阻塞,从而导致问题:如果父进程必须分叉,那么分析的重点是什么?等孩子完成?
什么不清除是父母可以在以后的任何时间发出waitpid($ childPID,0)!
当我的子线程完成最后一件事时,它创建一个文件......
open OUT, ">$$.pid";
print OUT $$;
close OUT;
exit(0);
每次父母要分叉时,它会检查.pid文件,并为每个文件发出waitpid,然后删除.pid文件。
这将不允许同时超过64个连接,但它将允许父级在子线程完成工作时继续,然后在子级完成时释放线程。
另一个值得一提的是,孩子应该做的第一件事就是关闭监听套接字(问题中的$ server),这样只有父母才能回答新客户端。