我正在一个学校项目中工作,我必须编写一个多线程服务器,现在我通过对它进行一些测试来将它与apache进行比较。我正在使用Autobench来解决此问题,但是在运行了几次测试之后,或者如果我给它很高的速率(大约600+)以建立连接,我会收到“打开的文件太多”的错误消息。
处理close()
完请求后,我总是在套接字上做一个。我也尝试过使用该shutdown()
函数,但似乎无济于事。可以解决吗?
Answers:
我有类似的问题。快速解决方案是:
ulimit -n 4096
解释如下-每个服务器连接都是一个文件描述符。在CentOS,Redhat和Fedora(可能还有其他)中,文件用户限制为1024-不知道为什么。当您键入以下内容时很容易看到:ulimit -n
请注意,这与系统最大文件(/ proc / sys / fs / file-max)没有太大关系。
就我而言,Redis存在问题,所以我这样做了:
ulimit -n 4096
redis-server -c xxxx
在这种情况下,您需要启动服务器而不是Redis。
select()
。
TCP具有称为“ TIME_WAIT”的功能,可确保干净地关闭连接。关闭插槽后,它需要连接的一端保持侦听一段时间。
在高性能服务器中,重要的是进入TIME_WAIT的是客户端,而不是服务器。客户端可以负担得起开放端口的费用,而繁忙的服务器可能会迅速用尽端口或开放的FD过多。
为此,服务器永远不要先关闭连接-它应始终等待客户端关闭连接。
这意味着同时打开文件的最大数量。
解决了:
在文件末尾,/etc/security/limits.conf
您需要添加以下行:
* soft nofile 16384
* hard nofile 16384
在当前控制台中从根目录(sudo不起作用)执行以下操作:
ulimit -n 16384
尽管这是可选的,但可以重新启动服务器。
在/etc/nginx/nginx.conf
文件中注册worker_connections
等于值的新16384
值worker_processes
。
如果没有执行此操作ulimit -n 16384
,则需要重新启动,然后问题将消失。
PS:
如果修复后在日志中可见error accept() failed (24: Too many open files)
:
在nginx配置中,propevia(例如):
worker_processes 2;
worker_rlimit_nofile 16384;
events {
worker_connections 8192;
}
我也有这个问题。您有文件句柄泄漏。您可以通过打印出所有打开的文件句柄的列表(在POSIX系统上)来进行调试:
void showFDInfo()
{
s32 numHandles = getdtablesize();
for ( s32 i = 0; i < numHandles; i++ )
{
s32 fd_flags = fcntl( i, F_GETFD );
if ( fd_flags == -1 ) continue;
showFDInfo( i );
}
}
void showFDInfo( s32 fd )
{
char buf[256];
s32 fd_flags = fcntl( fd, F_GETFD );
if ( fd_flags == -1 ) return;
s32 fl_flags = fcntl( fd, F_GETFL );
if ( fl_flags == -1 ) return;
char path[256];
sprintf( path, "/proc/self/fd/%d", fd );
memset( &buf[0], 0, 256 );
ssize_t s = readlink( path, &buf[0], 256 );
if ( s == -1 )
{
cerr << " (" << path << "): " << "not available";
return;
}
cerr << fd << " (" << buf << "): ";
if ( fd_flags & FD_CLOEXEC ) cerr << "cloexec ";
// file status
if ( fl_flags & O_APPEND ) cerr << "append ";
if ( fl_flags & O_NONBLOCK ) cerr << "nonblock ";
// acc mode
if ( fl_flags & O_RDONLY ) cerr << "read-only ";
if ( fl_flags & O_RDWR ) cerr << "read-write ";
if ( fl_flags & O_WRONLY ) cerr << "write-only ";
if ( fl_flags & O_DSYNC ) cerr << "dsync ";
if ( fl_flags & O_RSYNC ) cerr << "rsync ";
if ( fl_flags & O_SYNC ) cerr << "sync ";
struct flock fl;
fl.l_type = F_WRLCK;
fl.l_whence = 0;
fl.l_start = 0;
fl.l_len = 0;
fcntl( fd, F_GETLK, &fl );
if ( fl.l_type != F_UNLCK )
{
if ( fl.l_type == F_WRLCK )
cerr << "write-locked";
else
cerr << "read-locked";
cerr << "(pid:" << fl.l_pid << ") ";
}
}
通过转储所有打开的文件,您将快速找出文件句柄泄漏的位置。
如果服务器产生子进程。例如,如果这是一个“ fork”风格的服务器,或者您正在生成其他进程(例如,通过cgi),则必须确保使用“ cloexec”创建文件句柄-既用于真实文件,也用于套接字。
如果没有cloexec,则每次您分叉或生成时,所有打开的文件句柄都会在子进程中克隆。
无法关闭网络插座也很容易-例如,当远程方断开连接时,仅将其丢弃。这会像疯了一样泄漏手柄。
我遇到了同样的问题,我没有费心检查close()调用的返回值。当我开始检查返回值时,问题神秘地消失了。
我只能假设编译器有一个优化故障(在我的情况下为gcc),假设close()调用没有副作用,如果不使用返回值,则可以忽略。
valgrind
或其他此类工具进行跟踪。优化close
调用的编译器将是灾难性的。
EGAIN
很多情况,如果您忽略这种情况,那么所有赌注都将失效。
为了将来参考,我遇到了类似的问题。我通过创建太多文件和套接字来创建太多文件描述符(FD)(在Unix OS上,所有内容都是FD)。我的解决方案是在运行时使用来增加FD setrlimit()
。
首先,我获得了FD限制,并带有以下代码:
// This goes somewhere in your code
struct rlimit rlim;
if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
std::cout << "Soft limit: " << rlim.rlim_cur << std::endl;
std::cout << "Hard limit: " << rlim.rlim_max << std::endl;
} else {
std::cout << "Unable to get file descriptor limits" << std::endl;
}
运行后getrlimit()
,我可以确认系统上的软限制为256 FD,硬限制为无限FD(这取决于发行版和规格)。由于我在文件和套接字之间创建了300多个FD,因此我的代码崩溃了。
就我而言,我无法减少FD的数量,因此我决定使用以下代码来提高FD软限制:
// This goes somewhere in your code
struct rlimit rlim;
rlim.rlim_cur = NEW_SOFT_LIMIT;
rlim.rlim_max = NEW_HARD_LIMIT;
if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
std::cout << "Unable to set file descriptor limits" << std::endl;
}
请注意,通过此代码,您还可以获取正在使用的FD的数量以及这些FD的来源。
在vSphere上的Ubuntu 18上存在类似问题。原因-配置文件nginx.conf包含过多的日志文件和套接字。套接字在Linux中被视为文件。当重新加载nginx -s或sudo服务nginx启动/重新启动时,error.log中出现“打开文件过多”错误。
NGINX工作进程由NGINX用户启动。Nginx用户的Ulimit(软和硬)为65536。ulimit和设置limits.conf无效。
nginx.conf中的rlimit设置也无济于事:worker_rlimit_nofile 65536;
有效的解决方案是:
$ mkdir -p /etc/systemd/system/nginx.service.d
$ nano /etc/systemd/system/nginx.service.d/nginx.conf
[Service]
LimitNOFILE=30000
$ systemctl daemon-reload
$ systemctl restart nginx.service